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