/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ /* * This is the user interface module for the pcitool. It checks commandline * arguments and options and stores them in a pcitool_uiargs_t structure passed * back to the rest of the program for processing. * * Please see pcitool_usage.c for a complete commandline description. */ #include #include #include #include #include #include #include #include #include #include #include "pcitool_ui.h" /* * Uncomment the following for useful debugging / development options for this * module only. */ /* #define DEBUG 1 */ /* #define STANDALONE 1 */ #define DEVNAME_START_PCI "/pci" #define DEVNAME_START_NIU "/niu" /* Default read/write size when -s not specified. */ #define DEFAULT_SIZE 4 /* For get_value64 */ #define HEX_ONLY B_TRUE #define BASE_BY_PREFIX B_FALSE #define BITS_PER_BYTE 8 /* * This defines which main options can be specified by the user. * Options with colons after them require arguments. */ static char *opt_string = ":n:d:i:m:p:rw:o:s:e:b:vaqlcxgy"; /* This defines options used singly and only by themselves (no nexus). */ static char *no_dev_opt_string = "ahpqv"; static void print_bad_option(char *argv[], int optopt, char *optarg); static boolean_t get_confirmation(void); static int get_value64(char *value_str, uint64_t *value, boolean_t hex_only); static int parse_nexus_opts(char *input, uint64_t *flags_arg, uint8_t *bank_arg, uint64_t *base_addr_arg); static int extract_bdf_arg(char *cvalue, char *fld, uint64_t fld_flag, uint64_t *all_flags, uint8_t *ivalue); static int extract_bdf(char *value, char **bvalue_p, char **dvalue_p, char **fvalue_p); static int parse_device_opts(char *input, uint64_t *flags_arg, uint8_t *bus_arg, uint8_t *device_arg, uint8_t *func_arg, uint8_t *bank_arg); static int parse_ino_opts(char *input, uint64_t *flags_arg, uint32_t *cpu_arg, uint8_t *ino_arg); static int parse_msi_opts(char *input, uint64_t *flags_arg, uint16_t *msi_arg); static int parse_intr_set_opts(char *input, uint64_t *flags_arg, uint32_t *cpu_arg); static int parse_probeone_opts(char *input, uint64_t *flags_arg, uint8_t *bus_arg, uint8_t *device_arg, uint8_t *func_arg); #ifdef DEBUG void dump_struct(pcitool_uiargs_t *dump_this); #endif /* Exported functions. */ /* * Main commandline argument parsing routine. * * Takes argc and argv straight from the commandline. * Returns a pcitool_uiargs_t with flags of options specified, and values * associated with them. */ int get_commandline_args(int argc, char *argv[], pcitool_uiargs_t *parsed_args) { int c; /* Current option being processed. */ boolean_t error = B_FALSE; boolean_t confirm = B_FALSE; uint64_t recv64; /* Needed for getopt(3C) */ extern char *optarg; /* Current commandline string. */ extern int optind; /* Index of current commandline string. */ extern int optopt; /* Option (char) which is missing an operand. */ extern int opterr; /* Set to 0 to disable getopt err reporting. */ opterr = 0; bzero(parsed_args, sizeof (pcitool_uiargs_t)); /* No args. probe mode accounting for bus ranges, nonverbose. */ if (argc == 1) { usage(argv[0]); parsed_args->flags = 0; return (SUCCESS); } /* 1st arg is not a device name. */ if ((strstr(argv[1], DEVNAME_START_PCI) != argv[1]) && (strstr(argv[1], DEVNAME_START_NIU) != argv[1])) { /* Default is to probe all trees accounting for bus ranges. */ parsed_args->flags = PROBEALL_FLAG | PROBERNG_FLAG; /* Loop thru the options until complete or an error is found. */ while (((c = getopt(argc, argv, no_dev_opt_string)) != -1) && (error == B_FALSE)) { switch (c) { /* Help requested. */ case 'h': usage(argv[0]); parsed_args->flags = 0; return (SUCCESS); case 'p': /* Take default probe mode */ break; case 'a': /* * Enable display of ALL bus numbers. * * This takes precidence over PROBERNG as -a * is explicitly specified. */ parsed_args->flags &= ~PROBERNG_FLAG; break; case 'q': parsed_args->flags |= QUIET_FLAG; break; /* Verbose mode for full probe. */ case 'v': parsed_args->flags |= VERBOSE_FLAG; break; default: error = B_TRUE; break; } } /* Check for values straggling at the end of the command. */ if (optind != argc) { (void) fprintf(stderr, "%s: Unrecognized parameter " "at the end of the command.\n", argv[0]); error = B_TRUE; } if (error) { print_bad_option(argv, optopt, optarg); return (FAILURE); } return (SUCCESS); } /* Device node specified on commandline. */ /* Skip argv[1] before continuing below. */ optind++; /* Loop through the options until complete or an error is found. */ while (((c = getopt(argc, argv, opt_string)) != -1) && (error == B_FALSE)) { switch (c) { /* Nexus */ case 'n': if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS)) { (void) fprintf(stderr, "%s: -n set with " "-d, -p or -i or is set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= NEXUS_FLAG; if (parse_nexus_opts(optarg, &parsed_args->flags, &parsed_args->bank, &parsed_args->base_address) != SUCCESS) { (void) fprintf(stderr, "%s: Error parsing -n options\n", argv[0]); error = B_TRUE; break; } break; /* Device (leaf node) */ case 'd': if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS)) { (void) fprintf(stderr, "%s: -d set with " "-n, -p or -i or is set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= LEAF_FLAG; if (parse_device_opts(optarg, &parsed_args->flags, &parsed_args->bus, &parsed_args->device, &parsed_args->function, &parsed_args->bank) != SUCCESS) { (void) fprintf(stderr, "%s: Error parsing -d options\n", argv[0]); error = B_TRUE; break; } break; /* Interrupt */ case 'i': if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS)) { (void) fprintf(stderr, "%s: -i set with -m, " "-n, -d or -p or is set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= INTR_FLAG; /* parse input to get ino value. */ if (parse_ino_opts(optarg, &parsed_args->flags, &parsed_args->old_cpu, &parsed_args->intr_ino) != SUCCESS) { (void) fprintf(stderr, "%s: Error parsing interrupt options\n", argv[0]); error = B_TRUE; } break; /* Interrupt */ case 'm': if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS)) { (void) fprintf(stderr, "%s: -m set with -i, " "-n, -d or -p or is set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= INTR_FLAG; /* parse input to get msi value. */ if (parse_msi_opts(optarg, &parsed_args->flags, &parsed_args->intr_msi) != SUCCESS) { (void) fprintf(stderr, "%s: Error parsing interrupt options\n", argv[0]); error = B_TRUE; } break; /* Probe */ case 'p': if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS)) { (void) fprintf(stderr, "%s: -p set with " "-n, -d or -i or is set twice\n", argv[0]); error = B_TRUE; break; } /* Process -p with no dedicated options to it. */ if (optarg[0] == '-') { optind--; /* Probe given tree observing ranges */ parsed_args->flags |= (PROBETREE_FLAG | PROBERNG_FLAG); continue; } /* parse input to get ino value. */ if (parse_probeone_opts(optarg, &parsed_args->flags, &parsed_args->bus, &parsed_args->device, &parsed_args->function) != SUCCESS) { (void) fprintf(stderr, "%s: Error parsing probe options\n", argv[0]); error = B_TRUE; } else { /* * parse_probeone_opts found options to * set up bdf. */ parsed_args->flags |= PROBEDEV_FLAG; } break; /* Probe all busses */ case 'a': /* Must follow -p, and -p must have no bdf. */ if (!(parsed_args->flags & PROBETREE_FLAG)) { error = B_TRUE; break; } parsed_args->flags &= ~PROBERNG_FLAG; break; /* Read */ case 'r': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG))) { error = B_TRUE; break; } /* * Allow read and write to be set together for now, * since this means write then read back for device and * nexus accesses. Check for this and disallow with * interrupt command later. */ parsed_args->flags |= READ_FLAG; break; /* Write */ case 'w': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG))) { error = B_TRUE; break; } if (parsed_args->flags & WRITE_FLAG) { (void) fprintf(stderr, "%s: -w set twice\n", argv[0]); error = B_TRUE; break; } /* * For device and nexus, get a single register value * to write. */ if (parsed_args->flags & (NEXUS_FLAG | LEAF_FLAG)) { parsed_args->flags |= WRITE_FLAG; if (get_value64(optarg, &parsed_args->write_value, HEX_ONLY) != SUCCESS) { (void) fprintf(stderr, "%s: Error reading value to " "write.\n", argv[0]); error = B_TRUE; break; } /* For interrupt, parse input to get cpu value. */ } else if (parsed_args->flags & INTR_FLAG) { parsed_args->flags |= WRITE_FLAG; if (parse_intr_set_opts(optarg, &parsed_args->flags, &parsed_args->intr_cpu) != SUCCESS) { (void) fprintf(stderr, "%s: Error " "parsing interrupt options.\n", argv[0]); error = B_TRUE; break; } } else { error = B_TRUE; break; } break; /* Offset */ case 'o': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG))) { error = B_TRUE; break; } if (parsed_args->flags & OFFSET_FLAG) { (void) fprintf(stderr, "%s: -o set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= OFFSET_FLAG; if (get_value64(optarg, &recv64, HEX_ONLY) != SUCCESS) { (void) fprintf(stderr, "%s: Error in offset argument\n", argv[0]); error = B_TRUE; break; } parsed_args->offset = (uint32_t)recv64; if (parsed_args->offset != recv64) { (void) fprintf(stderr, "%s: Offset argument " "too large for 32 bits\n", argv[0]); error = B_TRUE; break; } break; /* Size */ case 's': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG))) { error = B_TRUE; break; } if (parsed_args->flags & SIZE_FLAG) { (void) fprintf(stderr, "%s: -s set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= SIZE_FLAG; if (get_value64(optarg, &recv64, HEX_ONLY) != SUCCESS) { (void) fprintf(stderr, "%s: Error in size argument\n", argv[0]); error = B_TRUE; break; } switch (recv64) { case 1: case 2: case 4: case 8: break; default: error = B_TRUE; (void) fprintf(stderr, "%s: Error in size argument\n", argv[0]); break; } parsed_args->size |= (uint8_t)recv64; break; /* Endian. */ case 'e': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG))) { error = B_TRUE; break; } if (parsed_args->flags & ENDIAN_FLAG) { (void) fprintf(stderr, "%s: -e set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= ENDIAN_FLAG; /* Only a single character allowed. */ if (optarg[1] != '\0') { (void) fprintf(stderr, "%s: Error in endian argument\n", argv[0]); error = B_TRUE; break; } switch (optarg[0]) { case 'b': parsed_args->big_endian = B_TRUE; break; case 'l': break; default: (void) fprintf(stderr, "%s: Error in endian argument\n", argv[0]); error = B_TRUE; break; } break; /* (Byte)dump */ case 'b': if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG))) { error = B_TRUE; break; } if (parsed_args->flags & BYTEDUMP_FLAG) { (void) fprintf(stderr, "%s: -b set twice\n", argv[0]); error = B_TRUE; break; } parsed_args->flags |= BYTEDUMP_FLAG; if (get_value64(optarg, &recv64, HEX_ONLY) != SUCCESS) { (void) fprintf(stderr, "%s: Error in " "bytedump argument\n", argv[0]); error = B_TRUE; break; } parsed_args->bytedump_amt = (uint32_t)recv64; if (parsed_args->bytedump_amt != recv64) { (void) fprintf(stderr, "%s: Bytedump amount " "too large for 32 bits\n", argv[0]); error = B_TRUE; break; } break; /* Verbose. */ case 'v': parsed_args->flags |= VERBOSE_FLAG; break; /* * Quiet - no errors reported as messages. * (Status still returned by program, however.) */ case 'q': parsed_args->flags |= QUIET_FLAG; break; /* Loop. */ case 'l': parsed_args->flags |= LOOP_FLAG; break; /* * Dump characters with bytedump (-b). * Show controller info with -i. */ case 'c': if (parsed_args->flags & BYTEDUMP_FLAG) { parsed_args->flags |= CHARDUMP_FLAG; } else if (parsed_args->flags & INTR_FLAG) { parsed_args->flags |= SHOWCTLR_FLAG; } else { error = B_TRUE; } break; /* Continue on errors with bytedump (-b). */ case 'x': if (!(parsed_args->flags & BYTEDUMP_FLAG)) { error = B_TRUE; break; } parsed_args->flags |= ERRCONT_FLAG; break; case 'g': if (!(parsed_args->flags & INTR_FLAG)) { error = B_TRUE; break; } parsed_args->flags |= SETGRP_FLAG; break; /* Take -y as confirmation and don't ask (where applicable). */ case 'y': confirm = B_TRUE; break; /* Option without operand. */ case ':': switch (optopt) { case 'p': /* Allow -p without bdf spec. */ parsed_args->flags |= (PROBETREE_FLAG | PROBERNG_FLAG); break; default: error = B_TRUE; break; } break; /* Unrecognized option. */ case '?': error = B_TRUE; break; } } /* * Commandline has been parsed. Check for errors which can be checked * only after commandline parsing is complete. */ if (!error) { /* Check for values straggling at the end of the command. */ if (optind != argc) { (void) fprintf(stderr, "%s: Unrecognized parameter " "at the end of the command.\n", argv[0]); print_bad_option(argv, optopt, optarg); return (FAILURE); } /* No args other than nexus. Default to probing that nexus */ if (!(parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG | INTR_FLAG | PROBE_FLAGS))) { usage(argv[0]); parsed_args->flags = 0; return (SUCCESS); } /* * Don't allow any options other than all-bus, verbose or * quiet with probe command. Set default probe flags if nexus * or leaf options are not specified. */ if (parsed_args->flags & (PROBETREE_FLAG | PROBEALL_FLAG)) { if (parsed_args->flags & ~(PROBE_FLAGS | QUIET_FLAG | VERBOSE_FLAG)) error = B_TRUE; } /* * Allow only read, write, quiet and verbose flags for * interrupt command. Note that INO_SPEC_FLAG and CPU_SPEC_FLAG * get set for interrupt command. */ if (parsed_args->flags & INTR_FLAG) { if (parsed_args->flags & ~(INTR_FLAG | VERBOSE_FLAG | QUIET_FLAG | READ_FLAG | WRITE_FLAG | SHOWCTLR_FLAG | SETGRP_FLAG | INO_ALL_FLAG | INO_SPEC_FLAG | MSI_ALL_FLAG | MSI_SPEC_FLAG | CPU_SPEC_FLAG)) { (void) fprintf(stderr, "%s: -v, -q, -r, -w, -c " "-g are only options allowed with " "interrupt command.\n", argv[0]); error = B_TRUE; } /* Need cpu and ino values for interrupt set command. */ if ((parsed_args->flags & WRITE_FLAG) && !(parsed_args->flags & CPU_SPEC_FLAG) && !((parsed_args->flags & INO_SPEC_FLAG) || (parsed_args->flags & MSI_SPEC_FLAG))) { (void) fprintf(stderr, "%s: Both cpu and ino/msi must be " "specified explicitly for interrupt " "set command.\n", argv[0]); error = B_TRUE; } /* Intr write and show ctlr flags are incompatible. */ if ((parsed_args->flags & (WRITE_FLAG + SHOWCTLR_FLAG)) == (WRITE_FLAG + SHOWCTLR_FLAG)) { (void) fprintf(stderr, "%s: -w and -c are incompatible for " "interrupt command.\n", argv[0]); error = B_TRUE; } /* Intr setgrp flag valid only for intr writes. */ if ((parsed_args->flags & (WRITE_FLAG + SETGRP_FLAG)) == SETGRP_FLAG) { (void) fprintf(stderr, "%s: -g is incompatible with -r " "for interrupt command.\n", argv[0]); error = B_TRUE; } /* * Disallow read & write together in interrupt command. */ if ((parsed_args->flags & (WRITE_FLAG | READ_FLAG)) == (WRITE_FLAG | READ_FLAG)) { (void) fprintf(stderr, "%s: Only one of -r and " "-w can be specified in " "interrupt command.\n", argv[0]); error = B_TRUE; } } /* Bytedump incompatible with some other options. */ if ((parsed_args->flags & BYTEDUMP_FLAG) && (parsed_args->flags & (WRITE_FLAG | PROBE_FLAGS | INTR_FLAG))) { (void) fprintf(stderr, "%s: -b is incompatible with " "another specified option.\n", argv[0]); error = B_TRUE; } if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG)) { if (!(parsed_args->flags & SIZE_FLAG)) { parsed_args->size = DEFAULT_SIZE; } if ((parsed_args->flags & WRITE_FLAG) && parsed_args->size < sizeof (uint64_t) && (parsed_args->write_value >> (parsed_args->size * BITS_PER_BYTE))) { (void) fprintf(stderr, "%s: Data to write is larger than " "specified size.\n", argv[0]); error = B_TRUE; } } else { /* Looping is compatible only with register cmds. */ if (parsed_args->flags & LOOP_FLAG) { (void) fprintf(stderr, "%s: -l is incompatible " "with given command.\n", argv[0]); error = B_TRUE; } } /* Call out an erroneous -y and then ignore it. */ if ((confirm) && (!(parsed_args->flags & BASE_SPEC_FLAG))) { (void) fprintf(stderr, "%s: -y is incompatible with given command." " Ignoring.\n", argv[0]); } } /* Now fill in the defaults and other holes. */ if (!(error)) { if (!(parsed_args->flags & (READ_FLAG | WRITE_FLAG))) { parsed_args->flags |= READ_FLAG; } if (parsed_args->flags & (LEAF_FLAG | NEXUS_FLAG)) { if (!(parsed_args->flags & ENDIAN_FLAG)) { parsed_args->big_endian = B_FALSE; } } if (parsed_args->flags & BASE_SPEC_FLAG) { if (!confirm) { confirm = get_confirmation(); } if (!confirm) { parsed_args->flags &= ~ALL_COMMANDS; } } /* * As far as other defaults are concerned: * Other fields: bus, device, function, offset, default to * zero. */ } else { /* An error occurred. */ print_bad_option(argv, optopt, optarg); } return (error); } /* Module-private functions. */ static void print_bad_option(char *argv[], int optopt, char *optarg) { /* Illegal option operand */ if (optarg != NULL) { (void) fprintf(stderr, "%s: illegal operand %s specified for option %c\n", argv[0], optarg, optopt); /* Illegal option */ } else if (optopt != 0) { (void) fprintf(stderr, "%s: option %c is illegal or is missing an operand\n", argv[0], optopt); /* getopt wasn't even called. Bad device spec. */ } else { (void) fprintf(stderr, "%s: device spec must start with %s or %s...\n", argv[0], DEVNAME_START_PCI, DEVNAME_START_NIU); } (void) fprintf(stderr, "%s: Type \"%s -h\" to get help on running this program.\n", argv[0], argv[0]); } /* * Warn the user and ask for confirmation. */ static boolean_t get_confirmation() { int i, b; (void) printf("WARNING: This cmd with a bad addr can panic " "the system. Continue [y/n] (n)? "); for (i = 0; ; i++) { b = getchar(); switch (b) { case ' ': case '\t': break; case 'y': case 'Y': return (B_TRUE); default: return (B_FALSE); } } } /* * Given a digit string, return a 64 bit value. * * If the hex_only arg is true, interpret all strings as hex. * Otherwise, interpret as strtoull(3C) does with base=0. */ static int get_value64(char *value_str, uint64_t *value, boolean_t hex_only) { /* This is overkill for now, as everything is in hex. */ static char dec_digits[] = "0123456789"; static char hex_digits[] = "01234567890abcdefABCDEF"; static char oct_digits[] = "01234567"; char *digit_string; char *string_to_check; if ((value_str == NULL) || (strlen(value_str) == 0)) { (void) fprintf(stderr, "Missing value argument.\n"); return (FAILURE); } if (!hex_only && (value_str[0] != '0')) { digit_string = dec_digits; string_to_check = value_str; } else if ((value_str[1] == 'X') || (value_str[1] == 'x')) { digit_string = hex_digits; string_to_check = &value_str[2]; /* Ignore 0x of hex */ } else if (hex_only) { digit_string = hex_digits; string_to_check = value_str; /* Hex number, no 0x prefix */ } else { digit_string = oct_digits; string_to_check = value_str; } /* * Verify value is all proper digits. * * For some reason, strtoull doesn't return an error when it cannot * interpret the value. This is why we do the checking ourselves. */ if (strspn(string_to_check, digit_string) != strlen(string_to_check)) { (void) fprintf(stderr, "Value must contain only valid digits.\n"); return (FAILURE); } *value = strtoull(value_str, NULL, (hex_only ? 16 : 0)); return (SUCCESS); } /* * Parse nexus options. This includes: * bank=number * * input is what the user specified for the options on the commandline, * flags_arg is modified with the option set, and bank_arg returns the value * specified for bank. */ static int parse_nexus_opts(char *input, uint64_t *flags_arg, uint8_t *bank_arg, uint64_t *base_addr_arg) { enum nexus_opts_index { bank = 0, base }; static char *nexus_opts[] = { "bank", "base", NULL }; char *value; uint64_t recv64; int rval = SUCCESS; if (input == NULL) { (void) fprintf(stderr, "Missing argument.\n"); return (FAILURE); } while ((*input != '\0') && (rval == SUCCESS)) { switch (getsubopt(&input, nexus_opts, &value)) { case bank: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, "The bank or bar arg is " "specified more than once.\n"); rval = FAILURE; break; } if (*flags_arg & BASE_SPEC_FLAG) { (void) fprintf(stderr, "Bank and base address " "cannot both be specified.\n"); rval = FAILURE; break; } if (value == NULL) { (void) fprintf(stderr, "Missing bank value.\n"); rval = FAILURE; break; } if ((rval = get_value64(value, &recv64, HEX_ONLY)) != SUCCESS) { break; } *bank_arg = (uint8_t)recv64; if (*bank_arg != recv64) { (void) fprintf(stderr, "Bank argument must fit into 8 bits.\n"); rval = FAILURE; break; } *flags_arg |= BANK_SPEC_FLAG; break; case base: if (*flags_arg & BASE_SPEC_FLAG) { (void) fprintf(stderr, "The base address " "is specified more than once.\n"); rval = FAILURE; break; } if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, "Bank and base address " "cannot both be specified.\n"); rval = FAILURE; break; } if (value == NULL) { (void) fprintf(stderr, "Missing base addr value.\n"); rval = FAILURE; break; } if ((rval = get_value64(value, base_addr_arg, HEX_ONLY)) != SUCCESS) { break; } *flags_arg |= BASE_SPEC_FLAG; break; default: (void) fprintf(stderr, "Unrecognized option for -n\n"); rval = FAILURE; break; } } return (rval); } static int extract_bdf_arg(char *cvalue, char *fld, uint64_t fld_flag, uint64_t *all_flags, uint8_t *ivalue) { uint64_t recv64; if (*all_flags & fld_flag) { (void) fprintf(stderr, "The %s is specified more than once.\n", fld); return (FAILURE); } if (get_value64(cvalue, &recv64, HEX_ONLY) != SUCCESS) return (FAILURE); *ivalue = (uint8_t)recv64; if (recv64 != *ivalue) { (void) fprintf(stderr, "This program limits the %s argument to 8 bits.\n", fld); (void) fprintf(stderr, "The actual maximum may be " "smaller but cannot be enforced by this program.\n"); return (FAILURE); } *all_flags |= fld_flag; return (SUCCESS); } static int extract_bdf(char *value, char **bvalue_p, char **dvalue_p, char **fvalue_p) { char *strtok_state; char *dummy; static char *separator = "."; *bvalue_p = strtok_r(value, separator, &strtok_state); *dvalue_p = strtok_r(NULL, separator, &strtok_state); *fvalue_p = strtok_r(NULL, separator, &strtok_state); dummy = strtok_r(NULL, separator, &strtok_state); /* Return failure only if too many values specified. */ return ((dummy) ? FAILURE : SUCCESS); } /* * Parse device options. This includes: * bus=number * dev=number * func=number * bank=number * config * bar0 * bar1 * bar2 * bar3 * bar4 * bar5 * rom * * input is what the user specified for the options on the commandline, * flags_arg is modified with the options set, and the rest of the args return * their respective values. */ static int parse_device_opts( char *input, uint64_t *flags_arg, uint8_t *bus_arg, uint8_t *device_arg, uint8_t *func_arg, uint8_t *bank_arg) { /* Needed by getsubopt(3C) */ enum bdf_opts_index { bus = 0, dev = 1, func = 2, bdf = 3, bank = 4, config = 5, bar0 = 6, bar1 = 7, bar2 = 8, bar3 = 9, bar4 = 10, bar5 = 11, rom = 12 }; /* Needed by getsubopt(3C) */ static char *bdf_opts[] = { "bus", "dev", "func", "bdf", "bank", "config", "bar0", "bar1", "bar2", "bar3", "bar4", "bar5", "rom", NULL }; char *value; /* Current suboption being processed. */ uint64_t recv64; /* Temporary value. */ /* This error message is used in many places. */ static char bank_err[] = {"The bank or bar arg is specified more than once.\n"}; int rval = SUCCESS; while ((*input != '\0') && (rval == SUCCESS)) { switch (getsubopt(&input, bdf_opts, &value)) { /* bus=number */ case bdf: { char *bvalue, *dvalue, *fvalue; if ((rval = extract_bdf(value, &bvalue, &dvalue, &fvalue)) != SUCCESS) { break; } if (!bvalue | !dvalue | !fvalue) { break; } if ((rval = extract_bdf_arg(bvalue, "bus", BUS_SPEC_FLAG, flags_arg, bus_arg)) != SUCCESS) { break; } if ((rval = extract_bdf_arg(dvalue, "dev", DEV_SPEC_FLAG, flags_arg, device_arg)) != SUCCESS) { break; } rval = extract_bdf_arg(fvalue, "func", FUNC_SPEC_FLAG, flags_arg, func_arg); break; } case bus: rval = extract_bdf_arg(value, "bus", BUS_SPEC_FLAG, flags_arg, bus_arg); break; /* dev=number */ case dev: rval = extract_bdf_arg(value, "dev", DEV_SPEC_FLAG, flags_arg, device_arg); break; /* func=number */ case func: rval = extract_bdf_arg(value, "func", FUNC_SPEC_FLAG, flags_arg, func_arg); break; /* bank=number */ case bank: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } if ((rval = get_value64(value, &recv64, HEX_ONLY)) != SUCCESS) { break; } *bank_arg = (uint8_t)recv64; if (rval || (*bank_arg != recv64)) { (void) fprintf(stderr, "Bank argument must" " fit into 8 bits.\n"); rval = FAILURE; break; } *flags_arg |= BANK_SPEC_FLAG; break; /* config */ case config: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_CONFIG; *flags_arg |= BANK_SPEC_FLAG; break; /* bar0 */ case bar0: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR0; *flags_arg |= BANK_SPEC_FLAG; break; /* bar1 */ case bar1: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR1; *flags_arg |= BANK_SPEC_FLAG; break; /* bar2 */ case bar2: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR2; *flags_arg |= BANK_SPEC_FLAG; break; /* bar3 */ case bar3: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR3; *flags_arg |= BANK_SPEC_FLAG; break; /* bar4 */ case bar4: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR4; *flags_arg |= BANK_SPEC_FLAG; break; /* bar5 */ case bar5: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_BAR5; *flags_arg |= BANK_SPEC_FLAG; break; /* rom */ case rom: if (*flags_arg & BANK_SPEC_FLAG) { (void) fprintf(stderr, bank_err); rval = FAILURE; break; } *bank_arg = PCITOOL_ROM; *flags_arg |= BANK_SPEC_FLAG; break; default: (void) fprintf(stderr, "Unrecognized option for -d\n"); rval = FAILURE; break; } } /* Bus, dev and func must all be specified. */ if ((*flags_arg & (BUS_SPEC_FLAG | DEV_SPEC_FLAG | FUNC_SPEC_FLAG)) != (BUS_SPEC_FLAG | DEV_SPEC_FLAG | FUNC_SPEC_FLAG)) { rval = FAILURE; /* No bank specified in any way. Default to config space */ } else if ((*flags_arg & BANK_SPEC_FLAG) == 0) { *flags_arg |= BANK_SPEC_FLAG; *bank_arg = PCITOOL_CONFIG; } return (rval); } /* * Parse INO options. This includes: * ino# | all * * input is the string of options to parse. flags_arg returns modified with * specified options set. Other args return their respective values. */ static int parse_ino_opts(char *input, uint64_t *flags_arg, uint32_t *cpu_arg, uint8_t *ino_arg) { uint64_t value; char *charvalue; int rval = SUCCESS; if (strcmp(input, "all") == 0) { *flags_arg |= INO_ALL_FLAG; #ifdef __x86 } else if (strstr(input, ",") == NULL) { (void) fprintf(stderr, "Interrupt format should be .\n"); rval = FAILURE; #else } else if (strstr(input, ",") == NULL) { if ((rval = get_value64(input, &value, HEX_ONLY)) == SUCCESS) *ino_arg = (uint8_t)value; if (*ino_arg != value) { (void) fprintf(stderr, "ino argument must fit into 8 bits.\n"); rval = FAILURE; } else { *flags_arg |= INO_SPEC_FLAG; } #endif } else if (charvalue = strtok(input, ",")) { if ((rval = get_value64(charvalue, &value, HEX_ONLY)) == SUCCESS) { *cpu_arg = (int)value; } input = strtok(NULL, ","); if (input == NULL) { (void) fprintf(stderr, "ino argument is need.\n"); return (FAILURE); } if ((rval = get_value64(input, &value, HEX_ONLY)) == SUCCESS) *ino_arg = (uint8_t)value; if (*ino_arg != value) { (void) fprintf(stderr, "ino argument must fit into 8 bits.\n"); rval = FAILURE; } else { *flags_arg |= INO_SPEC_FLAG; } } else { (void) fprintf(stderr, "Unrecognized option for -i\n"); rval = FAILURE; } return (rval); } /* * Parse MSI options. This includes: * msi# | all * * input is the string of options to parse. flags_arg returns modified with * specified options set. Other args return their respective values. */ static int parse_msi_opts(char *input, uint64_t *flags_arg, uint16_t *msi_arg) { uint64_t value; int rval = SUCCESS; if (strcmp(input, "all") == 0) { *flags_arg |= MSI_ALL_FLAG; } else if (strstr(input, ",") == NULL) { if ((rval = get_value64(input, &value, HEX_ONLY)) == SUCCESS) *msi_arg = (uint16_t)value; if (*msi_arg != value) { (void) fprintf(stderr, "msi argument must fit into 16 bits.\n"); rval = FAILURE; } else { *flags_arg |= MSI_SPEC_FLAG; } } else if (strtok(input, ",")) { input = strtok(NULL, ","); if (input == NULL) { (void) fprintf(stderr, "msi argument is need.\n"); return (FAILURE); } if ((rval = get_value64(input, &value, HEX_ONLY)) == SUCCESS) *msi_arg = (uint16_t)value; if (*msi_arg != value) { (void) fprintf(stderr, "msi argument must fit into 16 bits.\n"); rval = FAILURE; } else { *flags_arg |= MSI_SPEC_FLAG; } } else { (void) fprintf(stderr, "Unrecognized option for -m\n"); rval = FAILURE; } return (rval); } /* * Parse interrupt set options. This includes: * cpu=number * * input is the string of options to parse. flags_arg returns modified with * specified options set. Other args return their respective values. */ static int parse_intr_set_opts(char *input, uint64_t *flags_arg, uint32_t *cpu_arg) { uint64_t value; int rval = SUCCESS; if ((rval = get_value64(input, &value, HEX_ONLY)) == SUCCESS) { if ((long)value > sysconf(_SC_CPUID_MAX)) { (void) fprintf(stderr, "Cpu argument " "exceeds maximum for this system type.\n"); rval = FAILURE; } else { *cpu_arg = (uint32_t)value; *flags_arg |= CPU_SPEC_FLAG; } } else { (void) fprintf(stderr, "Unrecognized option for -i -m -w\n"); rval = FAILURE; } return (rval); } static int parse_probeone_opts( char *input, uint64_t *flags_arg, uint8_t *bus_arg, uint8_t *device_arg, uint8_t *func_arg) { enum p1_bdf_opts_index { bus = 0, dev = 1, func = 2, bdf = 3 }; /* Needed by getsubopt(3C) */ static char *p1_bdf_opts[] = { "bus", "dev", "func", "bdf", NULL }; char *value; /* Current suboption being processed. */ int rval = SUCCESS; while ((*input != '\0') && (rval == SUCCESS)) { switch (getsubopt(&input, p1_bdf_opts, &value)) { /* bus=number */ case bdf: { char *bvalue, *dvalue, *fvalue; if ((rval = extract_bdf(value, &bvalue, &dvalue, &fvalue)) != SUCCESS) { break; } if (bvalue) if ((rval = extract_bdf_arg(bvalue, "bus", BUS_SPEC_FLAG, flags_arg, bus_arg)) != SUCCESS) { break; } if (dvalue) if ((rval = extract_bdf_arg(dvalue, "dev", DEV_SPEC_FLAG, flags_arg, device_arg)) != SUCCESS) { break; } if (fvalue) rval = extract_bdf_arg(fvalue, "func", FUNC_SPEC_FLAG, flags_arg, func_arg); break; } case bus: rval = extract_bdf_arg(value, "bus", BUS_SPEC_FLAG, flags_arg, bus_arg); break; /* dev=number */ case dev: rval = extract_bdf_arg(value, "dev", DEV_SPEC_FLAG, flags_arg, device_arg); break; /* func=number */ case func: rval = extract_bdf_arg(value, "func", FUNC_SPEC_FLAG, flags_arg, func_arg); break; default: (void) fprintf(stderr, "Unrecognized option for -p\n"); rval = FAILURE; break; } } return (rval); } #ifdef DEBUG static void dump_struct(pcitool_uiargs_t *dumpthis) { (void) printf("flags:0x%x\n", dumpthis->flags); (void) printf("bus:%d (0x%x)\n", dumpthis->bus, dumpthis->bus); (void) printf("device:%d (0x%x)\n", dumpthis->device, dumpthis->device); (void) printf("function:%d (0x%x)\n", dumpthis->function, dumpthis->function); (void) printf("write_value:%" PRIu64 " (0x%" PRIx64 ")\n", dumpthis->write_value, dumpthis->write_value); (void) printf("bank:%d (0x%x)\n", dumpthis->bank, dumpthis->bank); (void) printf("offset:%d (0x%x)\n", dumpthis->offset, dumpthis->offset); (void) printf("size:%d, endian:%s\n", dumpthis->size, dumpthis->big_endian ? "BIG" : "little"); (void) printf("ino:%d, cpu:%d\n", dumpthis->intr_ino, dumpthis->intr_cpu); } #ifdef STANDALONE /* Test program for this module. Useful when implementing new options. */ int main(int argc, char *argv[]) { int status; pcitool_uiargs_t parsed_args; status = get_commandline_args(argc, argv, &parsed_args); if (status) { (void) printf("Error getting command.\n"); } dump_struct(&parsed_args); return (SUCCESS); } #endif /* STANDALONE */ #endif /* DEBUG */