/*
* 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.
*/
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inttypes.h>
#include <sys/types.h>
#include <sys/param.h>
#include <strings.h>
#include <errno.h>
#include <sys/pci.h>
#include <sys/pci_tools.h>
#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)
{
typedef enum {
bank = 0,
base
} nexus_opts_index_t;
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) */
typedef enum {
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
} bdf_opts_index_t;
/* 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 <cpu#,ino#>.\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)
{
typedef enum {
bus = 0,
dev = 1,
func = 2,
bdf = 3
} p1_bdf_opts_index_t;
/* 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 */