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", ®);
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