xref: /illumos-gate/usr/src/cmd/pcieadm/pcieadm_bar.c (revision 415435b44529c2d47836fd3e301c66134ae17524)
1*415435b4SRobert Mustacchi /*
2*415435b4SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*415435b4SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*415435b4SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*415435b4SRobert Mustacchi  * 1.0 of the CDDL.
6*415435b4SRobert Mustacchi  *
7*415435b4SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*415435b4SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*415435b4SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*415435b4SRobert Mustacchi  */
11*415435b4SRobert Mustacchi 
12*415435b4SRobert Mustacchi /*
13*415435b4SRobert Mustacchi  * Copyright 2026 Oxide Computer Company
14*415435b4SRobert Mustacchi  */
15*415435b4SRobert Mustacchi 
16*415435b4SRobert Mustacchi /*
17*415435b4SRobert Mustacchi  * Implement logic related to listing, reading, and writing BARs.
18*415435b4SRobert Mustacchi  */
19*415435b4SRobert Mustacchi 
20*415435b4SRobert Mustacchi #include <err.h>
21*415435b4SRobert Mustacchi #include <stdio.h>
22*415435b4SRobert Mustacchi #include <sys/pci.h>
23*415435b4SRobert Mustacchi #include <ofmt.h>
24*415435b4SRobert Mustacchi #include <stdbool.h>
25*415435b4SRobert Mustacchi #include <string.h>
26*415435b4SRobert Mustacchi #include <stdlib.h>
27*415435b4SRobert Mustacchi 
28*415435b4SRobert Mustacchi #include "pcieadm.h"
29*415435b4SRobert Mustacchi 
30*415435b4SRobert Mustacchi static void
pcieadm_bar_list_usage(FILE * f)31*415435b4SRobert Mustacchi pcieadm_bar_list_usage(FILE *f)
32*415435b4SRobert Mustacchi {
33*415435b4SRobert Mustacchi 	(void) fprintf(f, "\tbar list\t[-H] [-o field,... [-p]] -d device "
34*415435b4SRobert Mustacchi 	    "[filter...]\n");
35*415435b4SRobert Mustacchi }
36*415435b4SRobert Mustacchi 
37*415435b4SRobert Mustacchi static void
pcieadm_bar_list_help(const char * fmt,...)38*415435b4SRobert Mustacchi pcieadm_bar_list_help(const char *fmt, ...)
39*415435b4SRobert Mustacchi {
40*415435b4SRobert Mustacchi 	if (fmt != NULL) {
41*415435b4SRobert Mustacchi 		va_list ap;
42*415435b4SRobert Mustacchi 
43*415435b4SRobert Mustacchi 		va_start(ap, fmt);
44*415435b4SRobert Mustacchi 		vwarnx(fmt, ap);
45*415435b4SRobert Mustacchi 		va_end(ap);
46*415435b4SRobert Mustacchi 		(void) fprintf(stderr, "\n");
47*415435b4SRobert Mustacchi 	}
48*415435b4SRobert Mustacchi 
49*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  %s bar list [-H] [-o field,... [-p]] "
50*415435b4SRobert Mustacchi 	    "-d device [filter,...]\n", pcieadm_progname);
51*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "List BARs specific to a single device.\n\n"
52*415435b4SRobert Mustacchi 	    "\t-d device\tlist BARs from the specified device (driver instance,"
53*415435b4SRobert Mustacchi 	    "\n\t\t\t/devices path, or b/d/f)\n"
54*415435b4SRobert Mustacchi 	    "\t-H\t\tomit the column header\n"
55*415435b4SRobert Mustacchi 	    "\t-o field\toutput fields to print (required for -p)\n"
56*415435b4SRobert Mustacchi 	    "\t-p\t\tparsable output (requires -o)\n\n");
57*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "The following fields are supported:\n"
58*415435b4SRobert Mustacchi 	    "\taddress\t\tthe address programmed in the BAR\n"
59*415435b4SRobert Mustacchi 	    "\tbar\t\tthe bar's numeric identifier\n"
60*415435b4SRobert Mustacchi 	    "\tdesc\t\ta human description of the BAR\n"
61*415435b4SRobert Mustacchi 	    "\tmtype\t\tthe memory type of the BAR\n"
62*415435b4SRobert Mustacchi 	    "\tprefetech\tindicates whether or not the BAR is pre-fetchable\n"
63*415435b4SRobert Mustacchi 	    "\traw\t\tthe raw contents of the hardware BAR register\n"
64*415435b4SRobert Mustacchi 	    "\tsize\t\tthe size of the bar\n"
65*415435b4SRobert Mustacchi 	    "\tspace\t\tthe type of space the BAR represents\n"
66*415435b4SRobert Mustacchi 	    "\twidth\t\tindicates the width of the BAR in bytes\n");
67*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "The following filters are supported:\n"
68*415435b4SRobert Mustacchi 	    "\t<index>\t\tthe BAR matches the specified index\n"
69*415435b4SRobert Mustacchi 	    "\tio\t\tthe BAR is an I/O BAR\n"
70*415435b4SRobert Mustacchi 	    "\tmem\t\tthe BAR is a memory BAR\n"
71*415435b4SRobert Mustacchi 	    "\tmem32\t\tthe BAR is a 32-bit memory BAR\n"
72*415435b4SRobert Mustacchi 	    "\tmem64\t\tthe BAR is a 64-bit memory BAR\n"
73*415435b4SRobert Mustacchi 	    "\tprefetch\tthe BAR is prefetchable\n");
74*415435b4SRobert Mustacchi }
75*415435b4SRobert Mustacchi 
76*415435b4SRobert Mustacchi typedef enum pcieadm_bar_list_otype {
77*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_BAR,
78*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_ADDRESS,
79*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_DESC,
80*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_MTYPE,
81*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_SIZE,
82*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_SPACE,
83*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_PREFETCH,
84*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_WIDTH,
85*415435b4SRobert Mustacchi 	PCIEADM_BAR_LIST_RAW
86*415435b4SRobert Mustacchi } pcieadm_bar_list_otpye_t;
87*415435b4SRobert Mustacchi 
88*415435b4SRobert Mustacchi typedef struct pcieadm_bar_list_ofmt {
89*415435b4SRobert Mustacchi 	uint8_t pblo_idx;
90*415435b4SRobert Mustacchi 	uint8_t pblo_width;
91*415435b4SRobert Mustacchi 	bool pblo_mem;
92*415435b4SRobert Mustacchi 	bool pblo_prefetch;
93*415435b4SRobert Mustacchi 	uint64_t pblo_addr;
94*415435b4SRobert Mustacchi 	uint64_t pblo_size;
95*415435b4SRobert Mustacchi 	uint64_t pblo_raw;
96*415435b4SRobert Mustacchi 	const char *pblo_mtype;
97*415435b4SRobert Mustacchi } pcieadm_bar_list_ofmt_t;
98*415435b4SRobert Mustacchi 
99*415435b4SRobert Mustacchi static boolean_t
pcieadm_bar_list_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)100*415435b4SRobert Mustacchi pcieadm_bar_list_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
101*415435b4SRobert Mustacchi {
102*415435b4SRobert Mustacchi 	size_t ret;
103*415435b4SRobert Mustacchi 	const pcieadm_bar_list_ofmt_t *pblo = ofarg->ofmt_cbarg;
104*415435b4SRobert Mustacchi 
105*415435b4SRobert Mustacchi 	switch (ofarg->ofmt_id) {
106*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_BAR:
107*415435b4SRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", pblo->pblo_idx);
108*415435b4SRobert Mustacchi 		break;
109*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_ADDRESS:
110*415435b4SRobert Mustacchi 		ret = snprintf(buf, buflen, "0x%" PRIx64, pblo->pblo_addr);
111*415435b4SRobert Mustacchi 		break;
112*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_DESC:
113*415435b4SRobert Mustacchi 		if (pblo->pblo_mem) {
114*415435b4SRobert Mustacchi 			ret = snprintf(buf, buflen, "%s%s Memory",
115*415435b4SRobert Mustacchi 			    pblo->pblo_mtype, pblo->pblo_prefetch ?
116*415435b4SRobert Mustacchi 			    " Prefetchable" : "");
117*415435b4SRobert Mustacchi 		} else {
118*415435b4SRobert Mustacchi 			ret = strlcat(buf, "I/O", buflen);
119*415435b4SRobert Mustacchi 		}
120*415435b4SRobert Mustacchi 		break;
121*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_MTYPE:
122*415435b4SRobert Mustacchi 		ret = strlcat(buf, pblo->pblo_mtype, buflen);
123*415435b4SRobert Mustacchi 		break;
124*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_SIZE:
125*415435b4SRobert Mustacchi 		ret = snprintf(buf, buflen, "0x%" PRIx64, pblo->pblo_size);
126*415435b4SRobert Mustacchi 		break;
127*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_SPACE:
128*415435b4SRobert Mustacchi 		ret = strlcat(buf, pblo->pblo_mem ? "Memory" : "I/O", buflen);
129*415435b4SRobert Mustacchi 		break;
130*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_PREFETCH:
131*415435b4SRobert Mustacchi 		ret = strlcat(buf, pblo->pblo_prefetch ? "yes" : "no", buflen);
132*415435b4SRobert Mustacchi 		break;
133*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_WIDTH:
134*415435b4SRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", pblo->pblo_width);
135*415435b4SRobert Mustacchi 		break;
136*415435b4SRobert Mustacchi 	case PCIEADM_BAR_LIST_RAW:
137*415435b4SRobert Mustacchi 		ret = snprintf(buf, buflen, "0x%" PRIx64, pblo->pblo_raw);
138*415435b4SRobert Mustacchi 		break;
139*415435b4SRobert Mustacchi 	default:
140*415435b4SRobert Mustacchi 		return (B_FALSE);
141*415435b4SRobert Mustacchi 	}
142*415435b4SRobert Mustacchi 
143*415435b4SRobert Mustacchi 	return (buflen > ret);
144*415435b4SRobert Mustacchi }
145*415435b4SRobert Mustacchi 
146*415435b4SRobert Mustacchi static const char *pcieadm_bar_list_fields = "bar,size,address,desc";
147*415435b4SRobert Mustacchi static const ofmt_field_t pcieadm_bar_list_ofmt[] = {
148*415435b4SRobert Mustacchi 	{ "BAR", 8, PCIEADM_BAR_LIST_BAR, pcieadm_bar_list_ofmt_cb },
149*415435b4SRobert Mustacchi 	{ "ADDRESS", 16, PCIEADM_BAR_LIST_ADDRESS, pcieadm_bar_list_ofmt_cb },
150*415435b4SRobert Mustacchi 	{ "DESC", 32, PCIEADM_BAR_LIST_DESC, pcieadm_bar_list_ofmt_cb },
151*415435b4SRobert Mustacchi 	{ "MTYPE", 8, PCIEADM_BAR_LIST_MTYPE, pcieadm_bar_list_ofmt_cb },
152*415435b4SRobert Mustacchi 	{ "SIZE", 12, PCIEADM_BAR_LIST_SIZE, pcieadm_bar_list_ofmt_cb },
153*415435b4SRobert Mustacchi 	{ "SPACE", 10, PCIEADM_BAR_LIST_SPACE, pcieadm_bar_list_ofmt_cb },
154*415435b4SRobert Mustacchi 	{ "PREFETCH", 4, PCIEADM_BAR_LIST_PREFETCH, pcieadm_bar_list_ofmt_cb },
155*415435b4SRobert Mustacchi 	{ "WIDTH", 6, PCIEADM_BAR_LIST_WIDTH, pcieadm_bar_list_ofmt_cb },
156*415435b4SRobert Mustacchi 	{ "RAW", 16, PCIEADM_BAR_LIST_RAW, pcieadm_bar_list_ofmt_cb },
157*415435b4SRobert Mustacchi };
158*415435b4SRobert Mustacchi 
159*415435b4SRobert Mustacchi static bool
pcieadm_show_bar_match(const pcieadm_bar_list_ofmt_t * pblo,int nfilts,char ** filts,bool * used)160*415435b4SRobert Mustacchi pcieadm_show_bar_match(const pcieadm_bar_list_ofmt_t *pblo, int nfilts,
161*415435b4SRobert Mustacchi     char **filts, bool *used)
162*415435b4SRobert Mustacchi {
163*415435b4SRobert Mustacchi 	bool match = false;
164*415435b4SRobert Mustacchi 
165*415435b4SRobert Mustacchi 	if (nfilts <= 0) {
166*415435b4SRobert Mustacchi 		return (true);
167*415435b4SRobert Mustacchi 	}
168*415435b4SRobert Mustacchi 
169*415435b4SRobert Mustacchi 	for (int i = 0; i < nfilts; i++) {
170*415435b4SRobert Mustacchi 		if (strcmp(filts[i], "io") == 0 && !pblo->pblo_mem) {
171*415435b4SRobert Mustacchi 			used[i] = true;
172*415435b4SRobert Mustacchi 			match = true;
173*415435b4SRobert Mustacchi 			continue;
174*415435b4SRobert Mustacchi 		}
175*415435b4SRobert Mustacchi 
176*415435b4SRobert Mustacchi 		if (strcmp(filts[i], "mem32") == 0 &&
177*415435b4SRobert Mustacchi 		    strcmp(pblo->pblo_mtype, "32-bit") == 0) {
178*415435b4SRobert Mustacchi 			used[i] = true;
179*415435b4SRobert Mustacchi 			match = true;
180*415435b4SRobert Mustacchi 			continue;
181*415435b4SRobert Mustacchi 		}
182*415435b4SRobert Mustacchi 
183*415435b4SRobert Mustacchi 		if (strcmp(filts[i], "mem64") == 0 &&
184*415435b4SRobert Mustacchi 		    strcmp(pblo->pblo_mtype, "64-bit") == 0) {
185*415435b4SRobert Mustacchi 			used[i] = true;
186*415435b4SRobert Mustacchi 			match = true;
187*415435b4SRobert Mustacchi 			continue;
188*415435b4SRobert Mustacchi 		}
189*415435b4SRobert Mustacchi 
190*415435b4SRobert Mustacchi 		if (strcmp(filts[i], "mem") == 0 && pblo->pblo_mem) {
191*415435b4SRobert Mustacchi 			used[i] = true;
192*415435b4SRobert Mustacchi 			match = true;
193*415435b4SRobert Mustacchi 			continue;
194*415435b4SRobert Mustacchi 		}
195*415435b4SRobert Mustacchi 
196*415435b4SRobert Mustacchi 		if (strcmp(filts[i], "prefetch") == 0 && pblo->pblo_prefetch) {
197*415435b4SRobert Mustacchi 			used[i] = true;
198*415435b4SRobert Mustacchi 			match = true;
199*415435b4SRobert Mustacchi 			continue;
200*415435b4SRobert Mustacchi 		}
201*415435b4SRobert Mustacchi 
202*415435b4SRobert Mustacchi 		/*
203*415435b4SRobert Mustacchi 		 * Attempt to parse anything left as an integer indicating a BAR
204*415435b4SRobert Mustacchi 		 * index.
205*415435b4SRobert Mustacchi 		 */
206*415435b4SRobert Mustacchi 		const char *errstr;
207*415435b4SRobert Mustacchi 		long long l = strtonumx(filts[i], 0, UINT32_MAX, &errstr, 0);
208*415435b4SRobert Mustacchi 		if (errstr == NULL && l == pblo->pblo_idx) {
209*415435b4SRobert Mustacchi 			used[i] = true;
210*415435b4SRobert Mustacchi 			match = true;
211*415435b4SRobert Mustacchi 			continue;
212*415435b4SRobert Mustacchi 		}
213*415435b4SRobert Mustacchi 	}
214*415435b4SRobert Mustacchi 
215*415435b4SRobert Mustacchi 	return (match);
216*415435b4SRobert Mustacchi }
217*415435b4SRobert Mustacchi 
218*415435b4SRobert Mustacchi /*
219*415435b4SRobert Mustacchi  * Read information about BARs. There are basically two different sources that
220*415435b4SRobert Mustacchi  * we want to combine information from: reg[] and the device itself. We prefer
221*415435b4SRobert Mustacchi  * reg[] over assigned-addresses[] because the latter may not exist if we have
222*415435b4SRobert Mustacchi  * some kind of resource error.
223*415435b4SRobert Mustacchi  *
224*415435b4SRobert Mustacchi  * We prefer to walk the device itself and then augment it with size information
225*415435b4SRobert Mustacchi  * from reg[] as reg[] will skip unimplemented BARs and we want to be able to
226*415435b4SRobert Mustacchi  * accurately indicate the type and other information of a BAR with zero size.
227*415435b4SRobert Mustacchi  */
228*415435b4SRobert Mustacchi static int
pcieadm_bar_list(pcieadm_t * pcip,int argc,char * argv[])229*415435b4SRobert Mustacchi pcieadm_bar_list(pcieadm_t *pcip, int argc, char *argv[])
230*415435b4SRobert Mustacchi {
231*415435b4SRobert Mustacchi 	int c, ret = EXIT_SUCCESS;
232*415435b4SRobert Mustacchi 	const char *device = NULL;
233*415435b4SRobert Mustacchi 	const pcieadm_ops_t *ops;
234*415435b4SRobert Mustacchi 	void *readarg;
235*415435b4SRobert Mustacchi 	uint8_t hdr, nbar;
236*415435b4SRobert Mustacchi 	uint_t flags = 0;
237*415435b4SRobert Mustacchi 	bool parse = false, *filts = NULL, found = false;
238*415435b4SRobert Mustacchi 	const char *fields = NULL;
239*415435b4SRobert Mustacchi 	ofmt_status_t oferr;
240*415435b4SRobert Mustacchi 	ofmt_handle_t ofmt;
241*415435b4SRobert Mustacchi 
242*415435b4SRobert Mustacchi 	while ((c = getopt(argc, argv, ":d:Ho:p")) != -1) {
243*415435b4SRobert Mustacchi 		switch (c) {
244*415435b4SRobert Mustacchi 		case 'd':
245*415435b4SRobert Mustacchi 			device = optarg;
246*415435b4SRobert Mustacchi 			break;
247*415435b4SRobert Mustacchi 		case 'H':
248*415435b4SRobert Mustacchi 			flags |= OFMT_NOHEADER;
249*415435b4SRobert Mustacchi 			break;
250*415435b4SRobert Mustacchi 		case 'o':
251*415435b4SRobert Mustacchi 			fields = optarg;
252*415435b4SRobert Mustacchi 			break;
253*415435b4SRobert Mustacchi 		case 'p':
254*415435b4SRobert Mustacchi 			parse = true;
255*415435b4SRobert Mustacchi 			flags |= OFMT_PARSABLE;
256*415435b4SRobert Mustacchi 			break;
257*415435b4SRobert Mustacchi 		case ':':
258*415435b4SRobert Mustacchi 			pcieadm_bar_list_help("Option -%c requires an "
259*415435b4SRobert Mustacchi 			    "argument", optopt);
260*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
261*415435b4SRobert Mustacchi 		case '?':
262*415435b4SRobert Mustacchi 		default:
263*415435b4SRobert Mustacchi 			pcieadm_bar_list_help("unknown option: -%c",
264*415435b4SRobert Mustacchi 			    optopt);
265*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
266*415435b4SRobert Mustacchi 		}
267*415435b4SRobert Mustacchi 	}
268*415435b4SRobert Mustacchi 
269*415435b4SRobert Mustacchi 	if (device == NULL) {
270*415435b4SRobert Mustacchi 		pcieadm_bar_list_help("missing required device argument (-d)");
271*415435b4SRobert Mustacchi 		exit(EXIT_USAGE);
272*415435b4SRobert Mustacchi 	}
273*415435b4SRobert Mustacchi 
274*415435b4SRobert Mustacchi 	if (parse && fields == NULL) {
275*415435b4SRobert Mustacchi 		errx(EXIT_USAGE, "-p requires fields specified with -o");
276*415435b4SRobert Mustacchi 	}
277*415435b4SRobert Mustacchi 
278*415435b4SRobert Mustacchi 	if (fields == NULL) {
279*415435b4SRobert Mustacchi 		fields = pcieadm_bar_list_fields;
280*415435b4SRobert Mustacchi 	}
281*415435b4SRobert Mustacchi 
282*415435b4SRobert Mustacchi 	argc -= optind;
283*415435b4SRobert Mustacchi 	argv += optind;
284*415435b4SRobert Mustacchi 
285*415435b4SRobert Mustacchi 	if (argc > 0) {
286*415435b4SRobert Mustacchi 		filts = calloc(argc, sizeof (bool));
287*415435b4SRobert Mustacchi 		if (filts == NULL) {
288*415435b4SRobert Mustacchi 			err(EXIT_FAILURE, "failed to allocate filter tracking "
289*415435b4SRobert Mustacchi 			    "memory");
290*415435b4SRobert Mustacchi 		}
291*415435b4SRobert Mustacchi 	}
292*415435b4SRobert Mustacchi 
293*415435b4SRobert Mustacchi 	oferr = ofmt_open(fields, pcieadm_bar_list_ofmt, flags, 0, &ofmt);
294*415435b4SRobert Mustacchi 	ofmt_check(oferr, parse, ofmt, pcieadm_ofmt_errx, warnx);
295*415435b4SRobert Mustacchi 
296*415435b4SRobert Mustacchi 	/*
297*415435b4SRobert Mustacchi 	 * We will need full privileges to get information from the device.
298*415435b4SRobert Mustacchi 	 */
299*415435b4SRobert Mustacchi 	priv_fillset(pcip->pia_priv_eff);
300*415435b4SRobert Mustacchi 
301*415435b4SRobert Mustacchi 	pcieadm_find_dip(pcip, device);
302*415435b4SRobert Mustacchi 	int *reg;
303*415435b4SRobert Mustacchi 	int nreg = di_prop_lookup_ints(DDI_DEV_T_ANY, pcip->pia_devi,
304*415435b4SRobert Mustacchi 	    "reg", &reg);
305*415435b4SRobert Mustacchi 	if (nreg < 0 && errno != ENXIO) {
306*415435b4SRobert Mustacchi 		err(EXIT_FAILURE, "failed to look up reg[] property");
307*415435b4SRobert Mustacchi 	} else if (nreg < 0) {
308*415435b4SRobert Mustacchi 		nreg = 0;
309*415435b4SRobert Mustacchi 	} else if (nreg % 5 != 0) {
310*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "reg[] property has wrong shape, found %d "
311*415435b4SRobert Mustacchi 		    "integers but expected a multiple of 5", nreg);
312*415435b4SRobert Mustacchi 	}
313*415435b4SRobert Mustacchi 	nreg /= 5;
314*415435b4SRobert Mustacchi 
315*415435b4SRobert Mustacchi 	pcieadm_init_ops_kernel(pcip, &ops, &readarg);
316*415435b4SRobert Mustacchi 	if (!ops->pop_cfg(PCI_CONF_HEADER, sizeof (hdr), &hdr, readarg)) {
317*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to read offset %u from device "
318*415435b4SRobert Mustacchi 		    "configuration space", PCI_CONF_HEADER);
319*415435b4SRobert Mustacchi 	}
320*415435b4SRobert Mustacchi 
321*415435b4SRobert Mustacchi 	switch (hdr & PCI_HEADER_TYPE_M) {
322*415435b4SRobert Mustacchi 	case PCI_HEADER_ZERO:
323*415435b4SRobert Mustacchi 		nbar = PCI_BASE_NUM;
324*415435b4SRobert Mustacchi 		break;
325*415435b4SRobert Mustacchi 	case PCI_HEADER_ONE:
326*415435b4SRobert Mustacchi 		nbar = PCI_BCNF_BASE_NUM;
327*415435b4SRobert Mustacchi 		break;
328*415435b4SRobert Mustacchi 	case PCI_HEADER_TWO:
329*415435b4SRobert Mustacchi 	default:
330*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "unsupported PCI header type: 0x%x",
331*415435b4SRobert Mustacchi 		    hdr & PCI_HEADER_TYPE_M);
332*415435b4SRobert Mustacchi 	}
333*415435b4SRobert Mustacchi 
334*415435b4SRobert Mustacchi 	/*
335*415435b4SRobert Mustacchi 	 * First do a pass where we read all of the raw BAR registers from the
336*415435b4SRobert Mustacchi 	 * device.
337*415435b4SRobert Mustacchi 	 */
338*415435b4SRobert Mustacchi 	uint32_t raw[PCI_BASE_NUM];
339*415435b4SRobert Mustacchi 	for (uint8_t i = 0; i < nbar; i++) {
340*415435b4SRobert Mustacchi 		if (!ops->pop_cfg(PCI_CONF_BASE0 + i * 4, sizeof (uint32_t),
341*415435b4SRobert Mustacchi 		    &raw[i], readarg)) {
342*415435b4SRobert Mustacchi 			errx(EXIT_FAILURE, "failed to read BAR data form "
343*415435b4SRobert Mustacchi 			    "device at offset 0x%x", PCI_CONF_BASE0 + i * 4);
344*415435b4SRobert Mustacchi 		}
345*415435b4SRobert Mustacchi 	}
346*415435b4SRobert Mustacchi 
347*415435b4SRobert Mustacchi 	/*
348*415435b4SRobert Mustacchi 	 * Go through and process each BAR entry. Determine where we have a
349*415435b4SRobert Mustacchi 	 * 64-bit BAR and therefore need to account for two entries here. This
350*415435b4SRobert Mustacchi 	 * is also where we try to marry things up to assigned-addresses to
351*415435b4SRobert Mustacchi 	 * determine the size.
352*415435b4SRobert Mustacchi 	 */
353*415435b4SRobert Mustacchi 	for (uint8_t i = 0; i < nbar; i++) {
354*415435b4SRobert Mustacchi 		pcieadm_bar_list_ofmt_t arg;
355*415435b4SRobert Mustacchi 
356*415435b4SRobert Mustacchi 		(void) memset(&arg, 0, sizeof (arg));
357*415435b4SRobert Mustacchi 		arg.pblo_idx = i;
358*415435b4SRobert Mustacchi 		arg.pblo_width = PCI_BAR_SZ_32;
359*415435b4SRobert Mustacchi 		arg.pblo_raw = raw[i];
360*415435b4SRobert Mustacchi 		arg.pblo_prefetch = false;
361*415435b4SRobert Mustacchi 
362*415435b4SRobert Mustacchi 		if ((raw[i] & PCI_BASE_SPACE_M) == PCI_BASE_SPACE_IO) {
363*415435b4SRobert Mustacchi 			arg.pblo_mem = false;
364*415435b4SRobert Mustacchi 			arg.pblo_addr = raw[i] & PCI_BASE_IO_ADDR_M;
365*415435b4SRobert Mustacchi 			arg.pblo_mtype = "--";
366*415435b4SRobert Mustacchi 		} else {
367*415435b4SRobert Mustacchi 			arg.pblo_mem = true;
368*415435b4SRobert Mustacchi 			arg.pblo_addr = raw[i] & PCI_BASE_M_ADDR_M;
369*415435b4SRobert Mustacchi 
370*415435b4SRobert Mustacchi 			if ((raw[i] & PCI_BASE_TYPE_M) == PCI_BASE_TYPE_ALL) {
371*415435b4SRobert Mustacchi 				i++;
372*415435b4SRobert Mustacchi 				if (i == nbar) {
373*415435b4SRobert Mustacchi 					warnx("device %s has corrupt BAR %u: "
374*415435b4SRobert Mustacchi 					    "no additional BARs exist for "
375*415435b4SRobert Mustacchi 					    "upper 32-bit data", device,
376*415435b4SRobert Mustacchi 					    arg.pblo_idx);
377*415435b4SRobert Mustacchi 					ret = EXIT_FAILURE;
378*415435b4SRobert Mustacchi 					continue;
379*415435b4SRobert Mustacchi 				}
380*415435b4SRobert Mustacchi 				arg.pblo_width = PCI_BAR_SZ_64;
381*415435b4SRobert Mustacchi 				arg.pblo_raw |= (uint64_t)raw[i] << 32;
382*415435b4SRobert Mustacchi 				arg.pblo_addr |= (uint64_t)raw[i] << 32;
383*415435b4SRobert Mustacchi 			}
384*415435b4SRobert Mustacchi 
385*415435b4SRobert Mustacchi 			if ((arg.pblo_raw & PCI_BASE_PREF_M) != 0) {
386*415435b4SRobert Mustacchi 				arg.pblo_prefetch = true;
387*415435b4SRobert Mustacchi 			}
388*415435b4SRobert Mustacchi 
389*415435b4SRobert Mustacchi 			switch (arg.pblo_raw & PCI_BASE_TYPE_M) {
390*415435b4SRobert Mustacchi 			case PCI_BASE_TYPE_MEM:
391*415435b4SRobert Mustacchi 				arg.pblo_mtype = "32-bit";
392*415435b4SRobert Mustacchi 				break;
393*415435b4SRobert Mustacchi 			case PCI_BASE_TYPE_LOW:
394*415435b4SRobert Mustacchi 				arg.pblo_mtype = "1M";
395*415435b4SRobert Mustacchi 				break;
396*415435b4SRobert Mustacchi 			case PCI_BASE_TYPE_ALL:
397*415435b4SRobert Mustacchi 				arg.pblo_mtype = "64-bit";
398*415435b4SRobert Mustacchi 				break;
399*415435b4SRobert Mustacchi 			case PCI_BASE_TYPE_RES:
400*415435b4SRobert Mustacchi 				arg.pblo_mtype = "reserved";
401*415435b4SRobert Mustacchi 				break;
402*415435b4SRobert Mustacchi 			}
403*415435b4SRobert Mustacchi 		}
404*415435b4SRobert Mustacchi 
405*415435b4SRobert Mustacchi 		/*
406*415435b4SRobert Mustacchi 		 * Finally, before we call back, determine what the size of this
407*415435b4SRobert Mustacchi 		 * BAR is. We walk reg[] to try to find a matching register
408*415435b4SRobert Mustacchi 		 * index, which is considered the offset into configuration
409*415435b4SRobert Mustacchi 		 * space of the start of this BAR.
410*415435b4SRobert Mustacchi 		 *
411*415435b4SRobert Mustacchi 		 * If we can't find a match, then it likely is because the BAR
412*415435b4SRobert Mustacchi 		 * has no size. Using reg[] versus assigned-addresses[] gives us
413*415435b4SRobert Mustacchi 		 * a reasonable confidence here, though this doesn't account for
414*415435b4SRobert Mustacchi 		 * resizable BAR support.
415*415435b4SRobert Mustacchi 		 */
416*415435b4SRobert Mustacchi 		arg.pblo_size = 0;
417*415435b4SRobert Mustacchi 		uint32_t targ = PCI_CONF_BASE0 + arg.pblo_idx *
418*415435b4SRobert Mustacchi 		    sizeof (uint32_t);
419*415435b4SRobert Mustacchi 		const pci_regspec_t *rsp = (pci_regspec_t *)reg;
420*415435b4SRobert Mustacchi 		for (int ridx = 0; ridx < nreg; ridx++, rsp++) {
421*415435b4SRobert Mustacchi 			uint32_t check = PCI_REG_REG_G(rsp->pci_phys_hi);
422*415435b4SRobert Mustacchi 
423*415435b4SRobert Mustacchi 			if (targ == check) {
424*415435b4SRobert Mustacchi 				arg.pblo_size = (uint64_t)rsp->pci_size_hi <<
425*415435b4SRobert Mustacchi 				    32;
426*415435b4SRobert Mustacchi 				arg.pblo_size |= rsp->pci_size_low;
427*415435b4SRobert Mustacchi 				break;
428*415435b4SRobert Mustacchi 			}
429*415435b4SRobert Mustacchi 		}
430*415435b4SRobert Mustacchi 
431*415435b4SRobert Mustacchi 		if (pcieadm_show_bar_match(&arg, argc, argv, filts)) {
432*415435b4SRobert Mustacchi 			found = true;
433*415435b4SRobert Mustacchi 			ofmt_print(ofmt, &arg);
434*415435b4SRobert Mustacchi 		}
435*415435b4SRobert Mustacchi 	}
436*415435b4SRobert Mustacchi 
437*415435b4SRobert Mustacchi 	for (int i = 0; i < argc; i++) {
438*415435b4SRobert Mustacchi 		if (!filts[i]) {
439*415435b4SRobert Mustacchi 			warnx("filter '%s' did not match any BARs", argv[i]);
440*415435b4SRobert Mustacchi 			ret = EXIT_FAILURE;
441*415435b4SRobert Mustacchi 		}
442*415435b4SRobert Mustacchi 	}
443*415435b4SRobert Mustacchi 
444*415435b4SRobert Mustacchi 	if (!found) {
445*415435b4SRobert Mustacchi 		ret = EXIT_FAILURE;
446*415435b4SRobert Mustacchi 	}
447*415435b4SRobert Mustacchi 
448*415435b4SRobert Mustacchi 	free(filts);
449*415435b4SRobert Mustacchi 	pcieadm_fini_ops_kernel(readarg);
450*415435b4SRobert Mustacchi 	ofmt_close(ofmt);
451*415435b4SRobert Mustacchi 
452*415435b4SRobert Mustacchi 	return (ret);
453*415435b4SRobert Mustacchi }
454*415435b4SRobert Mustacchi 
455*415435b4SRobert Mustacchi static void
pcieadm_bar_read_usage(FILE * f)456*415435b4SRobert Mustacchi pcieadm_bar_read_usage(FILE *f)
457*415435b4SRobert Mustacchi {
458*415435b4SRobert Mustacchi 	(void) fprintf(f, "\tbar read\t[-l length] -d device -b bar reg\n");
459*415435b4SRobert Mustacchi }
460*415435b4SRobert Mustacchi 
461*415435b4SRobert Mustacchi static void
pcieadm_bar_read_help(const char * fmt,...)462*415435b4SRobert Mustacchi pcieadm_bar_read_help(const char *fmt, ...)
463*415435b4SRobert Mustacchi {
464*415435b4SRobert Mustacchi 	if (fmt != NULL) {
465*415435b4SRobert Mustacchi 		va_list ap;
466*415435b4SRobert Mustacchi 
467*415435b4SRobert Mustacchi 		va_start(ap, fmt);
468*415435b4SRobert Mustacchi 		vwarnx(fmt, ap);
469*415435b4SRobert Mustacchi 		va_end(ap);
470*415435b4SRobert Mustacchi 		(void) fprintf(stderr, "\n");
471*415435b4SRobert Mustacchi 	}
472*415435b4SRobert Mustacchi 
473*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  %s bar read [-l length] -d device "
474*415435b4SRobert Mustacchi 	    "-b bar reg\n", pcieadm_progname);
475*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Read data at offset <reg> from a device "
476*415435b4SRobert Mustacchi 	    "BAR.\n\n"
477*415435b4SRobert Mustacchi 	    "\t-b bar\t\tthe index of the BAR to read from\n"
478*415435b4SRobert Mustacchi 	    "\t-d device\tread BAR from the specified device (driver instance,"
479*415435b4SRobert Mustacchi 	    "\n\t\t\t/devices path, or b/d/f)\n"
480*415435b4SRobert Mustacchi 	    "\t-l length\tspecify the number of bytes to read: 1, 2, 4 "
481*415435b4SRobert Mustacchi 	    "(default),\n\t\t\tor 8\n");
482*415435b4SRobert Mustacchi }
483*415435b4SRobert Mustacchi 
484*415435b4SRobert Mustacchi /*
485*415435b4SRobert Mustacchi  * We can't use strtonumx here as its maximum values are all based in signed
486*415435b4SRobert Mustacchi  * integers.
487*415435b4SRobert Mustacchi  */
488*415435b4SRobert Mustacchi static uint64_t
pcieadm_bar_parse_u64(const char * reg,const char * desc)489*415435b4SRobert Mustacchi pcieadm_bar_parse_u64(const char *reg, const char *desc)
490*415435b4SRobert Mustacchi {
491*415435b4SRobert Mustacchi 	char *eptr;
492*415435b4SRobert Mustacchi 	unsigned long long ull;
493*415435b4SRobert Mustacchi 
494*415435b4SRobert Mustacchi 	errno = 0;
495*415435b4SRobert Mustacchi 	ull = strtoull(reg, &eptr, 0);
496*415435b4SRobert Mustacchi 	if (errno != 0 || *eptr != '\0') {
497*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to parse %s %s", desc, reg);
498*415435b4SRobert Mustacchi 	}
499*415435b4SRobert Mustacchi 
500*415435b4SRobert Mustacchi 	return ((uint64_t)ull);
501*415435b4SRobert Mustacchi }
502*415435b4SRobert Mustacchi 
503*415435b4SRobert Mustacchi static uint8_t
pcieadm_bar_parse_len(const char * len)504*415435b4SRobert Mustacchi pcieadm_bar_parse_len(const char *len)
505*415435b4SRobert Mustacchi {
506*415435b4SRobert Mustacchi 	char *eptr;
507*415435b4SRobert Mustacchi 	unsigned long ul;
508*415435b4SRobert Mustacchi 
509*415435b4SRobert Mustacchi 	errno = 0;
510*415435b4SRobert Mustacchi 	ul = strtoul(len, &eptr, 0);
511*415435b4SRobert Mustacchi 	if (errno != 0 || *eptr != '\0') {
512*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to parse length %s", len);
513*415435b4SRobert Mustacchi 	}
514*415435b4SRobert Mustacchi 
515*415435b4SRobert Mustacchi 	if (ul != 1 && ul != 2 && ul != 4 && ul != 8) {
516*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "invalid byte length 0x%lx: only 1, 2, 4, "
517*415435b4SRobert Mustacchi 		    "and 8 byte I/Os are supported", ul);
518*415435b4SRobert Mustacchi 	}
519*415435b4SRobert Mustacchi 
520*415435b4SRobert Mustacchi 	return ((uint8_t)ul);
521*415435b4SRobert Mustacchi }
522*415435b4SRobert Mustacchi 
523*415435b4SRobert Mustacchi static uint8_t
pcieadm_bar_parse_bar(const char * bar)524*415435b4SRobert Mustacchi pcieadm_bar_parse_bar(const char *bar)
525*415435b4SRobert Mustacchi {
526*415435b4SRobert Mustacchi 	uint8_t val;
527*415435b4SRobert Mustacchi 	const char *errstr;
528*415435b4SRobert Mustacchi 	val = (uint8_t)strtonumx(bar, 0, PCI_BASE_NUM - 1, &errstr, 0);
529*415435b4SRobert Mustacchi 	if (errstr != NULL) {
530*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to parse BAR %s: value is %s", bar,
531*415435b4SRobert Mustacchi 		    errstr);
532*415435b4SRobert Mustacchi 	}
533*415435b4SRobert Mustacchi 
534*415435b4SRobert Mustacchi 	return (val);
535*415435b4SRobert Mustacchi }
536*415435b4SRobert Mustacchi 
537*415435b4SRobert Mustacchi static int
pcieadm_bar_read(pcieadm_t * pcip,int argc,char * argv[])538*415435b4SRobert Mustacchi pcieadm_bar_read(pcieadm_t *pcip, int argc, char *argv[])
539*415435b4SRobert Mustacchi {
540*415435b4SRobert Mustacchi 	int c, ret = EXIT_SUCCESS;
541*415435b4SRobert Mustacchi 	const char *device = NULL, *barstr = NULL, *lenstr = NULL;
542*415435b4SRobert Mustacchi 	const pcieadm_ops_t *ops;
543*415435b4SRobert Mustacchi 	void *karg;
544*415435b4SRobert Mustacchi 
545*415435b4SRobert Mustacchi 	while ((c = getopt(argc, argv, ":b:d:l:")) != -1) {
546*415435b4SRobert Mustacchi 		switch (c) {
547*415435b4SRobert Mustacchi 		case 'b':
548*415435b4SRobert Mustacchi 			barstr = optarg;
549*415435b4SRobert Mustacchi 			break;
550*415435b4SRobert Mustacchi 		case 'd':
551*415435b4SRobert Mustacchi 			device = optarg;
552*415435b4SRobert Mustacchi 			break;
553*415435b4SRobert Mustacchi 		case 'l':
554*415435b4SRobert Mustacchi 			lenstr = optarg;
555*415435b4SRobert Mustacchi 			break;
556*415435b4SRobert Mustacchi 		case ':':
557*415435b4SRobert Mustacchi 			pcieadm_bar_read_help("Option -%c requires an "
558*415435b4SRobert Mustacchi 			    "argument", optopt);
559*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
560*415435b4SRobert Mustacchi 		case '?':
561*415435b4SRobert Mustacchi 		default:
562*415435b4SRobert Mustacchi 			pcieadm_bar_read_help("unknown option: -%c",
563*415435b4SRobert Mustacchi 			    optopt);
564*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
565*415435b4SRobert Mustacchi 		}
566*415435b4SRobert Mustacchi 	}
567*415435b4SRobert Mustacchi 
568*415435b4SRobert Mustacchi 	if (device == NULL) {
569*415435b4SRobert Mustacchi 		pcieadm_bar_read_help("missing required device argument (-d)");
570*415435b4SRobert Mustacchi 		exit(EXIT_USAGE);
571*415435b4SRobert Mustacchi 	}
572*415435b4SRobert Mustacchi 
573*415435b4SRobert Mustacchi 	if (barstr == NULL) {
574*415435b4SRobert Mustacchi 		pcieadm_bar_read_help("missing required bar argument (-b)");
575*415435b4SRobert Mustacchi 		exit(EXIT_USAGE);
576*415435b4SRobert Mustacchi 	}
577*415435b4SRobert Mustacchi 
578*415435b4SRobert Mustacchi 	argc -= optind;
579*415435b4SRobert Mustacchi 	argv += optind;
580*415435b4SRobert Mustacchi 
581*415435b4SRobert Mustacchi 	if (argc == 0) {
582*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "missing required register");
583*415435b4SRobert Mustacchi 	} else if (argc > 1) {
584*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "only a single register may be read");
585*415435b4SRobert Mustacchi 	}
586*415435b4SRobert Mustacchi 
587*415435b4SRobert Mustacchi 	uint8_t bar = pcieadm_bar_parse_bar(barstr);
588*415435b4SRobert Mustacchi 	uint8_t len = 4;
589*415435b4SRobert Mustacchi 	if (lenstr != NULL) {
590*415435b4SRobert Mustacchi 		len = pcieadm_bar_parse_len(lenstr);
591*415435b4SRobert Mustacchi 	}
592*415435b4SRobert Mustacchi 	uint64_t reg = pcieadm_bar_parse_u64(argv[0], "register");
593*415435b4SRobert Mustacchi 	void *buf = calloc(1, len);
594*415435b4SRobert Mustacchi 	if (buf == NULL) {
595*415435b4SRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate memory for read request");
596*415435b4SRobert Mustacchi 	}
597*415435b4SRobert Mustacchi 
598*415435b4SRobert Mustacchi 	/*
599*415435b4SRobert Mustacchi 	 * We will need full privileges to read a BAR.
600*415435b4SRobert Mustacchi 	 */
601*415435b4SRobert Mustacchi 	priv_fillset(pcip->pia_priv_eff);
602*415435b4SRobert Mustacchi 
603*415435b4SRobert Mustacchi 	pcieadm_find_dip(pcip, device);
604*415435b4SRobert Mustacchi 	pcieadm_init_ops_kernel(pcip, &ops, &karg);
605*415435b4SRobert Mustacchi 
606*415435b4SRobert Mustacchi 	if (!ops->pop_bar(bar, len, reg, buf, karg, B_FALSE)) {
607*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to read %u bytes at 0x%" PRIx64
608*415435b4SRobert Mustacchi 		    " from BAR %u", len, reg, bar);
609*415435b4SRobert Mustacchi 	}
610*415435b4SRobert Mustacchi 
611*415435b4SRobert Mustacchi 	switch (len) {
612*415435b4SRobert Mustacchi 	case 1:
613*415435b4SRobert Mustacchi 		(void) printf("0x%x\n", *(uint8_t *)buf);
614*415435b4SRobert Mustacchi 		break;
615*415435b4SRobert Mustacchi 	case 2:
616*415435b4SRobert Mustacchi 		(void) printf("0x%x\n", *(uint16_t *)buf);
617*415435b4SRobert Mustacchi 		break;
618*415435b4SRobert Mustacchi 	case 4:
619*415435b4SRobert Mustacchi 		(void) printf("0x%x\n", *(uint32_t *)buf);
620*415435b4SRobert Mustacchi 		break;
621*415435b4SRobert Mustacchi 	case 8:
622*415435b4SRobert Mustacchi 		(void) printf("0x%x\n", *(uint64_t *)buf);
623*415435b4SRobert Mustacchi 		break;
624*415435b4SRobert Mustacchi 	default:
625*415435b4SRobert Mustacchi 		abort();
626*415435b4SRobert Mustacchi 	}
627*415435b4SRobert Mustacchi 
628*415435b4SRobert Mustacchi 	free(buf);
629*415435b4SRobert Mustacchi 	pcieadm_fini_ops_kernel(karg);
630*415435b4SRobert Mustacchi 
631*415435b4SRobert Mustacchi 	return (ret);
632*415435b4SRobert Mustacchi }
633*415435b4SRobert Mustacchi 
634*415435b4SRobert Mustacchi static void
pcieadm_bar_write_usage(FILE * f)635*415435b4SRobert Mustacchi pcieadm_bar_write_usage(FILE *f)
636*415435b4SRobert Mustacchi {
637*415435b4SRobert Mustacchi 	(void) fprintf(f, "\tbar write\t[-l length] -d device -b bar "
638*415435b4SRobert Mustacchi 	    "reg=value\n");
639*415435b4SRobert Mustacchi }
640*415435b4SRobert Mustacchi 
641*415435b4SRobert Mustacchi static void
pcieadm_bar_write_help(const char * fmt,...)642*415435b4SRobert Mustacchi pcieadm_bar_write_help(const char *fmt, ...)
643*415435b4SRobert Mustacchi {
644*415435b4SRobert Mustacchi 	if (fmt != NULL) {
645*415435b4SRobert Mustacchi 		va_list ap;
646*415435b4SRobert Mustacchi 
647*415435b4SRobert Mustacchi 		va_start(ap, fmt);
648*415435b4SRobert Mustacchi 		vwarnx(fmt, ap);
649*415435b4SRobert Mustacchi 		va_end(ap);
650*415435b4SRobert Mustacchi 		(void) fprintf(stderr, "\n");
651*415435b4SRobert Mustacchi 	}
652*415435b4SRobert Mustacchi 
653*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  %s bar write [-l length] -d device "
654*415435b4SRobert Mustacchi 	    "-b bar reg=value", pcieadm_progname);
655*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Usage:  %s bar write [-l length] -d device "
656*415435b4SRobert Mustacchi 	    "-b bar reg=value\n", pcieadm_progname);
657*415435b4SRobert Mustacchi 	(void) fprintf(stderr, "Write data to a device BAR at offset <reg>.\n\n"
658*415435b4SRobert Mustacchi 	    "\t-b bar\t\tthe index of the BAR to write to\n"
659*415435b4SRobert Mustacchi 	    "\t-d device\tread BAR from the specified device (driver instance,"
660*415435b4SRobert Mustacchi 	    "\n\t\t\t/devices path, or b/d/f)\n"
661*415435b4SRobert Mustacchi 	    "\t-l length\tspecify the number of bytes to read: 1, 2, 4 "
662*415435b4SRobert Mustacchi 	    "(default),\n\t\t\tor 8\n");
663*415435b4SRobert Mustacchi 
664*415435b4SRobert Mustacchi }
665*415435b4SRobert Mustacchi 
666*415435b4SRobert Mustacchi static int
pcieadm_bar_write(pcieadm_t * pcip,int argc,char * argv[])667*415435b4SRobert Mustacchi pcieadm_bar_write(pcieadm_t *pcip, int argc, char *argv[])
668*415435b4SRobert Mustacchi {
669*415435b4SRobert Mustacchi 	int c, ret = EXIT_SUCCESS;
670*415435b4SRobert Mustacchi 	const char *device = NULL, *barstr = NULL, *lenstr = NULL;
671*415435b4SRobert Mustacchi 	const pcieadm_ops_t *ops;
672*415435b4SRobert Mustacchi 	void *karg;
673*415435b4SRobert Mustacchi 
674*415435b4SRobert Mustacchi 	while ((c = getopt(argc, argv, ":b:d:l:")) != -1) {
675*415435b4SRobert Mustacchi 		switch (c) {
676*415435b4SRobert Mustacchi 		case 'b':
677*415435b4SRobert Mustacchi 			barstr = optarg;
678*415435b4SRobert Mustacchi 			break;
679*415435b4SRobert Mustacchi 		case 'd':
680*415435b4SRobert Mustacchi 			device = optarg;
681*415435b4SRobert Mustacchi 			break;
682*415435b4SRobert Mustacchi 		case 'l':
683*415435b4SRobert Mustacchi 			lenstr = optarg;
684*415435b4SRobert Mustacchi 			break;
685*415435b4SRobert Mustacchi 		case ':':
686*415435b4SRobert Mustacchi 			pcieadm_bar_write_help("Option -%c requires an "
687*415435b4SRobert Mustacchi 			    "argument", optopt);
688*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
689*415435b4SRobert Mustacchi 		case '?':
690*415435b4SRobert Mustacchi 		default:
691*415435b4SRobert Mustacchi 			pcieadm_bar_write_help("unknown option: -%c",
692*415435b4SRobert Mustacchi 			    optopt);
693*415435b4SRobert Mustacchi 			exit(EXIT_USAGE);
694*415435b4SRobert Mustacchi 		}
695*415435b4SRobert Mustacchi 	}
696*415435b4SRobert Mustacchi 
697*415435b4SRobert Mustacchi 	if (device == NULL) {
698*415435b4SRobert Mustacchi 		pcieadm_bar_write_help("missing required device argument (-d)");
699*415435b4SRobert Mustacchi 		exit(EXIT_USAGE);
700*415435b4SRobert Mustacchi 	}
701*415435b4SRobert Mustacchi 
702*415435b4SRobert Mustacchi 	if (barstr == NULL) {
703*415435b4SRobert Mustacchi 		pcieadm_bar_write_help("missing required bar argument (-b)");
704*415435b4SRobert Mustacchi 		exit(EXIT_USAGE);
705*415435b4SRobert Mustacchi 	}
706*415435b4SRobert Mustacchi 
707*415435b4SRobert Mustacchi 	argc -= optind;
708*415435b4SRobert Mustacchi 	argv += optind;
709*415435b4SRobert Mustacchi 
710*415435b4SRobert Mustacchi 	if (argc == 0) {
711*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "missing required register");
712*415435b4SRobert Mustacchi 	} else if (argc > 1) {
713*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "only a single register may be read");
714*415435b4SRobert Mustacchi 	}
715*415435b4SRobert Mustacchi 	char *eq = strchr(argv[0], '=');
716*415435b4SRobert Mustacchi 	if (eq == NULL) {
717*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to parse value string %s: missing "
718*415435b4SRobert Mustacchi 		    "equals ('=') separator", argv[0]);
719*415435b4SRobert Mustacchi 	} else if (eq[1] == '\0') {
720*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "missing a value after the equals ('=') "
721*415435b4SRobert Mustacchi 		    "separator");
722*415435b4SRobert Mustacchi 	}
723*415435b4SRobert Mustacchi 	*eq = '\0';
724*415435b4SRobert Mustacchi 
725*415435b4SRobert Mustacchi 	uint8_t bar = pcieadm_bar_parse_bar(barstr);
726*415435b4SRobert Mustacchi 	uint8_t len = 4;
727*415435b4SRobert Mustacchi 	if (lenstr != NULL) {
728*415435b4SRobert Mustacchi 		len = pcieadm_bar_parse_len(lenstr);
729*415435b4SRobert Mustacchi 	}
730*415435b4SRobert Mustacchi 	void *buf = malloc(len);
731*415435b4SRobert Mustacchi 	if (buf == NULL) {
732*415435b4SRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate memory for read request");
733*415435b4SRobert Mustacchi 	}
734*415435b4SRobert Mustacchi 	uint64_t reg = pcieadm_bar_parse_u64(argv[0], "register");
735*415435b4SRobert Mustacchi 	uint64_t value = pcieadm_bar_parse_u64(eq + 1, "value");
736*415435b4SRobert Mustacchi 
737*415435b4SRobert Mustacchi 	uint8_t u8;
738*415435b4SRobert Mustacchi 	uint16_t u16;
739*415435b4SRobert Mustacchi 	uint32_t u32;
740*415435b4SRobert Mustacchi 	switch (len) {
741*415435b4SRobert Mustacchi 	case 1:
742*415435b4SRobert Mustacchi 		if (value > UINT8_MAX) {
743*415435b4SRobert Mustacchi 			errx(EXIT_FAILURE, "value %s is too large for a "
744*415435b4SRobert Mustacchi 			    "1 byte write", eq + 1);
745*415435b4SRobert Mustacchi 		}
746*415435b4SRobert Mustacchi 		u8 = (uint8_t)value;
747*415435b4SRobert Mustacchi 		(void) memcpy(buf, &u8, sizeof (uint8_t));
748*415435b4SRobert Mustacchi 		break;
749*415435b4SRobert Mustacchi 	case 2:
750*415435b4SRobert Mustacchi 		if (value > UINT16_MAX) {
751*415435b4SRobert Mustacchi 			errx(EXIT_FAILURE, "value %s is too large for a "
752*415435b4SRobert Mustacchi 			    "2 byte write", eq + 1);
753*415435b4SRobert Mustacchi 		}
754*415435b4SRobert Mustacchi 		u16 = (uint8_t)value;
755*415435b4SRobert Mustacchi 		(void) memcpy(buf, &u16, sizeof (uint16_t));
756*415435b4SRobert Mustacchi 		break;
757*415435b4SRobert Mustacchi 	case 4:
758*415435b4SRobert Mustacchi 		if (value > UINT32_MAX) {
759*415435b4SRobert Mustacchi 			errx(EXIT_FAILURE, "value %s is too large for a "
760*415435b4SRobert Mustacchi 			    "4 byte write", eq + 1);
761*415435b4SRobert Mustacchi 		}
762*415435b4SRobert Mustacchi 		u32 = (uint32_t)value;
763*415435b4SRobert Mustacchi 		(void) memcpy(buf, &u32, sizeof (uint32_t));
764*415435b4SRobert Mustacchi 		break;
765*415435b4SRobert Mustacchi 	case 8:
766*415435b4SRobert Mustacchi 		(void) memcpy(buf, &value, sizeof (uint64_t));
767*415435b4SRobert Mustacchi 		break;
768*415435b4SRobert Mustacchi 	default:
769*415435b4SRobert Mustacchi 		abort();
770*415435b4SRobert Mustacchi 	}
771*415435b4SRobert Mustacchi 
772*415435b4SRobert Mustacchi 
773*415435b4SRobert Mustacchi 	/*
774*415435b4SRobert Mustacchi 	 * We will need full privileges to read a BAR.
775*415435b4SRobert Mustacchi 	 */
776*415435b4SRobert Mustacchi 	priv_fillset(pcip->pia_priv_eff);
777*415435b4SRobert Mustacchi 
778*415435b4SRobert Mustacchi 	pcieadm_find_dip(pcip, device);
779*415435b4SRobert Mustacchi 	pcieadm_init_ops_kernel(pcip, &ops, &karg);
780*415435b4SRobert Mustacchi 
781*415435b4SRobert Mustacchi 	if (!ops->pop_bar(bar, len, reg, buf, karg, B_TRUE)) {
782*415435b4SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to write %u bytes at 0x%" PRIx64
783*415435b4SRobert Mustacchi 		    " from BAR %u", len, reg, bar);
784*415435b4SRobert Mustacchi 	}
785*415435b4SRobert Mustacchi 
786*415435b4SRobert Mustacchi 	pcieadm_fini_ops_kernel(karg);
787*415435b4SRobert Mustacchi 
788*415435b4SRobert Mustacchi 	return (ret);
789*415435b4SRobert Mustacchi }
790*415435b4SRobert Mustacchi 
791*415435b4SRobert Mustacchi 
792*415435b4SRobert Mustacchi static const pcieadm_cmdtab_t pcieadm_cmds_dev[] = {
793*415435b4SRobert Mustacchi 	{ "list", pcieadm_bar_list, pcieadm_bar_list_usage },
794*415435b4SRobert Mustacchi 	{ "read", pcieadm_bar_read, pcieadm_bar_read_usage },
795*415435b4SRobert Mustacchi 	{ "write", pcieadm_bar_write, pcieadm_bar_write_usage },
796*415435b4SRobert Mustacchi 	{ NULL }
797*415435b4SRobert Mustacchi };
798*415435b4SRobert Mustacchi 
799*415435b4SRobert Mustacchi int
pcieadm_bar(pcieadm_t * pcip,int argc,char * argv[])800*415435b4SRobert Mustacchi pcieadm_bar(pcieadm_t *pcip, int argc, char *argv[])
801*415435b4SRobert Mustacchi {
802*415435b4SRobert Mustacchi 	return (pcieadm_walk_tab(pcip, pcieadm_cmds_dev, argc, argv));
803*415435b4SRobert Mustacchi }
804*415435b4SRobert Mustacchi 
805*415435b4SRobert Mustacchi void
pcieadm_bar_usage(FILE * f)806*415435b4SRobert Mustacchi pcieadm_bar_usage(FILE *f)
807*415435b4SRobert Mustacchi {
808*415435b4SRobert Mustacchi 	pcieadm_walk_usage(pcieadm_cmds_dev, f);
809*415435b4SRobert Mustacchi }
810