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