1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2019, Joyent, Inc. 25 */ 26 27 /* 28 * Create bay topology node from SMBIOS Type 136 structure, call the disk 29 * enumerator to enumerate a SATA direct attached disk. 30 */ 31 32 #include <sys/types.h> 33 #include <strings.h> 34 #include <fm/topo_mod.h> 35 #include <fm/topo_method.h> 36 #include <fm/topo_hc.h> 37 #include <sys/systeminfo.h> 38 #include <sys/smbios_impl.h> 39 #include <x86pi_impl.h> 40 41 #define DEVICES "/devices" 42 #define HBA_DRV_NAME "ahci" 43 44 #define BDF(b, df) ((uint16_t)((((uint16_t)(b) << 8) & 0xFF00) | \ 45 ((uint16_t)(df) & 0x00FF))); 46 47 static const topo_pgroup_info_t io_pgroup = { 48 TOPO_PGROUP_IO, 49 TOPO_STABILITY_PRIVATE, 50 TOPO_STABILITY_PRIVATE, 51 1 52 }; 53 54 static const topo_pgroup_info_t binding_pgroup = { 55 TOPO_PGROUP_BINDING, 56 TOPO_STABILITY_PRIVATE, 57 TOPO_STABILITY_PRIVATE, 58 1 59 }; 60 61 static const topo_method_t bay_methods[] = { 62 { TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC, 63 TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL, 64 topo_mod_hc_occupied }, 65 { NULL } 66 }; 67 68 /* 69 * Return PCI Bus/Dev/Func 70 */ 71 int 72 bay_bdf(topo_mod_t *mod, smbios_port_ext_t *epp, uint16_t *bdf) 73 { 74 int devt; 75 id_t dev_id; 76 uint8_t bus, dev_funct; 77 78 char *f = "bay_bdf"; 79 smbios_hdl_t *shp; 80 81 shp = topo_mod_smbios(mod); 82 if (shp == NULL) { 83 topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f); 84 return (-1); 85 } 86 /* 87 * Depending on device type, BDF comes from either slot (type-9) or 88 * on-board (type-41) SMBIOS structure. 89 */ 90 devt = epp->smbporte_dtype; 91 dev_id = epp->smbporte_devhdl; 92 93 if (devt == SMB_TYPE_SLOT) { 94 smbios_slot_t slot; 95 (void) smbios_info_slot(shp, dev_id, &slot); 96 bus = slot.smbl_bus; 97 dev_funct = slot.smbl_df; 98 } else if (devt == SMB_TYPE_OBDEVEXT) { 99 smbios_obdev_ext_t ob; 100 (void) smbios_info_obdevs_ext(shp, dev_id, &ob); 101 bus = ob.smboe_bus; 102 dev_funct = ob.smboe_df; 103 } else { 104 topo_mod_dprintf(mod, "%s: unknown device type: %d\n", 105 f, devt); 106 return (-1); 107 } 108 topo_mod_dprintf(mod, "%s: %s: bus(0x%02x) dev/func(0x%02x)\n", f, 109 devt == SMB_TYPE_SLOT ? "slot" : "ob dev", bus, dev_funct); 110 111 *bdf = BDF(bus, dev_funct); 112 113 return (0); 114 } 115 116 /* 117 * Decorate topo node with pgroups. 118 */ 119 int 120 bay_pgroups(topo_mod_t *mod, tnode_t *tnp, di_node_t *dnp, di_node_t *sibp, 121 char *minor_name) 122 { 123 int rv, err; 124 char *ap_path, *oc_path; 125 126 char *f = "bay_pgoups"; 127 128 /* 129 * Create "io" pgroup and attachment point path. 130 */ 131 rv = topo_pgroup_create(tnp, &io_pgroup, &err); 132 if (rv != 0) { 133 topo_mod_dprintf(mod, 134 "%s: failed to create \"io\" pgroup: %s\n", 135 f, topo_strerror(err)); 136 (void) topo_mod_seterrno(mod, err); 137 return (err); 138 } 139 140 ap_path = topo_mod_alloc(mod, MAXPATHLEN); 141 if (ap_path == NULL) { 142 topo_mod_dprintf(mod, "%s: ap_path alloc failed\n"); 143 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 144 } 145 (void) snprintf(ap_path, MAXPATHLEN, "%s%s:%s", DEVICES, 146 di_devfs_path(*dnp), minor_name); 147 topo_mod_dprintf(mod, "%s: ap_path(%s)\n", f, ap_path); 148 149 /* add ap-path */ 150 rv = topo_prop_set_string(tnp, TOPO_PGROUP_IO, TOPO_IO_AP_PATH, 151 TOPO_PROP_IMMUTABLE, ap_path, &err); 152 if (rv != 0) { 153 topo_mod_dprintf(mod, "%s: failed to set ap-path: %s\n", 154 f, topo_strerror(err)); 155 topo_mod_free(mod, ap_path, MAXPATHLEN); 156 (void) topo_mod_seterrno(mod, err); 157 return (err); 158 } 159 topo_mod_free(mod, ap_path, MAXPATHLEN); 160 161 /* 162 * Create "binding" pgroup and occupant path. 163 */ 164 rv = topo_pgroup_create(tnp, &binding_pgroup, &err); 165 if (rv != 0) { 166 topo_mod_dprintf(mod, 167 "%s: failed to create \"io\" pgroup: %s\n", 168 f, topo_strerror(err)); 169 (void) topo_mod_seterrno(mod, err); 170 return (err); 171 } 172 173 oc_path = di_devfs_path(*sibp); 174 if (oc_path == NULL) { 175 topo_mod_dprintf(mod, "%s: no occupant path\n", f); 176 return (-1); 177 } 178 topo_mod_dprintf(mod, "%s: oc_path(%s)\n", f, oc_path); 179 180 /* add ocupant-path */ 181 rv = topo_prop_set_string(tnp, TOPO_PGROUP_BINDING, 182 TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, oc_path, 183 &err); 184 if (rv != 0) { 185 topo_mod_dprintf(mod, "%s: failed to set ap-path: %s\n", 186 f, topo_strerror(err)); 187 di_devfs_path_free(oc_path); 188 (void) topo_mod_seterrno(mod, err); 189 return (err); 190 } 191 di_devfs_path_free(oc_path); 192 193 return (0); 194 } 195 196 int 197 bay_update_tnode(topo_mod_t *mod, tnode_t *tnodep, uint16_t bdf, int phy) 198 { 199 int rv; 200 int minor_cnt = 0; 201 char *minor_name = NULL; 202 di_node_t devtree, dnode, sib; 203 di_minor_t minor = DI_MINOR_NIL; 204 205 char *f = "bay_update_tnode"; 206 207 /* 208 * Find HBA device node from BDF. 209 */ 210 devtree = topo_mod_devinfo(mod); 211 if (devtree == DI_NODE_NIL) { 212 topo_mod_dprintf(mod, "%s: failed to get dev tree\n", f); 213 return (-1); 214 } 215 for (dnode = di_drv_first_node(HBA_DRV_NAME, devtree); 216 dnode != DI_NODE_NIL; 217 dnode = di_drv_next_node(dnode)) { 218 if (bdf == x86pi_bdf(mod, dnode)) { 219 /* 220 * Match child node from PHY. 221 */ 222 sib = di_child_node(dnode); 223 while (sib != DI_NODE_NIL) { 224 if (phy == x86pi_phy(mod, sib)) 225 break; 226 sib = di_sibling_node(sib); 227 } 228 break; 229 } 230 } 231 if (dnode == DI_NODE_NIL) { 232 topo_mod_dprintf(mod, "%s: no HBA di_node\n", f); 233 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 234 } 235 236 /* 237 * HBA attachment point minor node name. 238 */ 239 while ((minor = di_minor_next(dnode, minor)) != DI_MINOR_NIL) { 240 if (strncmp(DDI_NT_SATA_ATTACHMENT_POINT, 241 di_minor_nodetype(minor), 242 strlen(DDI_NT_SATA_ATTACHMENT_POINT)) == 0) { 243 if (phy == minor_cnt++) { 244 minor_name = di_minor_name(minor); 245 topo_mod_dprintf(mod, 246 "%s: phy(%d) minor name(%s)\n", 247 f, phy, minor_name); 248 break; 249 } 250 } 251 } 252 253 rv = bay_pgroups(mod, tnodep, &dnode, &sib, minor_name); 254 if (rv != 0) { 255 topo_mod_dprintf(mod, "%s: failed to add pgroups\n", f); 256 return (-1); 257 } 258 259 260 return (0); 261 } 262 263 /* 264 * x86pi_gen_bay: 265 * create "bay" node 266 * call "disk" enum passing in "bay" node 267 */ 268 int 269 x86pi_gen_bay(topo_mod_t *mod, tnode_t *t_parent, smbios_port_ext_t *eport, 270 int instance) 271 { 272 int rv; 273 int min = 0, max = 0; 274 id_t port_id; 275 uint16_t bdf; 276 smbios_port_t smb_port; 277 x86pi_hcfmri_t hcfmri = {0}; 278 tnode_t *tn_bay; 279 280 char *f = "x86pi_gen_disk"; 281 smbios_hdl_t *shp; 282 283 shp = topo_mod_smbios(mod); 284 if (shp == NULL) { 285 topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f); 286 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 287 } 288 289 /* 290 * Label comes from the port (type-8) SMBIOS structure. 291 */ 292 port_id = eport->smbporte_port; 293 294 rv = smbios_info_port(shp, port_id, &smb_port); 295 if (rv != 0) { 296 topo_mod_dprintf(mod, 297 "%s: failed to get port %d SMBIOS struct\n", 298 f, port_id); 299 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 300 } 301 302 /* 303 * Fill in hcfmri info. 304 */ 305 hcfmri.hc_name = BAY; 306 hcfmri.instance = instance; 307 hcfmri.location = x86pi_cleanup_smbios_str(mod, smb_port.smbo_eref, 0); 308 309 /* 310 * Create "bay" node. 311 */ 312 rv = x86pi_enum_generic(mod, &hcfmri, t_parent, t_parent, &tn_bay, 0); 313 if (rv != 0) { 314 topo_mod_dprintf(mod, 315 "%s: failed to create %s topo node: %d\n", 316 f, BAY, instance); 317 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 318 } 319 320 /* free up location string */ 321 if (hcfmri.location != NULL) { 322 topo_mod_strfree(mod, (char *)hcfmri.location); 323 } 324 325 /* 326 * Determine the bay BDF. 327 */ 328 rv = bay_bdf(mod, eport, &bdf); 329 if (rv != 0) { 330 topo_mod_dprintf(mod, "%s: failed to get BDF\n", f); 331 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 332 } 333 topo_mod_dprintf(mod, "%s: BDF(0x%04x)\n", f, bdf); 334 335 /* 336 * Decorate bay topo node. 337 */ 338 rv = bay_update_tnode(mod, tn_bay, bdf, eport->smbporte_phy); 339 if (rv != 0) { 340 topo_mod_dprintf(mod, "%s: failed to decorate bay node\n", f); 341 return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM)); 342 } 343 344 if (topo_method_register(mod, tn_bay, bay_methods) != 0) { 345 topo_mod_dprintf(mod, "topo_method_register() failed on " 346 "%s=%d: %s", BAY, instance, 347 topo_mod_errmsg(mod)); 348 /* errno set */ 349 return (-1); 350 } 351 352 /* 353 * Call disk enum passing in decorated bay topo node. 354 */ 355 if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) { 356 topo_mod_dprintf(mod, "%s: Failed to load %s module: %s\n", 357 f, DISK, topo_strerror(topo_mod_errno(mod))); 358 return (topo_mod_errno(mod)); 359 } 360 361 rv = topo_node_range_create(mod, tn_bay, DISK, min, max); 362 if (rv != 0) { 363 topo_mod_dprintf(mod, "%s: failed to create range: %s\n", f, 364 topo_strerror(topo_mod_errno(mod))); 365 return (topo_mod_errno(mod)); 366 } 367 368 rv = topo_mod_enumerate(mod, tn_bay, DISK, DISK, min, max, NULL); 369 if (rv != 0) { 370 topo_mod_dprintf(mod, "%s: %s enumeration failed: %s\n", f, 371 DISK, topo_strerror(topo_mod_errno(mod))); 372 return (topo_mod_errno(mod)); 373 } 374 375 topo_mod_dprintf(mod, "%s: done.\n", f); 376 377 return (0); 378 } 379