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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2019 Joyent, Inc.
25 */
26
27/*
28 * did.c
29 *	The acronym did means "Dev-Info-Data".  Many properties and
30 *	characteristics of topology nodes are, with a bit of coaxing
31 *	derived from devinfo nodes.  These routines do some of the
32 *	derivation and also encapsulate the discoveries in did_t
33 *	structures that get associated with topology nodes as their
34 *	"private" data.
35 */
36#include <alloca.h>
37#include <assert.h>
38#include <string.h>
39#include <strings.h>
40#include <sys/types.h>
41#include <fm/topo_mod.h>
42#include <libnvpair.h>
43#include <libdevinfo.h>
44#include <sys/pcie.h>
45
46#include <hostbridge.h>
47#include <pcibus.h>
48#include <did_props.h>
49
50#include "did_impl.h"
51
52static void slotnm_destroy(slotnm_t *);
53
54static slotnm_t *
55slotnm_create(topo_mod_t *mp, int dev, char *str)
56{
57	slotnm_t *p;
58
59	if ((p = topo_mod_alloc(mp, sizeof (slotnm_t))) == NULL)
60		return (NULL);
61	p->snm_mod = mp;
62	p->snm_next = NULL;
63	p->snm_dev = dev;
64	p->snm_name = topo_mod_strdup(mp, str);
65	if (p->snm_name == NULL) {
66		slotnm_destroy(p);
67		return (NULL);
68	}
69	return (p);
70}
71
72static void
73slotnm_destroy(slotnm_t *p)
74{
75	if (p == NULL)
76		return;
77	slotnm_destroy(p->snm_next);
78	if (p->snm_name != NULL)
79		topo_mod_strfree(p->snm_mod, p->snm_name);
80	topo_mod_free(p->snm_mod, p, sizeof (slotnm_t));
81}
82
83static int
84di_devtype_get(topo_mod_t *mp, di_node_t src, char **devtype)
85{
86	int sz;
87	uchar_t *buf;
88
89	/*
90	 * For PCI the device type defined the type of device directly below.
91	 * For PCIe RP and Switches, the device-type should be "pciex".  For
92	 * PCIe-PCI and PCI-PCI bridges it should be "pci".  NICs = "network",
93	 * Graphics = "display", etc..
94	 */
95	if (di_bytes_get(mp, src, DI_DEVTYPPROP, &sz, &buf) == 0) {
96		*devtype = topo_mod_strdup(mp, (char *)buf);
97	} else {
98		*devtype = NULL;
99	}
100
101	if (*devtype != NULL)
102		return (0);
103	return (-1);
104}
105
106typedef struct smbios_slot_cb {
107	int		cb_slotnum;
108	int		cb_bdf;
109	const char	*cb_label;
110} smbios_slot_cb_t;
111
112static int
113di_smbios_find_slot_by_bdf(smbios_hdl_t *shp, const smbios_struct_t *strp,
114    void *data)
115{
116	smbios_slot_cb_t *cbp = data;
117	smbios_slot_t slot;
118	int bus, df;
119
120	bus = (cbp->cb_bdf & 0xFF00) >> 8;
121	df = cbp->cb_bdf & 0xFF;
122
123	if (strp->smbstr_type != SMB_TYPE_SLOT ||
124	    smbios_info_slot(shp, strp->smbstr_id, &slot) != 0)
125		return (0);
126
127	if (slot.smbl_bus == bus && slot.smbl_df == df) {
128		cbp->cb_label = slot.smbl_name;
129		cbp->cb_slotnum = slot.smbl_id;
130		return (1);
131	}
132
133	return (0);
134}
135
136static int
137di_smbios_find_slot_by_id(smbios_hdl_t *shp, const smbios_struct_t *strp,
138    void *data)
139{
140	smbios_slot_cb_t *cbp = data;
141	smbios_slot_t slot;
142
143	if (strp->smbstr_type != SMB_TYPE_SLOT ||
144	    smbios_info_slot(shp, strp->smbstr_id, &slot) != 0)
145		return (0);
146
147	if (slot.smbl_id == cbp->cb_slotnum) {
148		cbp->cb_label = slot.smbl_name;
149		return (1);
150	}
151
152	return (0);
153}
154
155static int
156di_physlotinfo_get(topo_mod_t *mp, di_node_t src, int bdf, int *slotnum,
157    char **slotname)
158{
159	char *slotbuf = NULL;
160	int sz;
161	uchar_t *buf;
162	smbios_hdl_t *shp;
163	boolean_t got_slotprop = B_FALSE;
164
165	*slotnum = -1;
166
167	(void) di_uintprop_get(mp, src, DI_PHYSPROP, (uint_t *)slotnum);
168
169	/*
170	 * For PCI-Express, there is only one downstream device, so check for
171	 * a slot-names property, and if it exists, ignore the slotmask value
172	 * and use the string as the label.
173	 */
174	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 &&
175	    sz > 4) {
176		/*
177		 * If there is a DI_SLOTPROP of the form SlotX (ie set up from
178		 * the IRQ routing table) then trust that in preference to
179		 * DI_PHYSPROP (which is set up from the PCIe slotcap reg).
180		 */
181		got_slotprop = B_TRUE;
182		(void) sscanf((char *)&buf[4], "Slot%d", slotnum);
183	}
184
185	/*
186	 * If the system supports SMBIOS (virtual certainty on X86) then we will
187	 * source the label from the Type 9 (Slot) records.  If we're unable
188	 * to correlate the device with a slot record (as would happen with
189	 * onboard PCIe devices), we return without setting slotname, which will
190	 * ultimately result in the node inheriting the FRU label from its
191	 * parent node.
192	 *
193	 * In the absence of any SMBIOS support (i.e. SPARC) then we will use
194	 * the slot-names property, if available.  Otherwise we'll fall back
195	 * to fabricating a label based on the slot number.
196	 */
197	if ((shp = topo_mod_smbios(mp)) != NULL) {
198		/*
199		 * The PCI spec describes slot number 0 as reserved for
200		 * internal PCI devices.  Unfortunately, not all platforms
201		 * respect this.  For that reason, we prefer to lookup the slot
202		 * record using the device's BDF.  However, SMBIOS
203		 * implementations prior to 2.6 don't encode the BDF in the
204		 * slot record.  In that case we resort to looking up the
205		 * slot record using the slot number.
206		 */
207		smbios_slot_cb_t cbdata;
208		smbios_version_t smbv;
209		boolean_t bdf_supp = B_TRUE;
210
211		cbdata.cb_slotnum = *slotnum;
212		cbdata.cb_bdf = bdf;
213		cbdata.cb_label = NULL;
214
215		/*
216		 * The bus and device/fn payload members of the SMBIOS slot
217		 * record were added in SMBIOS 2.6.
218		 */
219		smbios_info_smbios_version(shp, &smbv);
220		if (smbv.smbv_major < 2 ||
221		    (smbv.smbv_major == 2 && smbv.smbv_minor < 6)) {
222			bdf_supp = B_FALSE;
223		}
224
225		/*
226		 * If the SMBIOS implementation is too old to look up the slot
227		 * records by BDF and we weren't able to derive a slotnum then
228		 * there is nothing we can do here.
229		 */
230		if (!bdf_supp && *slotnum == -1)
231			return (0);
232
233		if (bdf_supp)
234			(void) smbios_iter(shp, di_smbios_find_slot_by_bdf,
235			    &cbdata);
236		else
237			(void) smbios_iter(shp, di_smbios_find_slot_by_id,
238			    &cbdata);
239
240		if (cbdata.cb_label == NULL)
241			return (0);
242
243		slotbuf = (char *)cbdata.cb_label;
244		topo_mod_dprintf(mp, "%s: di_node=%p: using smbios name: %s\n",
245		    __func__, src, slotbuf);
246	} else if (got_slotprop) {
247		slotbuf = (char *)&buf[4];
248		topo_mod_dprintf(mp, "%s: di_node=%p: using %s property: %s\n",
249		    __func__, src, DI_SLOTPROP, slotbuf);
250	} else {
251		/*
252		 * Make generic description string "SLOT <num>", allow up to
253		 * 10 digits for number.  Bail, if we weren't able to derive
254		 * a slotnum.
255		 */
256		if (*slotnum == -1)
257			return (0);
258
259		slotbuf = alloca(16);
260		(void) snprintf(slotbuf, 16, "SLOT %d", *slotnum);
261		topo_mod_dprintf(mp, "%s: di_node=%p: using fabricated slot "
262		    "name: %s\n", __func__, src, slotbuf);
263	}
264
265	if ((*slotname = topo_mod_strdup(mp, slotbuf)) == NULL) {
266		/* topo errno set */
267		return (-1);
268	}
269	return (0);
270}
271
272static int
273di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots,
274    slotnm_t **slotnames)
275{
276	slotnm_t *lastslot = NULL;
277	slotnm_t *newslot;
278	uchar_t *slotbuf;
279	uint_t slotmap = 0;
280	char *slotname;
281	int andbit;
282	int sz = -1;
283
284	*slotnames = NULL;
285	*nslots = 0;
286	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &slotbuf) < 0)
287		return (0);
288	if (sz < sizeof (uint_t))
289		return (0);
290	bcopy(slotbuf, &slotmap, sizeof (uint_t));
291	if (slotmap == 0)
292		return (0);
293
294	slotname = (char *)&slotbuf[4];
295	for (andbit = 0; andbit < 32; andbit++) {
296		if (slotmap & (1 << andbit)) {
297			char *s = slotname;
298			slotname += strlen(s) + 1;
299			if ((newslot = slotnm_create(mp, andbit, s)) == NULL) {
300				slotnm_destroy(*slotnames);
301				*slotnames = NULL;
302				*nslots = 0;
303				return (-1);
304			}
305			if (lastslot == NULL)
306				*slotnames = lastslot = newslot;
307			else {
308				lastslot->snm_next = newslot;
309				lastslot = newslot;
310			}
311			(*nslots)++;
312		}
313	}
314	return (0);
315}
316
317int
318did_physlot(did_t *did)
319{
320	assert(did != NULL);
321	return (did->dp_physlot);
322}
323
324int
325did_physlot_exists(did_t *did)
326{
327	assert(did != NULL);
328	return ((did->dp_physlot >= 0) || (did->dp_nslots > 0));
329}
330
331did_t *
332did_create(topo_mod_t *mp, di_node_t src,
333    int ibrd, int ibrdge, int irc, int ibus)
334{
335	did_t *np;
336	did_t *pd;
337	uint_t code;
338	uint_t reg;
339
340	if ((pd = did_hash_lookup(mp, src)) != NULL) {
341		topo_mod_dprintf(mp, "Attempt to create existing did_t.\n");
342		assert(ibus == TRUST_BDF || (pd->dp_bus == ibus));
343		return (pd);
344	}
345
346	if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL)
347		return (NULL);
348	np->dp_mod = mp;
349	np->dp_src = src;
350	np->dp_hash = (did_hash_t *)topo_mod_getspecific(mp);
351	np->dp_tnode = NULL;
352
353	/*
354	 * We must have a reg prop and from it we extract the bus #,
355	 * device #, and function #.
356	 */
357	if (di_uintprop_get(mp, src, DI_REGPROP, &reg) < 0) {
358		topo_mod_free(mp, np, sizeof (did_t));
359		return (NULL);
360	}
361	np->dp_board = ibrd;
362	np->dp_bridge = ibrdge;
363	np->dp_rc = irc;
364	if (ibus == TRUST_BDF)
365		np->dp_bus = PCI_REG_BUS_G(reg);
366	else
367		np->dp_bus = ibus;
368	np->dp_dev = PCI_REG_DEV_G(reg);
369	np->dp_fn = PCI_REG_FUNC_G(reg);
370	np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) |
371	    PCI_REG_FUNC_G(reg);
372	/*
373	 * There *may* be a class code we can capture.  If there wasn't
374	 * one, capture that fact by setting the class value to -1.
375	 */
376	if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) {
377		np->dp_class = GETCLASS(code);
378		np->dp_subclass = GETSUBCLASS(code);
379	} else {
380		np->dp_class = -1;
381	}
382	/*
383	 * There *may* be a device type we can capture.
384	 */
385	(void) di_devtype_get(mp, src, &np->dp_devtype);
386
387	if (irc >= 0) {
388		/*
389		 * This is a pciex node.
390		 */
391		if (di_physlotinfo_get(mp, src, np->dp_bdf, &np->dp_physlot,
392		    &np->dp_physlot_name) < 0) {
393			if (np->dp_devtype != NULL)
394				topo_mod_strfree(mp, np->dp_devtype);
395			topo_mod_free(mp, np, sizeof (did_t));
396			return (NULL);
397		}
398	} else {
399		/*
400		 * This is a pci node.
401		 */
402		np->dp_physlot = -1;
403		if (di_slotinfo_get(mp, src, &np->dp_nslots,
404		    &np->dp_slotnames) < 0) {
405			if (np->dp_devtype != NULL)
406				topo_mod_strfree(mp, np->dp_devtype);
407			topo_mod_free(mp, np, sizeof (did_t));
408			return (NULL);
409		}
410	}
411	did_hash_insert(mp, src, np);
412	did_hold(np);
413	return (np);
414}
415
416did_t *
417did_link_get(did_t *dp)
418{
419	assert(dp != NULL);
420	return (dp->dp_link);
421}
422
423did_t *
424did_chain_get(did_t *dp)
425{
426	assert(dp != NULL);
427	return (dp->dp_chain);
428}
429
430void
431did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail)
432{
433	did_t *hd, *pd;
434
435	assert(head != NULL);
436	pd = hd = did_find(mod, topo_node_getspecific(head));
437	assert(hd != NULL);
438	while ((hd = did_link_get(hd)) != NULL)
439		pd = hd;
440	pd->dp_link = tail;
441	tail->dp_link = NULL;
442}
443
444void
445did_did_link_set(did_t *from, did_t *to)
446{
447	assert(from != NULL && to != NULL);
448	from->dp_link = to;
449}
450
451void
452did_did_chain_set(did_t *from, did_t *to)
453{
454	assert(from != NULL && to != NULL);
455	from->dp_chain = to;
456}
457
458void
459did_destroy(did_t *dp)
460{
461	assert(dp != NULL);
462
463	/*
464	 * did_destroy() is called only from did_hash_destroy() when
465	 * all references to the did_t have been released.  We can
466	 * safely destroy the did_t.  If at some later time, more
467	 * fine-grained reference count control is desired, this
468	 * code will need to change
469	 */
470
471	if (dp->dp_devtype != NULL)
472		topo_mod_strfree(dp->dp_mod, dp->dp_devtype);
473	if (dp->dp_physlot_name != NULL)
474		topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name);
475	if (dp->dp_slot_label != NULL)
476		topo_mod_strfree(dp->dp_mod, dp->dp_slot_label);
477	slotnm_destroy(dp->dp_slotnames);
478	topo_mod_free(dp->dp_mod, dp, sizeof (did_t));
479}
480
481void
482did_hold(did_t *dp)
483{
484	assert(dp != NULL);
485	dp->dp_refcnt++;
486}
487
488void
489did_rele(did_t *dp)
490{
491	assert(dp != NULL);
492	assert(dp->dp_refcnt > 0);
493	dp->dp_refcnt--;
494}
495
496di_node_t
497did_dinode(did_t *dp)
498{
499	assert(dp != NULL);
500	assert(dp->dp_src != NULL);
501	return (dp->dp_src);
502}
503
504topo_mod_t *
505did_mod(did_t *dp)
506{
507	assert(dp != NULL);
508	return (dp->dp_mod);
509}
510
511void
512did_markrc(did_t *dp)
513{
514	assert(dp != NULL);
515	dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
516}
517
518void
519did_BDF(did_t *dp, int *bus, int *dev, int *fn)
520{
521	assert(dp != NULL);
522	if (bus != NULL)
523		*bus = dp->dp_bus;
524	if (dev != NULL)
525		*dev = dp->dp_dev;
526	if (fn != NULL)
527		*fn = dp->dp_fn;
528}
529
530int
531did_board(did_t *did)
532{
533	assert(did != NULL);
534	return (did->dp_board);
535}
536
537int
538did_bridge(did_t *did)
539{
540	assert(did != NULL);
541	return (did->dp_bridge);
542}
543
544int
545did_rc(did_t *did)
546{
547	assert(did != NULL);
548	return (did->dp_rc);
549}
550
551int
552did_excap(did_t *dp)
553{
554	assert(dp != NULL);
555	return ((int)dp->dp_excap);
556}
557
558void
559did_excap_set(did_t *dp, int type)
560{
561	dp->dp_excap = type;
562}
563
564int
565did_bdf(did_t *dp)
566{
567	assert(dp != NULL);
568	return ((int)dp->dp_bdf);
569}
570
571const char *
572did_physlot_name(did_t *dp, int dev)
573{
574	slotnm_t *slot;
575
576	assert(dp != NULL);
577
578	/*
579	 * For pciex, name will be in dp_physlot_name
580	 */
581	if (dp->dp_physlot_name != NULL)
582		return (dp->dp_physlot_name);
583
584	/*
585	 * For pci, name will be in dp_slotnames
586	 */
587	for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next)
588		if (slot->snm_dev == dev)
589			break;
590	if (slot != NULL)
591		return (slot->snm_name);
592	return (NULL);
593}
594
595char *
596did_slot_label_get(did_t *did)
597{
598	assert(did != NULL);
599	return (did->dp_slot_label);
600}
601
602void
603did_slot_label_set(did_t *did, char *l)
604{
605	assert(did != NULL);
606	did->dp_slot_label = l;
607}
608
609did_t *
610did_find(topo_mod_t *mp, di_node_t dn)
611{
612	return (did_hash_lookup(mp, dn));
613}
614
615int
616pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn)
617{
618	did_t *dp;
619
620	if ((dp = did_find(mp, dn)) == NULL)
621		return (-1);
622	*bus = dp->dp_bus;
623	*dev = dp->dp_dev;
624	*fn = dp->dp_fn;
625	did_rele(dp);
626	return (0);
627}
628
629int
630pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub)
631{
632	did_t *dp;
633
634	if ((dp = did_find(mp, dn)) == NULL)
635		return (-1);
636	if (dp->dp_class < 0) {
637		did_rele(dp);
638		return (-1);
639	}
640	*class = dp->dp_class;
641	*sub = dp->dp_subclass;
642	did_rele(dp);
643	return (0);
644}
645
646char *
647pci_devtype_get(topo_mod_t *mp, di_node_t dn)
648{
649	did_t *dp;
650
651	if ((dp = did_find(mp, dn)) == NULL)
652		return (NULL);
653	did_rele(dp);
654	return (dp->dp_devtype);
655}
656
657int
658pciex_cap_get(topo_mod_t *mp, di_node_t dn)
659{
660	did_t *dp;
661
662	if ((dp = did_find(mp, dn)) == NULL)
663		return (-1);
664	did_rele(dp);
665	return (dp->dp_excap);
666}
667
668void
669did_setspecific(topo_mod_t *mp, void *data)
670{
671	did_t *hbdid;
672
673	hbdid = (did_t *)data;
674	topo_mod_setspecific(mp, hbdid->dp_hash);
675}
676
677void
678did_settnode(did_t *pd, tnode_t *tn)
679{
680	assert(tn != NULL);
681	pd->dp_tnode = tn;
682}
683
684tnode_t *
685did_gettnode(did_t *pd)
686{
687	return (pd->dp_tnode);
688}
689