17687d0d8SRobert Mustacchi /*
27687d0d8SRobert Mustacchi  * This file and its contents are supplied under the terms of the
37687d0d8SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
47687d0d8SRobert Mustacchi  * You may only use this file in accordance with the terms of version
57687d0d8SRobert Mustacchi  * 1.0 of the CDDL.
67687d0d8SRobert Mustacchi  *
77687d0d8SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
87687d0d8SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
97687d0d8SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
107687d0d8SRobert Mustacchi  */
117687d0d8SRobert Mustacchi 
127687d0d8SRobert Mustacchi /*
1392f11af9SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
147687d0d8SRobert Mustacchi  */
157687d0d8SRobert Mustacchi 
167687d0d8SRobert Mustacchi /*
177687d0d8SRobert Mustacchi  * This file contains logic to walk and print a large chunk of configuration
187687d0d8SRobert Mustacchi  * space and many of the capabilities. There are multiple sub-commands that
197687d0d8SRobert Mustacchi  * vector into the same logic (e.g. 'save-cfgspace' and 'show-cfgspace'). In
207687d0d8SRobert Mustacchi  * general, there are a few major goals with this bit of code:
217687d0d8SRobert Mustacchi  *
227687d0d8SRobert Mustacchi  *  o Every field should strive to be parsable and therefore selectable for
237687d0d8SRobert Mustacchi  *    output. This drove the idea that every field has both a short name and a
247687d0d8SRobert Mustacchi  *    human name. The short name is a dot-delineated name. When in parsable
257687d0d8SRobert Mustacchi  *    mode, the name will always refer to a single field. However, for
267687d0d8SRobert Mustacchi  *    convenience for humans, when not trying to be parsable, we show the
277687d0d8SRobert Mustacchi  *    parents in the tree. That is if you specify something like
287687d0d8SRobert Mustacchi  *    'pcie.linkcap.maxspeed', in parsable mode you'll only get that; however,
297687d0d8SRobert Mustacchi  *    in non-parsable mode, you'll get an indication of the capability and
307687d0d8SRobert Mustacchi  *    register that field was in.
317687d0d8SRobert Mustacchi  *
327687d0d8SRobert Mustacchi  *  o Related to the above, parsable mode always outputs a raw, uninterpreted
337687d0d8SRobert Mustacchi  *    value. This was done on purpose. Some fields require interpreting multiple
347687d0d8SRobert Mustacchi  *    registers to have meaning and long strings aren't always the most useful.
357687d0d8SRobert Mustacchi  *
367687d0d8SRobert Mustacchi  *  o Every field isn't always pretty printed. This was generally just a
377687d0d8SRobert Mustacchi  *    decision based upon the field itself and how much work it'd be to fit it
387687d0d8SRobert Mustacchi  *    into the framework we have. In general, the ones we're mostly guilty of
397687d0d8SRobert Mustacchi  *    doing this with are related to cases where there's a scaling value in a
407687d0d8SRobert Mustacchi  *    subsequent register. If you find yourself wanting this, feel free to add
417687d0d8SRobert Mustacchi  *    it.
427687d0d8SRobert Mustacchi  *
437687d0d8SRobert Mustacchi  *  o Currently designated vendor-specific capabilities aren't included here (or
447687d0d8SRobert Mustacchi  *    any specific vendor-specific capabilities for that matter). If they are
457687d0d8SRobert Mustacchi  *    added, they should follow the same angle of using a name to represent a
467687d0d8SRobert Mustacchi  *    sub-capability as we did with HyperTransport.
477687d0d8SRobert Mustacchi  */
487687d0d8SRobert Mustacchi 
497687d0d8SRobert Mustacchi #include <err.h>
507687d0d8SRobert Mustacchi #include <strings.h>
517687d0d8SRobert Mustacchi #include <sys/sysmacros.h>
527687d0d8SRobert Mustacchi #include <sys/pci.h>
537687d0d8SRobert Mustacchi #include <sys/pcie.h>
547687d0d8SRobert Mustacchi #include <sys/debug.h>
557687d0d8SRobert Mustacchi #include <ofmt.h>
567687d0d8SRobert Mustacchi #include <sys/types.h>
577687d0d8SRobert Mustacchi #include <sys/stat.h>
587687d0d8SRobert Mustacchi #include <fcntl.h>
597687d0d8SRobert Mustacchi #include <unistd.h>
607687d0d8SRobert Mustacchi 
617687d0d8SRobert Mustacchi #include "pcieadm.h"
627687d0d8SRobert Mustacchi 
637687d0d8SRobert Mustacchi typedef enum pcieadm_cfgspace_op {
647687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_OP_PRINT,
657687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_OP_WRITE
667687d0d8SRobert Mustacchi } pcieadm_cfgspace_op_t;
677687d0d8SRobert Mustacchi 
687687d0d8SRobert Mustacchi typedef enum piceadm_cfgspace_flag {
697687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_F_PARSE	= 1 << 0,
707687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_F_SHORT	= 1 << 1,
717687d0d8SRobert Mustacchi } pcieadm_cfgspace_flags_t;
727687d0d8SRobert Mustacchi 
737687d0d8SRobert Mustacchi typedef enum pcieadm_cfgspace_otype {
747687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_OT_SHORT,
757687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_OT_HUMAN,
767687d0d8SRobert Mustacchi 	PCIEADM_CFGSPACE_OT_VALUE
777687d0d8SRobert Mustacchi } pcieadm_cfgsapce_otype_t;
787687d0d8SRobert Mustacchi 
797687d0d8SRobert Mustacchi typedef struct pcieadm_cfgspace_ofmt {
807687d0d8SRobert Mustacchi 	const char *pco_base;
817687d0d8SRobert Mustacchi 	const char *pco_short;
827687d0d8SRobert Mustacchi 	const char *pco_human;
837687d0d8SRobert Mustacchi 	uint64_t pco_value;
847687d0d8SRobert Mustacchi 	const char *pco_strval;
857687d0d8SRobert Mustacchi } pcieadm_cfgspace_ofmt_t;
867687d0d8SRobert Mustacchi 
877687d0d8SRobert Mustacchi typedef enum pcieadm_regdef_val {
887687d0d8SRobert Mustacchi 	PRDV_STRVAL,
897687d0d8SRobert Mustacchi 	PRDV_BITFIELD,
907687d0d8SRobert Mustacchi 	PRDV_HEX
917687d0d8SRobert Mustacchi } pcieadm_regdef_val_t;
927687d0d8SRobert Mustacchi 
937687d0d8SRobert Mustacchi typedef struct pcieadm_regdef_addend {
947687d0d8SRobert Mustacchi 	uint8_t pra_shift;
957687d0d8SRobert Mustacchi 	int64_t pra_addend;
967687d0d8SRobert Mustacchi } pcieadm_regdef_addend_t;
977687d0d8SRobert Mustacchi 
987687d0d8SRobert Mustacchi typedef struct pcieadm_regdef {
997687d0d8SRobert Mustacchi 	uint8_t prd_lowbit;
1007687d0d8SRobert Mustacchi 	uint8_t prd_hibit;
1017687d0d8SRobert Mustacchi 	const char *prd_short;
1027687d0d8SRobert Mustacchi 	const char *prd_human;
1037687d0d8SRobert Mustacchi 	pcieadm_regdef_val_t prd_valtype;
1047687d0d8SRobert Mustacchi 	union {
1057687d0d8SRobert Mustacchi 		/*
1067687d0d8SRobert Mustacchi 		 * Enough space for up to an 8-bit fields worth of values
1077687d0d8SRobert Mustacchi 		 * (though we expect most to be sparse).
1087687d0d8SRobert Mustacchi 		 */
1097687d0d8SRobert Mustacchi 		const char *prdv_strval[128];
1107687d0d8SRobert Mustacchi 		pcieadm_regdef_addend_t prdv_hex;
1117687d0d8SRobert Mustacchi 	} prd_val;
1127687d0d8SRobert Mustacchi } pcieadm_regdef_t;
1137687d0d8SRobert Mustacchi 
1147687d0d8SRobert Mustacchi typedef struct pcieadm_unitdef {
1157687d0d8SRobert Mustacchi 	const char *pcd_unit;
1167687d0d8SRobert Mustacchi 	uint32_t pcd_mult;
1177687d0d8SRobert Mustacchi } pcieadm_unitdef_t;
1187687d0d8SRobert Mustacchi 
1197687d0d8SRobert Mustacchi typedef struct pcieadm_strmap {
1207687d0d8SRobert Mustacchi 	const char *psr_str;
1217687d0d8SRobert Mustacchi 	uint64_t psr_val;
1227687d0d8SRobert Mustacchi } pcieadm_strmap_t;
1237687d0d8SRobert Mustacchi 
1247687d0d8SRobert Mustacchi typedef struct pcieadm_cfgspace_filter {
1257687d0d8SRobert Mustacchi 	const char *pcf_string;
1267687d0d8SRobert Mustacchi 	size_t pcf_len;
1277687d0d8SRobert Mustacchi 	boolean_t pcf_used;
1287687d0d8SRobert Mustacchi } pcieadm_cfgspace_filter_t;
1297687d0d8SRobert Mustacchi 
1307687d0d8SRobert Mustacchi typedef struct pcieadm_strfilt {
1317687d0d8SRobert Mustacchi 	struct pcieadm_strfilt *pstr_next;
1327687d0d8SRobert Mustacchi 	const char *pstr_str;
1337687d0d8SRobert Mustacchi 	char pstr_curgen[256];
1347687d0d8SRobert Mustacchi } pcieadm_strfilt_t;
1357687d0d8SRobert Mustacchi 
1367687d0d8SRobert Mustacchi /*
1377687d0d8SRobert Mustacchi  * Data is sized to be large enough that we can hold all of PCIe extended
1387687d0d8SRobert Mustacchi  * configuration space.
1397687d0d8SRobert Mustacchi  */
1407687d0d8SRobert Mustacchi typedef union pcieadm_cfgspace_data {
1417687d0d8SRobert Mustacchi 	uint8_t pcb_u8[PCIE_CONF_HDR_SIZE];
1427687d0d8SRobert Mustacchi 	uint32_t pcb_u32[PCIE_CONF_HDR_SIZE / 4];
1437687d0d8SRobert Mustacchi } pcieadm_cfgspace_data_t;
1447687d0d8SRobert Mustacchi 
1457687d0d8SRobert Mustacchi typedef struct pcieadm_cfgspace_walk {
1467687d0d8SRobert Mustacchi 	pcieadm_t *pcw_pcieadm;
1477687d0d8SRobert Mustacchi 	pcieadm_cfgspace_op_t pcw_op;
1487687d0d8SRobert Mustacchi 	uint32_t pcw_valid;
1497687d0d8SRobert Mustacchi 	pcieadm_cfgspace_data_t *pcw_data;
1507687d0d8SRobert Mustacchi 	uint16_t pcw_capoff;
1517687d0d8SRobert Mustacchi 	uint32_t pcw_caplen;
1527687d0d8SRobert Mustacchi 	int pcw_outfd;
1537687d0d8SRobert Mustacchi 	uint_t pcw_dtype;
1547687d0d8SRobert Mustacchi 	uint_t pcw_nlanes;
1557687d0d8SRobert Mustacchi 	uint_t pcw_pcietype;
1567687d0d8SRobert Mustacchi 	uint_t pcw_nfilters;
1577687d0d8SRobert Mustacchi 	pcieadm_cfgspace_filter_t *pcw_filters;
1587687d0d8SRobert Mustacchi 	pcieadm_cfgspace_flags_t pcw_flags;
1597687d0d8SRobert Mustacchi 	ofmt_handle_t pcw_ofmt;
1607687d0d8SRobert Mustacchi 	pcieadm_strfilt_t *pcw_filt;
1617687d0d8SRobert Mustacchi } pcieadm_cfgspace_walk_t;
1627687d0d8SRobert Mustacchi 
1637687d0d8SRobert Mustacchi void
pcieadm_strfilt_pop(pcieadm_cfgspace_walk_t * walkp)1647687d0d8SRobert Mustacchi pcieadm_strfilt_pop(pcieadm_cfgspace_walk_t *walkp)
1657687d0d8SRobert Mustacchi {
1667687d0d8SRobert Mustacchi 	pcieadm_strfilt_t *filt;
1677687d0d8SRobert Mustacchi 
1687687d0d8SRobert Mustacchi 	VERIFY3P(walkp->pcw_filt, !=, NULL);
1697687d0d8SRobert Mustacchi 	filt = walkp->pcw_filt;
1707687d0d8SRobert Mustacchi 	walkp->pcw_filt = filt->pstr_next;
1717687d0d8SRobert Mustacchi 	free(filt);
1727687d0d8SRobert Mustacchi }
1737687d0d8SRobert Mustacchi 
1747687d0d8SRobert Mustacchi void
pcieadm_strfilt_push(pcieadm_cfgspace_walk_t * walkp,const char * str)1757687d0d8SRobert Mustacchi pcieadm_strfilt_push(pcieadm_cfgspace_walk_t *walkp, const char *str)
1767687d0d8SRobert Mustacchi {
1777687d0d8SRobert Mustacchi 	pcieadm_strfilt_t *filt;
1787687d0d8SRobert Mustacchi 	size_t len;
1797687d0d8SRobert Mustacchi 
1807687d0d8SRobert Mustacchi 	filt = calloc(1, sizeof (*filt));
1817687d0d8SRobert Mustacchi 	if (filt == NULL) {
1827687d0d8SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to allocate memory for string "
1837687d0d8SRobert Mustacchi 		    "filter");
1847687d0d8SRobert Mustacchi 	}
1857687d0d8SRobert Mustacchi 
1867687d0d8SRobert Mustacchi 	filt->pstr_str = str;
1877687d0d8SRobert Mustacchi 	if (walkp->pcw_filt == NULL) {
1887687d0d8SRobert Mustacchi 		len = strlcat(filt->pstr_curgen, str,
1897687d0d8SRobert Mustacchi 		    sizeof (filt->pstr_curgen));
1907687d0d8SRobert Mustacchi 	} else {
1917687d0d8SRobert Mustacchi 		len = snprintf(filt->pstr_curgen, sizeof (filt->pstr_curgen),
1927687d0d8SRobert Mustacchi 		    "%s.%s", walkp->pcw_filt->pstr_curgen, str);
1937687d0d8SRobert Mustacchi 		filt->pstr_next = walkp->pcw_filt;
1947687d0d8SRobert Mustacchi 	}
1957687d0d8SRobert Mustacchi 
1967687d0d8SRobert Mustacchi 	if (len >= sizeof (filt->pstr_curgen)) {
1977687d0d8SRobert Mustacchi 		errx(EXIT_FAILURE, "overflowed internal string buffer "
1987687d0d8SRobert Mustacchi 		    "appending %s", str);
1997687d0d8SRobert Mustacchi 	}
2007687d0d8SRobert Mustacchi 
2017687d0d8SRobert Mustacchi 	walkp->pcw_filt = filt;
2027687d0d8SRobert Mustacchi }
2037687d0d8SRobert Mustacchi 
2047687d0d8SRobert Mustacchi static boolean_t
pcieadm_cfgspace_filter(pcieadm_cfgspace_walk_t * walkp,const char * str)2057687d0d8SRobert Mustacchi pcieadm_cfgspace_filter(pcieadm_cfgspace_walk_t *walkp, const char *str)
2067687d0d8SRobert Mustacchi {
2077687d0d8SRobert Mustacchi 	char buf[1024];
2087687d0d8SRobert Mustacchi 	size_t len;
2097687d0d8SRobert Mustacchi 
2107687d0d8SRobert Mustacchi 	if (walkp->pcw_nfilters == 0) {
2117687d0d8SRobert Mustacchi 		return (B_TRUE);
2127687d0d8SRobert Mustacchi 	}
2137687d0d8SRobert Mustacchi 
2147687d0d8SRobert Mustacchi 	if (str == NULL) {
2157687d0d8SRobert Mustacchi 		return (B_FALSE);
2167687d0d8SRobert Mustacchi 	}
2177687d0d8SRobert Mustacchi 
2187687d0d8SRobert Mustacchi 	if (walkp->pcw_filt != NULL) {
2197687d0d8SRobert Mustacchi 		len = snprintf(buf, sizeof (buf), "%s.%s",
2207687d0d8SRobert Mustacchi 		    walkp->pcw_filt->pstr_curgen, str);
2217687d0d8SRobert Mustacchi 	} else {
2227687d0d8SRobert Mustacchi 		len = snprintf(buf, sizeof (buf), "%s", str);
2237687d0d8SRobert Mustacchi 	}
2247687d0d8SRobert Mustacchi 
2257687d0d8SRobert Mustacchi 	if (len >= sizeof (buf)) {
2267687d0d8SRobert Mustacchi 		abort();
2277687d0d8SRobert Mustacchi 	}
2287687d0d8SRobert Mustacchi 
2297687d0d8SRobert Mustacchi 	for (uint_t i = 0; i < walkp->pcw_nfilters; i++) {
2307687d0d8SRobert Mustacchi 		if (strcmp(buf, walkp->pcw_filters[i].pcf_string) == 0) {
2317687d0d8SRobert Mustacchi 			walkp->pcw_filters[i].pcf_used = B_TRUE;
2327687d0d8SRobert Mustacchi 			return (B_TRUE);
2337687d0d8SRobert Mustacchi 		}
2347687d0d8SRobert Mustacchi 
2357687d0d8SRobert Mustacchi 		/*
2367687d0d8SRobert Mustacchi 		 * If we're in non-parsable mode, we want to do a little bit
2377687d0d8SRobert Mustacchi 		 * more in a few cases. We want to make sure that we print the
2387687d0d8SRobert Mustacchi 		 * parents of more-specific entries. That is, if someone
2397687d0d8SRobert Mustacchi 		 * specified 'header.command.serr', then we want to print
2407687d0d8SRobert Mustacchi 		 * 'header', and 'header.command'. Similarly, if someone
2417687d0d8SRobert Mustacchi 		 * specifies an individual field, we want to print all of its
2427687d0d8SRobert Mustacchi 		 * subfields, that is asking for 'header.command', really gets
2437687d0d8SRobert Mustacchi 		 * that and all of 'header.command.*'.
2447687d0d8SRobert Mustacchi 		 */
2457687d0d8SRobert Mustacchi 		if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_PARSE) != 0) {
2467687d0d8SRobert Mustacchi 			continue;
2477687d0d8SRobert Mustacchi 		}
2487687d0d8SRobert Mustacchi 
2497687d0d8SRobert Mustacchi 		if (len >= walkp->pcw_filters[i].pcf_len) {
2507687d0d8SRobert Mustacchi 			if (strncmp(buf, walkp->pcw_filters[i].pcf_string,
2517687d0d8SRobert Mustacchi 			    walkp->pcw_filters[i].pcf_len) == 0 &&
2527687d0d8SRobert Mustacchi 			    buf[walkp->pcw_filters[i].pcf_len] == '.') {
2537687d0d8SRobert Mustacchi 				return (B_TRUE);
2547687d0d8SRobert Mustacchi 			}
2557687d0d8SRobert Mustacchi 		} else {
2567687d0d8SRobert Mustacchi 			if (strncmp(buf, walkp->pcw_filters[i].pcf_string,
2577687d0d8SRobert Mustacchi 			    len) == 0 &&
2587687d0d8SRobert Mustacchi 			    walkp->pcw_filters[i].pcf_string[len] == '.') {
2597687d0d8SRobert Mustacchi 				return (B_TRUE);
2607687d0d8SRobert Mustacchi 			}
2617687d0d8SRobert Mustacchi 		}
2627687d0d8SRobert Mustacchi 	}
2637687d0d8SRobert Mustacchi 
2647687d0d8SRobert Mustacchi 	return (B_FALSE);
2657687d0d8SRobert Mustacchi }
2667687d0d8SRobert Mustacchi 
2677687d0d8SRobert Mustacchi static boolean_t
pcieadm_cfgspace_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)2687687d0d8SRobert Mustacchi pcieadm_cfgspace_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
2697687d0d8SRobert Mustacchi {
2707687d0d8SRobert Mustacchi 	pcieadm_cfgspace_ofmt_t *pco = ofarg->ofmt_cbarg;
2717687d0d8SRobert Mustacchi 
2727687d0d8SRobert Mustacchi 	switch (ofarg->ofmt_id) {
2737687d0d8SRobert Mustacchi 	case PCIEADM_CFGSPACE_OT_SHORT:
2747687d0d8SRobert Mustacchi 		if (snprintf(buf, buflen, "%s.%s", pco->pco_base,
2757687d0d8SRobert Mustacchi 		    pco->pco_short) >= buflen) {
2767687d0d8SRobert Mustacchi 			return (B_FALSE);
2777687d0d8SRobert Mustacchi 		}
2787687d0d8SRobert Mustacchi 		break;
2797687d0d8SRobert Mustacchi 	case PCIEADM_CFGSPACE_OT_HUMAN:
2807687d0d8SRobert Mustacchi 		if (strlcpy(buf, pco->pco_human, buflen) >= buflen) {
2817687d0d8SRobert Mustacchi 			return (B_FALSE);
2827687d0d8SRobert Mustacchi 		}
2837687d0d8SRobert Mustacchi 		break;
2847687d0d8SRobert Mustacchi 	case PCIEADM_CFGSPACE_OT_VALUE:
2857687d0d8SRobert Mustacchi 		if (pco->pco_strval != NULL) {
2867687d0d8SRobert Mustacchi 			if (strlcpy(buf, pco->pco_strval, buflen) >= buflen) {
2877687d0d8SRobert Mustacchi 				return (B_FALSE);
2887687d0d8SRobert Mustacchi 			}
2897687d0d8SRobert Mustacchi 		} else {
2907687d0d8SRobert Mustacchi 			if (snprintf(buf, buflen, "0x%" PRIx64,
2917687d0d8SRobert Mustacchi 			    pco->pco_value) >= buflen) {
2927687d0d8SRobert Mustacchi 				return (B_FALSE);
2937687d0d8SRobert Mustacchi 			}
2947687d0d8SRobert Mustacchi 		}
2957687d0d8SRobert Mustacchi 		break;
2967687d0d8SRobert Mustacchi 	default:
2977687d0d8SRobert Mustacchi 		abort();
2987687d0d8SRobert Mustacchi 	}
2997687d0d8SRobert Mustacchi 
3007687d0d8SRobert Mustacchi 	return (B_TRUE);
3017687d0d8SRobert Mustacchi }
3027687d0d8SRobert Mustacchi 
3037687d0d8SRobert Mustacchi 
3047687d0d8SRobert Mustacchi static const ofmt_field_t pcieadm_cfgspace_ofmt[] = {
3057687d0d8SRobert Mustacchi 	{ "SHORT", 30, PCIEADM_CFGSPACE_OT_SHORT, pcieadm_cfgspace_ofmt_cb },
3067687d0d8SRobert Mustacchi 	{ "HUMAN", 30, PCIEADM_CFGSPACE_OT_HUMAN, pcieadm_cfgspace_ofmt_cb },
3077687d0d8SRobert Mustacchi 	{ "VALUE", 20, PCIEADM_CFGSPACE_OT_VALUE, pcieadm_cfgspace_ofmt_cb },
3087687d0d8SRobert Mustacchi 	{ NULL, 0, 0, NULL }
3097687d0d8SRobert Mustacchi };
3107687d0d8SRobert Mustacchi 
3117687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_parse(pcieadm_cfgspace_walk_t * walkp,const char * sname,const char * human,uint64_t value)3127687d0d8SRobert Mustacchi pcieadm_cfgspace_print_parse(pcieadm_cfgspace_walk_t *walkp,
3137687d0d8SRobert Mustacchi     const char *sname, const char *human, uint64_t value)
3147687d0d8SRobert Mustacchi {
3157687d0d8SRobert Mustacchi 	pcieadm_cfgspace_ofmt_t pco;
3167687d0d8SRobert Mustacchi 
3177687d0d8SRobert Mustacchi 	VERIFY3P(walkp->pcw_filt, !=, NULL);
3187687d0d8SRobert Mustacchi 	pco.pco_base = walkp->pcw_filt->pstr_curgen;
3197687d0d8SRobert Mustacchi 	pco.pco_short = sname;
3207687d0d8SRobert Mustacchi 	pco.pco_human = human;
3217687d0d8SRobert Mustacchi 	pco.pco_value = value;
3227687d0d8SRobert Mustacchi 	pco.pco_strval = NULL;
3237687d0d8SRobert Mustacchi 	ofmt_print(walkp->pcw_ofmt, &pco);
3247687d0d8SRobert Mustacchi }
3257687d0d8SRobert Mustacchi 
3267687d0d8SRobert Mustacchi typedef struct pcieadm_cfgspace_print pcieadm_cfgspace_print_t;
3277687d0d8SRobert Mustacchi typedef void (*pcieadm_cfgspace_print_f)(pcieadm_cfgspace_walk_t *,
3287687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *, void *);
3297687d0d8SRobert Mustacchi 
3307687d0d8SRobert Mustacchi struct pcieadm_cfgspace_print {
3317687d0d8SRobert Mustacchi 	uint8_t pcp_off;
3327687d0d8SRobert Mustacchi 	uint8_t pcp_len;
3337687d0d8SRobert Mustacchi 	const char *pcp_short;
3347687d0d8SRobert Mustacchi 	const char *pcp_human;
3357687d0d8SRobert Mustacchi 	pcieadm_cfgspace_print_f pcp_print;
3367687d0d8SRobert Mustacchi 	void *pcp_arg;
3377687d0d8SRobert Mustacchi };
3387687d0d8SRobert Mustacchi 
3397687d0d8SRobert Mustacchi static void
pcieadm_field_printf(pcieadm_cfgspace_walk_t * walkp,const char * shortf,const char * humanf,uint64_t val,const char * fmt,...)3407687d0d8SRobert Mustacchi pcieadm_field_printf(pcieadm_cfgspace_walk_t *walkp, const char *shortf,
3417687d0d8SRobert Mustacchi     const char *humanf, uint64_t val, const char *fmt, ...)
3427687d0d8SRobert Mustacchi {
3437687d0d8SRobert Mustacchi 	va_list ap;
3447687d0d8SRobert Mustacchi 
3457687d0d8SRobert Mustacchi 	if (!pcieadm_cfgspace_filter(walkp, shortf))
3467687d0d8SRobert Mustacchi 		return;
3477687d0d8SRobert Mustacchi 
3487687d0d8SRobert Mustacchi 	if (walkp->pcw_ofmt != NULL) {
3497687d0d8SRobert Mustacchi 		pcieadm_cfgspace_print_parse(walkp, shortf, humanf, val);
3507687d0d8SRobert Mustacchi 		return;
3517687d0d8SRobert Mustacchi 	}
3527687d0d8SRobert Mustacchi 
3537687d0d8SRobert Mustacchi 	if (walkp->pcw_pcieadm->pia_indent > 0) {
3547687d0d8SRobert Mustacchi 		(void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
3557687d0d8SRobert Mustacchi 	}
3567687d0d8SRobert Mustacchi 
3577687d0d8SRobert Mustacchi 	if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
3587687d0d8SRobert Mustacchi 		(void) printf("|--> %s (%s.%s): ", humanf,
3597687d0d8SRobert Mustacchi 		    walkp->pcw_filt->pstr_curgen, shortf);
3607687d0d8SRobert Mustacchi 	} else {
3617687d0d8SRobert Mustacchi 		(void) printf("|--> %s: ", humanf);
3627687d0d8SRobert Mustacchi 	}
3637687d0d8SRobert Mustacchi 
3647687d0d8SRobert Mustacchi 	va_start(ap, fmt);
3657687d0d8SRobert Mustacchi 	(void) vprintf(fmt, ap);
3667687d0d8SRobert Mustacchi 	va_end(ap);
3677687d0d8SRobert Mustacchi 
3687687d0d8SRobert Mustacchi }
3697687d0d8SRobert Mustacchi 
3707687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_printf(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,uint64_t val,const char * fmt,...)3717687d0d8SRobert Mustacchi pcieadm_cfgspace_printf(pcieadm_cfgspace_walk_t *walkp,
3727687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, uint64_t val, const char *fmt, ...)
3737687d0d8SRobert Mustacchi {
3747687d0d8SRobert Mustacchi 	va_list ap;
3757687d0d8SRobert Mustacchi 
3767687d0d8SRobert Mustacchi 	if (!pcieadm_cfgspace_filter(walkp, print->pcp_short))
3777687d0d8SRobert Mustacchi 		return;
3787687d0d8SRobert Mustacchi 
3797687d0d8SRobert Mustacchi 	if (walkp->pcw_ofmt != NULL) {
3807687d0d8SRobert Mustacchi 		pcieadm_cfgspace_print_parse(walkp, print->pcp_short,
3817687d0d8SRobert Mustacchi 		    print->pcp_human, val);
3827687d0d8SRobert Mustacchi 		return;
3837687d0d8SRobert Mustacchi 	}
3847687d0d8SRobert Mustacchi 
3857687d0d8SRobert Mustacchi 	if (walkp->pcw_pcieadm->pia_indent > 0) {
3867687d0d8SRobert Mustacchi 		(void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
3877687d0d8SRobert Mustacchi 	}
3887687d0d8SRobert Mustacchi 
3897687d0d8SRobert Mustacchi 	if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
3907687d0d8SRobert Mustacchi 		(void) printf("%s (%s.%s): ", print->pcp_human,
3917687d0d8SRobert Mustacchi 		    walkp->pcw_filt->pstr_curgen, print->pcp_short);
3927687d0d8SRobert Mustacchi 	} else {
3937687d0d8SRobert Mustacchi 		(void) printf("%s: ", print->pcp_human);
3947687d0d8SRobert Mustacchi 	}
3957687d0d8SRobert Mustacchi 
3967687d0d8SRobert Mustacchi 	va_start(ap, fmt);
3977687d0d8SRobert Mustacchi 	(void) vprintf(fmt, ap);
3987687d0d8SRobert Mustacchi 	va_end(ap);
3997687d0d8SRobert Mustacchi }
4007687d0d8SRobert Mustacchi 
4017687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_puts(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,const char * str)4027687d0d8SRobert Mustacchi pcieadm_cfgspace_puts(pcieadm_cfgspace_walk_t *walkp,
4037687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, const char *str)
4047687d0d8SRobert Mustacchi {
4057687d0d8SRobert Mustacchi 	if (!pcieadm_cfgspace_filter(walkp, print->pcp_short))
4067687d0d8SRobert Mustacchi 		return;
4077687d0d8SRobert Mustacchi 
4087687d0d8SRobert Mustacchi 	if (walkp->pcw_ofmt != NULL) {
4097687d0d8SRobert Mustacchi 		pcieadm_cfgspace_ofmt_t pco;
4107687d0d8SRobert Mustacchi 
4117687d0d8SRobert Mustacchi 		VERIFY3P(walkp->pcw_filt, !=, NULL);
4127687d0d8SRobert Mustacchi 		pco.pco_base = walkp->pcw_filt->pstr_curgen;
4137687d0d8SRobert Mustacchi 		pco.pco_short = print->pcp_short;
4147687d0d8SRobert Mustacchi 		pco.pco_human = print->pcp_human;
4157687d0d8SRobert Mustacchi 		pco.pco_strval = str;
4167687d0d8SRobert Mustacchi 		ofmt_print(walkp->pcw_ofmt, &pco);
4177687d0d8SRobert Mustacchi 		return;
4187687d0d8SRobert Mustacchi 	}
4197687d0d8SRobert Mustacchi 
4207687d0d8SRobert Mustacchi 	if (walkp->pcw_pcieadm->pia_indent > 0) {
4217687d0d8SRobert Mustacchi 		(void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
4227687d0d8SRobert Mustacchi 	}
4237687d0d8SRobert Mustacchi 
4247687d0d8SRobert Mustacchi 	if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
4257687d0d8SRobert Mustacchi 		(void) printf("%s (%s.%s): %s\n", print->pcp_human,
4267687d0d8SRobert Mustacchi 		    walkp->pcw_filt->pstr_curgen, print->pcp_short, str);
4277687d0d8SRobert Mustacchi 	} else {
4287687d0d8SRobert Mustacchi 		(void) printf("%s: %s\n", print->pcp_human, str);
4297687d0d8SRobert Mustacchi 	}
4307687d0d8SRobert Mustacchi }
4317687d0d8SRobert Mustacchi 
4327687d0d8SRobert Mustacchi static uint64_t
pcieadm_cfgspace_extract(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print)4337687d0d8SRobert Mustacchi pcieadm_cfgspace_extract(pcieadm_cfgspace_walk_t *walkp,
4347687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print)
4357687d0d8SRobert Mustacchi {
4367687d0d8SRobert Mustacchi 	uint32_t val = 0;
4377687d0d8SRobert Mustacchi 
4387687d0d8SRobert Mustacchi 	VERIFY3U(print->pcp_len, <=, 8);
4397687d0d8SRobert Mustacchi 	VERIFY3U(print->pcp_off + print->pcp_len + walkp->pcw_capoff, <=,
4407687d0d8SRobert Mustacchi 	    walkp->pcw_valid);
4417687d0d8SRobert Mustacchi 	for (uint8_t i = print->pcp_len; i > 0; i--) {
4427687d0d8SRobert Mustacchi 		val <<= 8;
4437687d0d8SRobert Mustacchi 		val |= walkp->pcw_data->pcb_u8[walkp->pcw_capoff +
4447687d0d8SRobert Mustacchi 		    print->pcp_off + i - 1];
4457687d0d8SRobert Mustacchi 	}
4467687d0d8SRobert Mustacchi 
4477687d0d8SRobert Mustacchi 	return (val);
4487687d0d8SRobert Mustacchi }
4497687d0d8SRobert Mustacchi 
4507687d0d8SRobert Mustacchi static uint16_t
pcieadm_cfgspace_extract_u16(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print)4517687d0d8SRobert Mustacchi pcieadm_cfgspace_extract_u16(pcieadm_cfgspace_walk_t *walkp,
4527687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print)
4537687d0d8SRobert Mustacchi {
4547687d0d8SRobert Mustacchi 	VERIFY(print->pcp_len == 2);
4557687d0d8SRobert Mustacchi 	return ((uint16_t)pcieadm_cfgspace_extract(walkp, print));
4567687d0d8SRobert Mustacchi }
4577687d0d8SRobert Mustacchi 
4587687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_unit(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)4597687d0d8SRobert Mustacchi pcieadm_cfgspace_print_unit(pcieadm_cfgspace_walk_t *walkp,
4607687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
4617687d0d8SRobert Mustacchi {
4627687d0d8SRobert Mustacchi 	pcieadm_unitdef_t *unit = arg;
4637687d0d8SRobert Mustacchi 	uint64_t rawval = pcieadm_cfgspace_extract(walkp, print);
4647687d0d8SRobert Mustacchi 	uint64_t val = rawval;
4657687d0d8SRobert Mustacchi 
4667687d0d8SRobert Mustacchi 	if (unit->pcd_mult > 1) {
4677687d0d8SRobert Mustacchi 		val *= unit->pcd_mult;
4687687d0d8SRobert Mustacchi 	}
4697687d0d8SRobert Mustacchi 	pcieadm_cfgspace_printf(walkp, print, rawval, "0x%" PRIx64 " %s%s\n",
4707687d0d8SRobert Mustacchi 	    val, unit->pcd_unit, val != 1 ? "s" : "");
4717687d0d8SRobert Mustacchi }
4727687d0d8SRobert Mustacchi 
4737687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_regdef(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)4747687d0d8SRobert Mustacchi pcieadm_cfgspace_print_regdef(pcieadm_cfgspace_walk_t *walkp,
4757687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
4767687d0d8SRobert Mustacchi {
4777687d0d8SRobert Mustacchi 	pcieadm_regdef_t *regdef = arg;
4787687d0d8SRobert Mustacchi 	uint64_t val = pcieadm_cfgspace_extract(walkp, print);
4797687d0d8SRobert Mustacchi 
4807687d0d8SRobert Mustacchi 	pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val);
4817687d0d8SRobert Mustacchi 
4827687d0d8SRobert Mustacchi 	pcieadm_indent();
4837687d0d8SRobert Mustacchi 	pcieadm_strfilt_push(walkp, print->pcp_short);
4847687d0d8SRobert Mustacchi 
4857687d0d8SRobert Mustacchi 	for (regdef = arg; regdef->prd_short != NULL; regdef++) {
4867687d0d8SRobert Mustacchi 		uint32_t nbits = regdef->prd_hibit - regdef->prd_lowbit + 1UL;
4877687d0d8SRobert Mustacchi 		uint32_t bitmask = (1UL << nbits) - 1UL;
4887687d0d8SRobert Mustacchi 		uint64_t regval = (val >> regdef->prd_lowbit) & bitmask;
4897687d0d8SRobert Mustacchi 		const char *strval;
4907687d0d8SRobert Mustacchi 		uint64_t actval;
4917687d0d8SRobert Mustacchi 
4927687d0d8SRobert Mustacchi 		if (!pcieadm_cfgspace_filter(walkp, regdef->prd_short)) {
4937687d0d8SRobert Mustacchi 			continue;
4947687d0d8SRobert Mustacchi 		}
4957687d0d8SRobert Mustacchi 
4967687d0d8SRobert Mustacchi 		switch (regdef->prd_valtype) {
4977687d0d8SRobert Mustacchi 		case PRDV_STRVAL:
4987687d0d8SRobert Mustacchi 			strval = regdef->prd_val.prdv_strval[regval];
4997687d0d8SRobert Mustacchi 			if (strval == NULL) {
5007687d0d8SRobert Mustacchi 				strval = "reserved";
5017687d0d8SRobert Mustacchi 			}
50292f11af9SRobert Mustacchi 
5037687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, regdef->prd_short,
5047687d0d8SRobert Mustacchi 			    regdef->prd_human, regval, "%s (0x%" PRIx64 ")\n",
5057687d0d8SRobert Mustacchi 			    strval, regval << regdef->prd_lowbit);
5067687d0d8SRobert Mustacchi 			break;
5077687d0d8SRobert Mustacchi 		case PRDV_HEX:
5087687d0d8SRobert Mustacchi 			actval = regval;
5097687d0d8SRobert Mustacchi 			if (regdef->prd_val.prdv_hex.pra_shift > 0) {
5107687d0d8SRobert Mustacchi 				actval <<= regdef->prd_val.prdv_hex.pra_shift;
5117687d0d8SRobert Mustacchi 			}
5127687d0d8SRobert Mustacchi 			actval += regdef->prd_val.prdv_hex.pra_addend;
5137687d0d8SRobert Mustacchi 
5147687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, regdef->prd_short,
5157687d0d8SRobert Mustacchi 			    regdef->prd_human, regval, "0x% " PRIx64 "\n",
5167687d0d8SRobert Mustacchi 			    actval);
5177687d0d8SRobert Mustacchi 			break;
5187687d0d8SRobert Mustacchi 		case PRDV_BITFIELD:
5197687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, regdef->prd_short,
5207687d0d8SRobert Mustacchi 			    regdef->prd_human, regval, "0x%" PRIx64 "\n",
5217687d0d8SRobert Mustacchi 			    regval << regdef->prd_lowbit);
5227687d0d8SRobert Mustacchi 
5237687d0d8SRobert Mustacchi 			if (walkp->pcw_ofmt == NULL) {
5247687d0d8SRobert Mustacchi 				pcieadm_indent();
5257687d0d8SRobert Mustacchi 				for (uint32_t i = 0; i < nbits; i++) {
5267687d0d8SRobert Mustacchi 					if (((1 << i) & regval) == 0)
5277687d0d8SRobert Mustacchi 						continue;
5287687d0d8SRobert Mustacchi 					pcieadm_print("|--> %s (0x%x)\n",
5297687d0d8SRobert Mustacchi 					    regdef->prd_val.prdv_strval[i],
5307687d0d8SRobert Mustacchi 					    1UL << (i + regdef->prd_lowbit));
5317687d0d8SRobert Mustacchi 				}
5327687d0d8SRobert Mustacchi 				pcieadm_deindent();
5337687d0d8SRobert Mustacchi 			}
5347687d0d8SRobert Mustacchi 			break;
5357687d0d8SRobert Mustacchi 		}
5367687d0d8SRobert Mustacchi 	}
5377687d0d8SRobert Mustacchi 
5387687d0d8SRobert Mustacchi 	pcieadm_strfilt_pop(walkp);
5397687d0d8SRobert Mustacchi 	pcieadm_deindent();
5407687d0d8SRobert Mustacchi }
5417687d0d8SRobert Mustacchi 
5427687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_strmap(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)5437687d0d8SRobert Mustacchi pcieadm_cfgspace_print_strmap(pcieadm_cfgspace_walk_t *walkp,
5447687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
5457687d0d8SRobert Mustacchi {
5467687d0d8SRobert Mustacchi 	pcieadm_strmap_t *strmap = arg;
5477687d0d8SRobert Mustacchi 	uint64_t val = pcieadm_cfgspace_extract(walkp, print);
5487687d0d8SRobert Mustacchi 	const char *str = "reserved";
5497687d0d8SRobert Mustacchi 
5507687d0d8SRobert Mustacchi 	for (uint_t i = 0; strmap[i].psr_str != NULL; i++) {
5517687d0d8SRobert Mustacchi 		if (strmap[i].psr_val == val) {
5527687d0d8SRobert Mustacchi 			str = strmap[i].psr_str;
5537687d0d8SRobert Mustacchi 			break;
5547687d0d8SRobert Mustacchi 		}
5557687d0d8SRobert Mustacchi 	}
5567687d0d8SRobert Mustacchi 
5577687d0d8SRobert Mustacchi 	pcieadm_cfgspace_printf(walkp, print, val, "0x%x -- %s\n", val, str);
5587687d0d8SRobert Mustacchi }
5597687d0d8SRobert Mustacchi 
5607687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_hex(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)5617687d0d8SRobert Mustacchi pcieadm_cfgspace_print_hex(pcieadm_cfgspace_walk_t *walkp,
5627687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
5637687d0d8SRobert Mustacchi {
5647687d0d8SRobert Mustacchi 	uint64_t val = pcieadm_cfgspace_extract(walkp, print);
5657687d0d8SRobert Mustacchi 
5667687d0d8SRobert Mustacchi 	pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val);
5677687d0d8SRobert Mustacchi }
5687687d0d8SRobert Mustacchi 
5697687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_vendor(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)5707687d0d8SRobert Mustacchi pcieadm_cfgspace_print_vendor(pcieadm_cfgspace_walk_t *walkp,
5717687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
5727687d0d8SRobert Mustacchi {
5737687d0d8SRobert Mustacchi 	pcidb_vendor_t *vend;
5747687d0d8SRobert Mustacchi 	uint16_t vid = pcieadm_cfgspace_extract_u16(walkp, print);
5757687d0d8SRobert Mustacchi 
5767687d0d8SRobert Mustacchi 	vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb, vid);
5777687d0d8SRobert Mustacchi 	if (vend != NULL) {
5787687d0d8SRobert Mustacchi 		pcieadm_cfgspace_printf(walkp, print, vid, "0x%x -- %s\n", vid,
5797687d0d8SRobert Mustacchi 		    pcidb_vendor_name(vend));
5807687d0d8SRobert Mustacchi 	} else {
5817687d0d8SRobert Mustacchi 		pcieadm_cfgspace_printf(walkp, print, vid, "0x%x\n", vid);
5827687d0d8SRobert Mustacchi 	}
5837687d0d8SRobert Mustacchi }
5847687d0d8SRobert Mustacchi 
5857687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_device(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)5867687d0d8SRobert Mustacchi pcieadm_cfgspace_print_device(pcieadm_cfgspace_walk_t *walkp,
5877687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
5887687d0d8SRobert Mustacchi {
5897687d0d8SRobert Mustacchi 	pcidb_device_t *dev;
5907687d0d8SRobert Mustacchi 	uint16_t did = pcieadm_cfgspace_extract_u16(walkp, print);
5917687d0d8SRobert Mustacchi 	uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] +
5927687d0d8SRobert Mustacchi 	    (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8);
5937687d0d8SRobert Mustacchi 
5947687d0d8SRobert Mustacchi 	dev = pcidb_lookup_device(walkp->pcw_pcieadm->pia_pcidb, vid, did);
5957687d0d8SRobert Mustacchi 	if (dev != NULL) {
5967687d0d8SRobert Mustacchi 		pcieadm_cfgspace_printf(walkp, print, did, "0x%x -- %s\n", did,
5977687d0d8SRobert Mustacchi 		    pcidb_device_name(dev));
5987687d0d8SRobert Mustacchi 	} else {
5997687d0d8SRobert Mustacchi 		pcieadm_cfgspace_printf(walkp, print, did, "0x%x\n", did);
6007687d0d8SRobert Mustacchi 	}
6017687d0d8SRobert Mustacchi }
6027687d0d8SRobert Mustacchi 
6037687d0d8SRobert Mustacchi /*
6047687d0d8SRobert Mustacchi  * To print out detailed information about a subsystem vendor or device, we need
6057687d0d8SRobert Mustacchi  * all of the information about the vendor and device due to the organization of
6067687d0d8SRobert Mustacchi  * the PCI IDs db.
6077687d0d8SRobert Mustacchi  */
6087687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_subid(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)6097687d0d8SRobert Mustacchi pcieadm_cfgspace_print_subid(pcieadm_cfgspace_walk_t *walkp,
6107687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
6117687d0d8SRobert Mustacchi {
6127687d0d8SRobert Mustacchi 	uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] +
6137687d0d8SRobert Mustacchi 	    (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8);
6147687d0d8SRobert Mustacchi 	uint16_t did = walkp->pcw_data->pcb_u8[PCI_CONF_DEVID] +
6157687d0d8SRobert Mustacchi 	    (walkp->pcw_data->pcb_u8[PCI_CONF_DEVID + 1] << 8);
6167687d0d8SRobert Mustacchi 	uint16_t svid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID] +
6177687d0d8SRobert Mustacchi 	    (walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID + 1] << 8);
6187687d0d8SRobert Mustacchi 	uint16_t sdid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID] +
6197687d0d8SRobert Mustacchi 	    (walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID + 1] << 8);
6207687d0d8SRobert Mustacchi 	uint16_t val = pcieadm_cfgspace_extract_u16(walkp, print);
6217687d0d8SRobert Mustacchi 	boolean_t isvendor = print->pcp_off == PCI_CONF_SUBVENID;
6227687d0d8SRobert Mustacchi 
6237687d0d8SRobert Mustacchi 	if (isvendor) {
6247687d0d8SRobert Mustacchi 		pcidb_vendor_t *vend;
6257687d0d8SRobert Mustacchi 		vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb,
6267687d0d8SRobert Mustacchi 		    svid);
6277687d0d8SRobert Mustacchi 		if (vend != NULL) {
6287687d0d8SRobert Mustacchi 			pcieadm_cfgspace_printf(walkp, print, val,
6297687d0d8SRobert Mustacchi 			    "0x%x -- %s\n", val, pcidb_vendor_name(vend));
6307687d0d8SRobert Mustacchi 		} else {
6317687d0d8SRobert Mustacchi 			pcieadm_cfgspace_printf(walkp, print, val,
6327687d0d8SRobert Mustacchi 			    "0x%x\n", val);
6337687d0d8SRobert Mustacchi 		}
6347687d0d8SRobert Mustacchi 	} else {
6357687d0d8SRobert Mustacchi 		pcidb_subvd_t *subvd;
6367687d0d8SRobert Mustacchi 		subvd = pcidb_lookup_subvd(walkp->pcw_pcieadm->pia_pcidb, vid,
6377687d0d8SRobert Mustacchi 		    did, svid, sdid);
6387687d0d8SRobert Mustacchi 		if (subvd != NULL) {
6397687d0d8SRobert Mustacchi 			pcieadm_cfgspace_printf(walkp, print, val,
6407687d0d8SRobert Mustacchi 			    "0x%x -- %s\n", val, pcidb_subvd_name(subvd));
6417687d0d8SRobert Mustacchi 		} else {
6427687d0d8SRobert Mustacchi 			pcieadm_cfgspace_printf(walkp, print, val, "0x%x\n",
6437687d0d8SRobert Mustacchi 			    val);
6447687d0d8SRobert Mustacchi 		}
6457687d0d8SRobert Mustacchi 	}
6467687d0d8SRobert Mustacchi }
6477687d0d8SRobert Mustacchi 
6487687d0d8SRobert Mustacchi /*
6497687d0d8SRobert Mustacchi  * The variable natures of BARs is a pain. This makes printing this out and the
6507687d0d8SRobert Mustacchi  * fields all a bit gross.
6517687d0d8SRobert Mustacchi  */
6527687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_bars(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)6537687d0d8SRobert Mustacchi pcieadm_cfgspace_print_bars(pcieadm_cfgspace_walk_t *walkp,
6547687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
6557687d0d8SRobert Mustacchi {
6567687d0d8SRobert Mustacchi 	uint32_t *barp = &walkp->pcw_data->pcb_u32[(walkp->pcw_capoff +
6577687d0d8SRobert Mustacchi 	    print->pcp_off) / 4];
6587687d0d8SRobert Mustacchi 	char barname[32];
6597687d0d8SRobert Mustacchi 	const char *typestrs[2] = { "Memory Space", "I/O Space" };
6607687d0d8SRobert Mustacchi 
6617687d0d8SRobert Mustacchi 	for (uint_t i = 0; i < print->pcp_len / 4; i++) {
6627687d0d8SRobert Mustacchi 		uint_t type;
6637687d0d8SRobert Mustacchi 		(void) snprintf(barname, sizeof (barname), "%s%u",
6647687d0d8SRobert Mustacchi 		    print->pcp_short, i);
6657687d0d8SRobert Mustacchi 
6667687d0d8SRobert Mustacchi 		type = barp[i] & PCI_BASE_SPACE_M;
6677687d0d8SRobert Mustacchi 
6687687d0d8SRobert Mustacchi 		if (pcieadm_cfgspace_filter(walkp, barname) &&
6697687d0d8SRobert Mustacchi 		    walkp->pcw_ofmt == NULL) {
6707687d0d8SRobert Mustacchi 			if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) !=
6717687d0d8SRobert Mustacchi 			    0) {
6727687d0d8SRobert Mustacchi 				pcieadm_print("%s %u (%s.%s)\n",
6737687d0d8SRobert Mustacchi 				    print->pcp_human, i,
6747687d0d8SRobert Mustacchi 				    walkp->pcw_filt->pstr_curgen, barname);
6757687d0d8SRobert Mustacchi 			} else {
6767687d0d8SRobert Mustacchi 				pcieadm_print("%s %u\n", print->pcp_human, i);
6777687d0d8SRobert Mustacchi 			}
6787687d0d8SRobert Mustacchi 		}
6797687d0d8SRobert Mustacchi 
6807687d0d8SRobert Mustacchi 		pcieadm_strfilt_push(walkp, barname);
6817687d0d8SRobert Mustacchi 		pcieadm_indent();
6827687d0d8SRobert Mustacchi 
6837687d0d8SRobert Mustacchi 		pcieadm_field_printf(walkp, "space", "Space", type,
6847687d0d8SRobert Mustacchi 		    "%s (0x%x)\n", typestrs[type], type);
6857687d0d8SRobert Mustacchi 
6867687d0d8SRobert Mustacchi 		if (type == PCI_BASE_SPACE_IO) {
6877687d0d8SRobert Mustacchi 			uint32_t addr = barp[i] & PCI_BASE_IO_ADDR_M;
6887687d0d8SRobert Mustacchi 
6897687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, "addr", "Address", addr,
6907687d0d8SRobert Mustacchi 			    "0x%" PRIx32 "\n", addr);
6917687d0d8SRobert Mustacchi 		} else {
6927687d0d8SRobert Mustacchi 			uint8_t type, pre;
6937687d0d8SRobert Mustacchi 			uint64_t addr;
6947687d0d8SRobert Mustacchi 			const char *locstr;
6957687d0d8SRobert Mustacchi 
6967687d0d8SRobert Mustacchi 			type = barp[i] & PCI_BASE_TYPE_M;
6977687d0d8SRobert Mustacchi 			pre = barp[i] & PCI_BASE_PREF_M;
6987687d0d8SRobert Mustacchi 			addr = barp[i] & PCI_BASE_M_ADDR_M;
6997687d0d8SRobert Mustacchi 
7007687d0d8SRobert Mustacchi 			if (type == PCI_BASE_TYPE_ALL) {
7017687d0d8SRobert Mustacchi 				addr += (uint64_t)barp[i+1] << 32;
7027687d0d8SRobert Mustacchi 				i++;
7037687d0d8SRobert Mustacchi 			}
7047687d0d8SRobert Mustacchi 
7057687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, "addr", "Address", addr,
7067687d0d8SRobert Mustacchi 			    "0x%" PRIx64 "\n", addr);
7077687d0d8SRobert Mustacchi 
7087687d0d8SRobert Mustacchi 			switch (type) {
7097687d0d8SRobert Mustacchi 			case PCI_BASE_TYPE_MEM:
7107687d0d8SRobert Mustacchi 				locstr = "32-bit";
7117687d0d8SRobert Mustacchi 				break;
7127687d0d8SRobert Mustacchi 			case PCI_BASE_TYPE_LOW:
7137687d0d8SRobert Mustacchi 				locstr = "Sub-1 MiB";
7147687d0d8SRobert Mustacchi 				break;
7157687d0d8SRobert Mustacchi 			case PCI_BASE_TYPE_ALL:
7167687d0d8SRobert Mustacchi 				locstr = "64-bit";
7177687d0d8SRobert Mustacchi 				break;
7187687d0d8SRobert Mustacchi 			case PCI_BASE_TYPE_RES:
7197687d0d8SRobert Mustacchi 			default:
7207687d0d8SRobert Mustacchi 				locstr = "Reserved";
7217687d0d8SRobert Mustacchi 				break;
7227687d0d8SRobert Mustacchi 			}
7237687d0d8SRobert Mustacchi 
7247687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, "addr", "Address", addr,
7257687d0d8SRobert Mustacchi 			    "%s (0x%x)\n", locstr, type >> 1);
7267687d0d8SRobert Mustacchi 			pcieadm_field_printf(walkp, "prefetch", "Prefetchable",
7277687d0d8SRobert Mustacchi 			    pre != 0, "%s (0x%x)\n", pre != 0 ? "yes" : "no",
7287687d0d8SRobert Mustacchi 			    pre != 0);
7297687d0d8SRobert Mustacchi 		}
7307687d0d8SRobert Mustacchi 
7317687d0d8SRobert Mustacchi 		pcieadm_deindent();
7327687d0d8SRobert Mustacchi 		pcieadm_strfilt_pop(walkp);
7337687d0d8SRobert Mustacchi 	}
7347687d0d8SRobert Mustacchi }
7357687d0d8SRobert Mustacchi 
7367687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_ecv(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)7377687d0d8SRobert Mustacchi pcieadm_cfgspace_print_ecv(pcieadm_cfgspace_walk_t *walkp,
7387687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
7397687d0d8SRobert Mustacchi {
7407687d0d8SRobert Mustacchi 	uint16_t bitlen, nwords;
7417687d0d8SRobert Mustacchi 
7427687d0d8SRobert Mustacchi 	if (BITX(walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 4], 5, 5) == 0) {
7437687d0d8SRobert Mustacchi 		return;
7447687d0d8SRobert Mustacchi 	}
7457687d0d8SRobert Mustacchi 
7467687d0d8SRobert Mustacchi 	bitlen = walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 5];
7477687d0d8SRobert Mustacchi 	if (bitlen == 0) {
7487687d0d8SRobert Mustacchi 		bitlen = 256;
7497687d0d8SRobert Mustacchi 	}
7507687d0d8SRobert Mustacchi 
7517687d0d8SRobert Mustacchi 	nwords = bitlen / 32;
7527687d0d8SRobert Mustacchi 	if ((bitlen % 8) != 0) {
7537687d0d8SRobert Mustacchi 		nwords++;
7547687d0d8SRobert Mustacchi 	}
7557687d0d8SRobert Mustacchi 
7567687d0d8SRobert Mustacchi 	for (uint16_t i = 0; i < nwords; i++) {
7577687d0d8SRobert Mustacchi 		char tshort[32], thuman[128];
7587687d0d8SRobert Mustacchi 		pcieadm_cfgspace_print_t p;
7597687d0d8SRobert Mustacchi 
7607687d0d8SRobert Mustacchi 		(void) snprintf(tshort, sizeof (tshort), "ecv%u", i);
7617687d0d8SRobert Mustacchi 		(void) snprintf(thuman, sizeof (thuman), "Egress Control "
7627687d0d8SRobert Mustacchi 		    "Vector %u", i);
7637687d0d8SRobert Mustacchi 		p.pcp_off = print->pcp_off + i * 4;
7647687d0d8SRobert Mustacchi 		p.pcp_len = 4;
7657687d0d8SRobert Mustacchi 		p.pcp_short = tshort;
7667687d0d8SRobert Mustacchi 		p.pcp_human = thuman;
7677687d0d8SRobert Mustacchi 		p.pcp_print = pcieadm_cfgspace_print_hex;
7687687d0d8SRobert Mustacchi 		p.pcp_arg = NULL;
7697687d0d8SRobert Mustacchi 
7707687d0d8SRobert Mustacchi 		p.pcp_print(walkp, &p, p.pcp_arg);
7717687d0d8SRobert Mustacchi 	}
7727687d0d8SRobert Mustacchi }
7737687d0d8SRobert Mustacchi 
7747687d0d8SRobert Mustacchi static void
pcieadm_cfgspace_print_dpa_paa(pcieadm_cfgspace_walk_t * walkp,pcieadm_cfgspace_print_t * print,void * arg)7757687d0d8SRobert Mustacchi pcieadm_cfgspace_print_dpa_paa(pcieadm_cfgspace_walk_t *walkp,
7767687d0d8SRobert Mustacchi     pcieadm_cfgspace_print_t *print, void *arg)
7777687d0d8SRobert Mustacchi {