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.
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.
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]
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
28 * The acronym did means "Dev-Info-Data". Many properties and
29 * characteristics of topology nodes are, with a bit of coaxing
30 * derived from devinfo nodes. These routines do some of the
31 * derivation and also encapsulate the discoveries in did_t
32 * structures that get associated with topology nodes as their
39 #include <sys/types.h>
40 #include <fm/topo_mod.h>
41 #include <libnvpair.h>
42 #include <libdevinfo.h>
45 #include <hostbridge.h>
47 #include <did_props.h>
51 static void slotnm_destroy(slotnm_t
*);
54 slotnm_create(topo_mod_t
*mp
, int dev
, char *str
)
58 if ((p
= topo_mod_alloc(mp
, sizeof (slotnm_t
))) == NULL
)
63 p
->snm_name
= topo_mod_strdup(mp
, str
);
64 if (p
->snm_name
== NULL
) {
72 slotnm_destroy(slotnm_t
*p
)
76 slotnm_destroy(p
->snm_next
);
77 if (p
->snm_name
!= NULL
)
78 topo_mod_strfree(p
->snm_mod
, p
->snm_name
);
79 topo_mod_free(p
->snm_mod
, p
, sizeof (slotnm_t
));
83 di_devtype_get(topo_mod_t
*mp
, di_node_t src
, char **devtype
)
89 * For PCI the device type defined the type of device directly below.
90 * For PCIe RP and Switches, the device-type should be "pciex". For
91 * PCIe-PCI and PCI-PCI bridges it should be "pci". NICs = "network",
92 * Graphics = "display", etc..
94 if (di_bytes_get(mp
, src
, DI_DEVTYPPROP
, &sz
, &buf
) == 0) {
95 *devtype
= topo_mod_strdup(mp
, (char *)buf
);
100 if (*devtype
!= NULL
)
105 typedef struct smbios_slot_cb
{
107 const char *cb_label
;
111 di_smbios_find_slot(smbios_hdl_t
*shp
, const smbios_struct_t
*strp
, void *data
)
113 smbios_slot_cb_t
*cbp
= data
;
116 if (strp
->smbstr_type
!= SMB_TYPE_SLOT
||
117 smbios_info_slot(shp
, strp
->smbstr_id
, &slot
) != 0)
120 if (slot
.smbl_id
== cbp
->cb_slotnum
) {
121 cbp
->cb_label
= slot
.smbl_name
;
129 di_physlotinfo_get(topo_mod_t
*mp
, di_node_t src
, int *slotnum
, char **slotname
)
135 boolean_t got_slotprop
= B_FALSE
;
139 (void) di_uintprop_get(mp
, src
, DI_PHYSPROP
, (uint_t
*)slotnum
);
142 * For PCI-Express, there is only one downstream device, so check for
143 * a slot-names property, and if it exists, ignore the slotmask value
144 * and use the string as the label.
146 if (di_bytes_get(mp
, src
, DI_SLOTPROP
, &sz
, &buf
) == 0 &&
149 * If there is a DI_SLOTPROP of the form SlotX (ie set up from
150 * the IRQ routing table) then trust that in preference to
151 * DI_PHYSPROP (which is set up from the PCIe slotcap reg).
153 got_slotprop
= B_TRUE
;
154 (void) sscanf((char *)&buf
[4], "Slot%d", slotnum
);
161 * Order of preference
162 * 1) take slotnum and look up in SMBIOS table
164 * 3) fabricate name based on slotnum
166 if ((shp
= topo_mod_smbios(mp
)) != NULL
) {
168 * The PCI spec describes slot number 0 as reserved for
169 * internal PCI devices. Not all platforms respect
170 * this, so we have to treat slot 0 as a valid device.
171 * But other platforms use 0 to identify an internal
172 * device. We deal with this by letting SMBIOS be the
173 * final decision maker. If SMBIOS is supported, but
174 * the given slot number is not represented in the
175 * SMBIOS tables, then ignore the slot entirely.
177 smbios_slot_cb_t cbdata
;
179 cbdata
.cb_slotnum
= *slotnum
;
180 cbdata
.cb_label
= NULL
;
181 if (smbios_iter(shp
, di_smbios_find_slot
, &cbdata
) <= 0)
183 slotbuf
= (char *)cbdata
.cb_label
;
184 topo_mod_dprintf(mp
, "%s: node=%p: using smbios name\n",
186 } else if (got_slotprop
== B_TRUE
) {
187 slotbuf
= (char *)&buf
[4];
188 topo_mod_dprintf(mp
, "%s: node=%p: found %s property\n",
189 __func__
, src
, DI_SLOTPROP
);
192 * Make generic description string "SLOT <num>", allow up to
193 * 10 digits for number
195 slotbuf
= alloca(16);
196 (void) snprintf(slotbuf
, 16, "SLOT %d", *slotnum
);
197 topo_mod_dprintf(mp
, "%s: node=%p: using generic slot name\n",
200 if ((*slotname
= topo_mod_strdup(mp
, slotbuf
)) == NULL
)
203 topo_mod_dprintf(mp
, "%s: node=%p: slotname=%s\n",
204 __func__
, src
, *slotname
);
210 di_slotinfo_get(topo_mod_t
*mp
, di_node_t src
, int *nslots
,
211 slotnm_t
**slotnames
)
213 slotnm_t
*lastslot
= NULL
;
223 if (di_bytes_get(mp
, src
, DI_SLOTPROP
, &sz
, &slotbuf
) < 0)
225 if (sz
< sizeof (uint_t
))
227 bcopy(slotbuf
, &slotmap
, sizeof (uint_t
));
231 slotname
= (char *)&slotbuf
[4];
232 for (andbit
= 0; andbit
< 32; andbit
++) {
233 if (slotmap
& (1 << andbit
)) {
235 slotname
+= strlen(s
) + 1;
236 if ((newslot
= slotnm_create(mp
, andbit
, s
)) == NULL
) {
237 slotnm_destroy(*slotnames
);
242 if (lastslot
== NULL
)
243 *slotnames
= lastslot
= newslot
;
245 lastslot
->snm_next
= newslot
;
255 did_physlot(did_t
*did
)
258 return (did
->dp_physlot
);
262 did_physlot_exists(did_t
*did
)
265 return ((did
->dp_physlot
>= 0) || (did
->dp_nslots
> 0));
269 did_create(topo_mod_t
*mp
, di_node_t src
,
270 int ibrd
, int ibrdge
, int irc
, int ibus
)
277 if ((pd
= did_hash_lookup(mp
, src
)) != NULL
) {
278 topo_mod_dprintf(mp
, "Attempt to create existing did_t.\n");
279 assert(ibus
== TRUST_BDF
|| (pd
->dp_bus
== ibus
));
283 if ((np
= topo_mod_zalloc(mp
, sizeof (did_t
))) == NULL
)
287 np
->dp_hash
= (did_hash_t
*)topo_mod_getspecific(mp
);
291 * We must have a reg prop and from it we extract the bus #,
292 * device #, and function #.
294 if (di_uintprop_get(mp
, src
, DI_REGPROP
, ®
) < 0) {
295 topo_mod_free(mp
, np
, sizeof (did_t
));
299 np
->dp_bridge
= ibrdge
;
301 if (ibus
== TRUST_BDF
)
302 np
->dp_bus
= PCI_REG_BUS_G(reg
);
305 np
->dp_dev
= PCI_REG_DEV_G(reg
);
306 np
->dp_fn
= PCI_REG_FUNC_G(reg
);
307 np
->dp_bdf
= (PCI_REG_BUS_G(reg
) << 8) | (PCI_REG_DEV_G(reg
) << 3) |
310 * There *may* be a class code we can capture. If there wasn't
311 * one, capture that fact by setting the class value to -1.
313 if (di_uintprop_get(mp
, src
, DI_CCPROP
, &code
) == 0) {
314 np
->dp_class
= GETCLASS(code
);
315 np
->dp_subclass
= GETSUBCLASS(code
);
320 * There *may* be a device type we can capture.
322 (void) di_devtype_get(mp
, src
, &np
->dp_devtype
);
326 * This is a pciex node.
328 if (di_physlotinfo_get(mp
, src
, &np
->dp_physlot
,
329 &np
->dp_physlot_name
) < 0) {
330 if (np
->dp_devtype
!= NULL
)
331 topo_mod_strfree(mp
, np
->dp_devtype
);
332 topo_mod_free(mp
, np
, sizeof (did_t
));
337 * This is a pci node.
340 if (di_slotinfo_get(mp
, src
, &np
->dp_nslots
,
341 &np
->dp_slotnames
) < 0) {
342 if (np
->dp_devtype
!= NULL
)
343 topo_mod_strfree(mp
, np
->dp_devtype
);
344 topo_mod_free(mp
, np
, sizeof (did_t
));
348 did_hash_insert(mp
, src
, np
);
354 did_link_get(did_t
*dp
)
357 return (dp
->dp_link
);
361 did_chain_get(did_t
*dp
)
364 return (dp
->dp_chain
);
368 did_link_set(topo_mod_t
*mod
, tnode_t
*head
, did_t
*tail
)
372 assert(head
!= NULL
);
373 pd
= hd
= did_find(mod
, topo_node_getspecific(head
));
375 while ((hd
= did_link_get(hd
)) != NULL
)
378 tail
->dp_link
= NULL
;
382 did_did_link_set(did_t
*from
, did_t
*to
)
384 assert(from
!= NULL
&& to
!= NULL
);
389 did_did_chain_set(did_t
*from
, did_t
*to
)
391 assert(from
!= NULL
&& to
!= NULL
);
396 did_destroy(did_t
*dp
)
401 * did_destroy() is called only from did_hash_destroy() when
402 * all references to the did_t have been released. We can
403 * safely destroy the did_t. If at some later time, more
404 * fine-grained reference count control is desired, this
405 * code will need to change
408 if (dp
->dp_devtype
!= NULL
)
409 topo_mod_strfree(dp
->dp_mod
, dp
->dp_devtype
);
410 if (dp
->dp_physlot_name
!= NULL
)
411 topo_mod_strfree(dp
->dp_mod
, dp
->dp_physlot_name
);
412 if (dp
->dp_slot_label
!= NULL
)
413 topo_mod_strfree(dp
->dp_mod
, dp
->dp_slot_label
);
414 slotnm_destroy(dp
->dp_slotnames
);
415 topo_mod_free(dp
->dp_mod
, dp
, sizeof (did_t
));
429 assert(dp
->dp_refcnt
> 0);
434 did_dinode(did_t
*dp
)
437 assert(dp
->dp_src
!= NULL
);
449 did_markrc(did_t
*dp
)
452 dp
->dp_excap
|= PCIE_PCIECAP_DEV_TYPE_ROOT
;
456 did_BDF(did_t
*dp
, int *bus
, int *dev
, int *fn
)
468 did_board(did_t
*did
)
471 return (did
->dp_board
);
475 did_bridge(did_t
*did
)
478 return (did
->dp_bridge
);
492 return ((int)dp
->dp_excap
);
496 did_excap_set(did_t
*dp
, int type
)
505 return ((int)dp
->dp_bdf
);
509 did_physlot_name(did_t
*dp
, int dev
)
516 * For pciex, name will be in dp_physlot_name
518 if (dp
->dp_physlot_name
!= NULL
)
519 return (dp
->dp_physlot_name
);
522 * For pci, name will be in dp_slotnames
524 for (slot
= dp
->dp_slotnames
; slot
!= NULL
; slot
= slot
->snm_next
)
525 if (slot
->snm_dev
== dev
)
528 return (slot
->snm_name
);
533 did_slot_label_get(did_t
*did
)
536 return (did
->dp_slot_label
);
540 did_slot_label_set(did_t
*did
, char *l
)
543 did
->dp_slot_label
= l
;
547 did_find(topo_mod_t
*mp
, di_node_t dn
)
549 return (did_hash_lookup(mp
, dn
));
553 pci_BDF_get(topo_mod_t
*mp
, di_node_t dn
, int *bus
, int *dev
, int *fn
)
557 if ((dp
= did_find(mp
, dn
)) == NULL
)
567 pci_classcode_get(topo_mod_t
*mp
, di_node_t dn
, uint_t
*class, uint_t
*sub
)
571 if ((dp
= did_find(mp
, dn
)) == NULL
)
573 if (dp
->dp_class
< 0) {
577 *class = dp
->dp_class
;
578 *sub
= dp
->dp_subclass
;
584 pci_devtype_get(topo_mod_t
*mp
, di_node_t dn
)
588 if ((dp
= did_find(mp
, dn
)) == NULL
)
591 return (dp
->dp_devtype
);
595 pciex_cap_get(topo_mod_t
*mp
, di_node_t dn
)
599 if ((dp
= did_find(mp
, dn
)) == NULL
)
602 return (dp
->dp_excap
);
606 did_setspecific(topo_mod_t
*mp
, void *data
)
610 hbdid
= (did_t
*)data
;
611 topo_mod_setspecific(mp
, hbdid
->dp_hash
);
615 did_settnode(did_t
*pd
, tnode_t
*tn
)
622 did_gettnode(did_t
*pd
)
624 return (pd
->dp_tnode
);