#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inttypes.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <fcntl.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <libdevinfo.h>
#include <sys/sunddi.h>
#ifdef __x86
#include <sys/apic_ctlr.h>
#endif
#include <sys/pci.h>
#include <sys/pci_tools.h>
#include "pcitool_ui.h"
typedef union {
uint8_t bytes[16 * sizeof (uint32_t)];
uint32_t dwords[16];
} pci_conf_hdr_t;
typedef struct {
uint16_t cfg_offset;
uint8_t size;
char *abbrev_hdr;
char *full_hdr;
} field_type_t;
typedef struct {
pcitool_uiargs_t *input_args_p;
char *pathname;
di_prom_handle_t di_phdl;
} probe_walk_args_t;
#if defined(__sparc)
#define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_BIG
#elif defined(__x86)
#define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_LTL
#else
#error "ISA is neither __sparc nor __x86"
#endif
static struct {
pcitool_errno_t value;
char *string;
} pcitool_stat_str[] = {
{ PCITOOL_SUCCESS,
"No error status returned from driver" },
{ PCITOOL_INVALID_CPUID,
"CPU is non-existent or not online" },
{ PCITOOL_INVALID_INO,
"INO is out of range or invalid" },
{ PCITOOL_INVALID_MSI,
"MSI is out of range or invalid" },
{ PCITOOL_PENDING_INTRTIMEOUT,
"Timeout waiting for pending interrupts to clear" },
{ PCITOOL_REGPROP_NOTWELLFORMED,
"Reg property has invalid format" },
{ PCITOOL_INVALID_ADDRESS,
"Address out of range or invalid" },
{ PCITOOL_NOT_ALIGNED,
"Improper address alignment for access attempted" },
{ PCITOOL_OUT_OF_RANGE,
"Argument out of range" },
{ PCITOOL_END_OF_RANGE,
"End of address range" },
{ PCITOOL_ROM_DISABLED,
"Device ROM is disabled. Cannot read" },
{ PCITOOL_ROM_WRITE,
"Write to ROM not allowed" },
{ PCITOOL_IO_ERROR,
"IO error encountered" },
{ PCITOOL_INVALID_SIZE,
"Size is invalid for this platform" },
{ 0, NULL }
};
static boolean_t keep_looping = B_TRUE;
static void signal_handler(int dummy);
static char *strstatus(pcitool_errno_t pcitool_status);
static int open_node(char *device, pcitool_uiargs_t *input_args_p);
static void print_probe_value(pci_conf_hdr_t *config_hdr_p, uint16_t offset,
uint8_t size);
static void print_probe_info_verbose(pci_conf_hdr_t *config_hdr_p,
pcitool_reg_t *info_p);
static void print_probe_info_nonverbose(pci_conf_hdr_t *config_hdr_p,
pcitool_reg_t *info_p);
static void print_probe_info(pci_conf_hdr_t *config_hdr_p,
pcitool_reg_t *info_p, boolean_t verbose);
static int get_config_header(int fd, uint8_t bus_no, uint8_t dev_no,
uint8_t func_no, pci_conf_hdr_t *config_hdr_p);
static int supports_ari(int fd, uint8_t bus_no);
static int probe_dev(int fd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p);
static int do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl,
pcitool_uiargs_t *input_args_p);
static int process_nexus_node(di_node_t node, di_minor_t minor, void *arg);
static int do_probe_walk(pcitool_uiargs_t *input_args_p, char *pathname);
static void print_bytedump_header(boolean_t do_chardump);
static int bytedump_get(int fd, int cmd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p);
static uint32_t set_acc_attr(pcitool_uiargs_t *input_args_p);
static int do_single_access(int fd, int cmd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p);
static int do_device_or_nexus(int fd, pcitool_uiargs_t *input_args_p);
static void print_intr_info(pcitool_intr_get_t *iget_p);
static int get_single_interrupt(int fd, pcitool_intr_get_t **iget_pp,
pcitool_uiargs_t *input_args_p);
static int get_interrupts(int fd, pcitool_uiargs_t *input_args_p);
static int set_interrupts(int fd, pcitool_uiargs_t *input_args_p);
static int do_interrupts(int fd, pcitool_uiargs_t *input_args_p);
static void
signal_handler(int dummy)
{
keep_looping = B_FALSE;
}
static char *
strstatus(pcitool_errno_t pcitool_status)
{
int i;
for (i = 0; pcitool_stat_str[i].string != NULL; i++) {
if (pcitool_stat_str[i].value == pcitool_status) {
return (pcitool_stat_str[i].string);
}
}
return ("Unknown status returned from driver.");
}
static int
open_node(char *device, pcitool_uiargs_t *input_args_p)
{
int fd;
char *path;
int stringsize;
char *prefix;
char *suffix;
char *format;
static char slash_devices[] = {"/devices"};
static char wcolon[] = {"%s%s:%s"};
static char wocolon[] = {"%s%s%s"};
prefix = (strstr(device, slash_devices) == device) ? "" : slash_devices;
format = wcolon;
if (input_args_p->flags & INTR_FLAG) {
if (strstr(device, PCI_MINOR_INTR) ==
device + (strlen(device) - strlen(PCI_MINOR_INTR))) {
suffix = "";
format = wocolon;
} else {
suffix = PCI_MINOR_INTR;
}
} else {
if (strstr(device, PCI_MINOR_REG) ==
device + (strlen(device) - strlen(PCI_MINOR_REG))) {
suffix = "";
format = wocolon;
} else {
suffix = PCI_MINOR_REG;
}
}
stringsize = strlen(prefix) + strlen(device) + strlen(suffix) + 2;
path = malloc(stringsize);
(void) snprintf(path, stringsize, format, prefix, device, suffix);
if ((fd = open(path, O_RDWR)) == -1) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"Could not open nexus node %s: %s\n",
path, strerror(errno));
}
}
return (fd);
}
static field_type_t first_fields[] = {
{ PCI_CONF_VENID, 2, "Vend", "Vendor ID" },
{ PCI_CONF_DEVID, 2, "Dev ", "Device ID" },
{ PCI_CONF_COMM, 2, "Cmd ", "Command" },
{ PCI_CONF_STAT, 2, "Stat", "Status" },
{ PCI_CONF_REVID, 1, "Rv", "Revision ID" },
{ PCI_CONF_PROGCLASS, 3, "Class ", "Class Code" },
{ PCI_CONF_CACHE_LINESZ, 1, "Ca", "Cache Line Size" },
{ PCI_CONF_LATENCY_TIMER, 1, "LT", "Latency Timer" },
{ PCI_CONF_HEADER, 1, "Hd", "Header Type" },
{ PCI_CONF_BIST, 1, "BI", "BIST" },
{ 0, 0, NULL, NULL }
};
static field_type_t last_dev_fields[] = {
{ PCI_CONF_BASE0, 4, "BAR0", "Base Address Register 0 (@10)" },
{ PCI_CONF_BASE1, 4, "BAR1", "Base Address Register 1 (@14)" },
{ PCI_CONF_BASE2, 4, "BAR2", "Base Address Register 2 (@18)" },
{ PCI_CONF_BASE3, 4, "BAR3", "Base Address Register 3 (@1C)" },
{ PCI_CONF_BASE4, 4, "BAR4", "Base Address Register 4 (@20)" },
{ PCI_CONF_BASE5, 4, "BAR5", "Base Address Register 5 (@24)" },
{ PCI_CONF_ROM, 4, "ROM", "Expansion ROM Base Address Register (@30)" },
{ 0, 0, NULL, NULL }
};
static field_type_t last_pcibrg_fields[] = {
{ PCI_CONF_BASE0, 4, "BAR0", "Base Address Register 0 (@10)" },
{ PCI_CONF_BASE1, 4, "BAR1", "Base Address Register 1 (@14)" },
{ PCI_BCNF_ROM, 4, "ROM", "Expansion ROM Base Address Register (@38)" },
{ 0, 0, NULL, NULL }
};
static field_type_t last_cbbrg_fields[] = {
{ PCI_CBUS_SOCK_REG, 4, "SCKT", "Socket/ExCA Base Address (@10)" },
{ 0, 0, NULL, NULL }
};
#define FMT_SIZE 7
static void
print_probe_value(pci_conf_hdr_t *config_hdr_p, uint16_t offset, uint8_t size)
{
char format[FMT_SIZE];
uint32_t value = 0;
(void) snprintf(format, FMT_SIZE, "%%%d.%dx ", size * 2, size * 2);
while (size-- > 0) {
value = (value << 8) + config_hdr_p->bytes[offset + size];
}
(void) printf(format, value);
}
static void
print_probe_info_verbose(pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p)
{
field_type_t *last_fields = NULL;
int i;
(void) printf("\n"
"Bus Number: %x Device Number: %x Function Number: %x\n",
info_p->bus_no, info_p->dev_no, info_p->func_no);
if (info_p->phys_addr != 0) {
(void) printf("Physical Address: 0x%" PRIx64 " \n",
info_p->phys_addr);
}
switch (config_hdr_p->bytes[PCI_CONF_HEADER] & PCI_HEADER_TYPE_M) {
case PCI_HEADER_ZERO:
last_fields = last_dev_fields;
break;
case PCI_HEADER_PPB:
last_fields = last_pcibrg_fields;
(void) printf("PCI-PCI bridge\n");
break;
case PCI_HEADER_CARDBUS:
last_fields = last_cbbrg_fields;
(void) printf("PCI-Cardbus bridge\n");
break;
default:
(void) printf("Unknown device\n");
break;
}
if (last_fields != NULL) {
for (i = 0; first_fields[i].size != 0; i++) {
(void) printf("%s: ", first_fields[i].full_hdr);
print_probe_value(config_hdr_p,
first_fields[i].cfg_offset, first_fields[i].size);
(void) putchar('\n');
}
for (i = 0; last_fields[i].size != 0; i++) {
(void) printf("%s: ", last_fields[i].full_hdr);
print_probe_value(config_hdr_p,
last_fields[i].cfg_offset, last_fields[i].size);
(void) putchar('\n');
}
}
}
static void
print_probe_info_nonverbose(pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p)
{
int i;
(void) printf("%2.2x %2.2x %1.1x ",
info_p->bus_no, info_p->dev_no, info_p->func_no);
for (i = 0; first_fields[i].size != 0; i++) {
print_probe_value(config_hdr_p,
first_fields[i].cfg_offset, first_fields[i].size);
}
(void) putchar('\n');
}
static void
print_probe_info(
pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p, boolean_t verbose)
{
int i;
if (config_hdr_p == NULL) {
if (!verbose) {
(void) printf("B D F ");
for (i = 0; first_fields[i].size != 0; i++) {
(void) printf("%s ",
first_fields[i].abbrev_hdr);
}
(void) putchar('\n');
}
return;
}
if (verbose) {
print_probe_info_verbose(config_hdr_p, info_p);
} else {
print_probe_info_nonverbose(config_hdr_p, info_p);
}
}
static int
get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no,
pci_conf_hdr_t *config_hdr_p)
{
pcitool_reg_t cfg_prg;
int i;
int rval = SUCCESS;
cfg_prg.offset = 0;
cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
cfg_prg.bus_no = bus_no;
cfg_prg.dev_no = dev_no;
cfg_prg.func_no = func_no;
cfg_prg.barnum = 0;
cfg_prg.user_version = PCITOOL_VERSION;
for (i = 1; i < (sizeof (pci_conf_hdr_t) / sizeof (uint32_t)); i++) {
cfg_prg.offset += sizeof (uint32_t);
if ((rval =
ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != SUCCESS) {
break;
}
config_hdr_p->dwords[i] = (uint32_t)cfg_prg.data;
}
return (rval);
}
static int
supports_ari(int fd, uint8_t bus_no)
{
pcitool_reg_t cfg_prg;
int deadcount = 0;
uint32_t data, hdr_next_ptr, hdr_cap_id;
uint8_t dev_no = 0;
uint8_t func_no = 0;
cfg_prg.bus_no = bus_no;
cfg_prg.dev_no = dev_no;
cfg_prg.func_no = func_no;
cfg_prg.barnum = 0;
cfg_prg.user_version = PCITOOL_VERSION;
cfg_prg.offset = 0;
cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + PCITOOL_ACC_ATTR_ENDN_LTL;
if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
return (FAILURE);
}
data = (uint32_t)cfg_prg.data;
if (data == (uint32_t)(-1))
return (FAILURE);
cfg_prg.offset = PCI_CONF_COMM;
if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
return (FAILURE);
}
data = (uint32_t)cfg_prg.data;
if (!((data >> 16) & PCI_STAT_CAP))
return (FAILURE);
cfg_prg.offset = PCI_CONF_CAP_PTR;
if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
return (FAILURE);
}
data = (uint32_t)cfg_prg.data;
hdr_next_ptr = data & 0xff;
hdr_cap_id = 0;
while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
(hdr_cap_id != PCI_CAP_ID_PCI_E)) {
if (hdr_next_ptr < 0x40)
break;
cfg_prg.offset = hdr_next_ptr;
if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS)
return (FAILURE);
data = (uint32_t)cfg_prg.data;
hdr_next_ptr = (data >> 8) & 0xFF;
hdr_cap_id = data & 0xFF;
if (deadcount++ > 100)
return (FAILURE);
}
if (hdr_cap_id != PCI_CAP_ID_PCI_E)
return (FAILURE);
hdr_next_ptr = 0x100;
hdr_cap_id = 0;
while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
(hdr_cap_id != 0xe)) {
if (hdr_next_ptr < 0x40)
break;
cfg_prg.offset = hdr_next_ptr;
if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) {
return (FAILURE);
}
data = (uint32_t)cfg_prg.data;
hdr_next_ptr = (data >> 20) & 0xFFF;
hdr_cap_id = data & 0xFFFF;
if (deadcount++ > 100)
return (FAILURE);
}
if (hdr_cap_id != 0xe)
return (FAILURE);
return (SUCCESS);
}
#if (NATIVE_ENDIAN == PCITOOL_ACC_ATTR_ENDN_BIG)
#define U45_SB_DEVID_VID 0xb9104952
#define U45_SB_CLASS_RID 0x00000406
#else
#define U45_SB_DEVID_VID 0x524910b9
#define U45_SB_CLASS_RID 0x06040000
#endif
static int
probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p)
{
pci_conf_hdr_t config_hdr;
boolean_t multi_function_device = B_FALSE;
int func;
int first_func = 0;
int last_func = PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT;
int rval = SUCCESS;
if (input_args_p->flags & FUNC_SPEC_FLAG) {
first_func = last_func = input_args_p->function;
} else if (supports_ari(fd, prg_p->bus_no) == SUCCESS) {
multi_function_device = B_TRUE;
if (!(input_args_p->flags & DEV_SPEC_FLAG))
last_func = 255;
}
for (func = first_func; ((func <= last_func) &&
((func == first_func) || (multi_function_device)));
func++) {
if (last_func > 7) {
prg_p->func_no = func & 0x7;
prg_p->dev_no = (func >> 3) & 0x1f;
} else
prg_p->func_no = func;
prg_p->status = PCITOOL_SUCCESS;
prg_p->offset = 0;
prg_p->data = 0;
prg_p->user_version = PCITOOL_VERSION;
if (((rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) ||
(prg_p->data == 0xffffffff)) {
if ((errno == EINVAL) ||
(prg_p->status == PCITOOL_OUT_OF_RANGE)) {
break;
}
else if ((errno == ENXIO) &&
(prg_p->status == PCITOOL_IO_ERROR)) {
break;
}
else if (((errno != EFAULT) ||
(prg_p->status != PCITOOL_INVALID_ADDRESS)) &&
(prg_p->data != 0xffffffff)) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"Ioctl error: %s\n",
strerror(errno));
}
break;
} else {
rval = SUCCESS;
}
} else if (prg_p->data == 0) {
rval = SUCCESS;
} else {
config_hdr.dwords[0] = (uint32_t)prg_p->data;
if ((rval = get_config_header(fd, prg_p->bus_no,
prg_p->dev_no, prg_p->func_no, &config_hdr)) !=
SUCCESS) {
break;
}
print_probe_info(&config_hdr, prg_p,
IS_VERBOSE(input_args_p->flags));
if ((config_hdr.dwords[0] == U45_SB_DEVID_VID) &&
(config_hdr.dwords[2] == U45_SB_CLASS_RID)) {
rval = ECANCELED;
break;
}
if (config_hdr.bytes[PCI_CONF_HEADER] &
PCI_HEADER_MULTI) {
multi_function_device = B_TRUE;
}
}
}
return (rval);
}
static int
do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl,
pcitool_uiargs_t *input_args_p)
{
pcitool_reg_t prg;
int bus;
int dev;
int last_bus = PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT;
int last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT;
int first_bus = 0;
int first_dev = 0;
int rval = SUCCESS;
prg.barnum = 0;
prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
prg.data = 0;
if (input_args_p->flags & BUS_SPEC_FLAG) {
first_bus = last_bus = input_args_p->bus;
} else if (input_args_p->flags & PROBERNG_FLAG) {
int len;
uint32_t *rangebuf = NULL;
len = di_prop_lookup_ints(DDI_DEV_T_ANY, di_node,
"bus-range", (int **)&rangebuf);
if (len <= 0) {
len = di_prom_prop_lookup_ints(di_phdl, di_node,
"bus-range", (int **)&rangebuf);
}
if (len > 0) {
first_bus = rangebuf[0];
last_bus = rangebuf[1];
}
}
if (last_bus == first_bus) {
if (input_args_p->flags & DEV_SPEC_FLAG) {
(void) puts("");
} else {
(void) printf("*********** Probing bus %x "
"***********\n\n", first_bus);
}
} else {
(void) printf("*********** Probing buses %x through %x "
"***********\n\n", first_bus, last_bus);
}
print_probe_info(NULL, NULL, IS_VERBOSE(input_args_p->flags));
if (input_args_p->flags & DEV_SPEC_FLAG) {
first_dev = last_dev = input_args_p->device;
}
for (bus = first_bus; ((bus <= last_bus) && (rval == SUCCESS)); bus++) {
prg.bus_no = bus;
if (input_args_p->flags & DEV_SPEC_FLAG) {
first_dev = last_dev = input_args_p->device;
} else if (supports_ari(fd, bus) == SUCCESS) {
last_dev = 0;
first_dev = 0;
} else {
last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT;
}
for (dev = first_dev;
((dev <= last_dev) && (rval == SUCCESS)); dev++) {
prg.dev_no = dev;
rval = probe_dev(fd, &prg, input_args_p);
}
if (rval == ECANCELED) {
rval = SUCCESS;
}
}
return (rval);
}
static int
process_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
{
int fd;
char *trunc;
probe_walk_args_t *walk_args_p = (probe_walk_args_t *)arg;
char *pathname = walk_args_p->pathname;
char *nexus_path = di_devfs_minor_path(minor);
if (nexus_path == NULL) {
(void) fprintf(stderr, "Error getting nexus path: %s\n",
strerror(errno));
return (DI_WALK_CONTINUE);
}
if ((pathname != NULL) &&
((strstr(nexus_path, pathname) != nexus_path) ||
(strlen(nexus_path) !=
(strlen(pathname) + strlen(PCI_MINOR_REG) + 1)))) {
di_devfs_path_free(nexus_path);
return (DI_WALK_CONTINUE);
}
if ((fd = open_node(nexus_path, walk_args_p->input_args_p)) >= 0) {
if ((trunc = strstr(nexus_path, PCI_MINOR_REG)) != NULL) {
trunc--;
*trunc = '\0';
}
(void) puts("");
if (pathname == NULL) {
(void) printf("********** Devices in tree under %s "
"**********\n", nexus_path);
}
if ((do_probe(fd, di_node, walk_args_p->di_phdl,
walk_args_p->input_args_p) != SUCCESS) &&
(errno != ENXIO)) {
(void) fprintf(stderr, "Error probing node %s: %s\n",
nexus_path, strerror(errno));
}
(void) close(fd);
}
di_devfs_path_free(nexus_path);
return ((pathname == NULL) ? DI_WALK_CONTINUE : DI_WALK_TERMINATE);
}
static int
do_probe_walk(pcitool_uiargs_t *input_args_p, char *pathname)
{
di_node_t di_node;
di_prom_handle_t di_phdl = DI_PROM_HANDLE_NIL;
probe_walk_args_t walk_args;
int rval = SUCCESS;
if ((di_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
(void) fprintf(stderr, "di_init() failed: %s\n",
strerror(errno));
rval = errno;
} else if ((input_args_p->flags & PROBERNG_FLAG) &&
((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)) {
(void) fprintf(stderr, "di_prom_init failed: %s\n",
strerror(errno));
rval = errno;
} else {
walk_args.input_args_p = input_args_p;
walk_args.di_phdl = di_phdl;
walk_args.pathname = pathname;
(void) di_walk_minor(di_node, DDI_NT_REGACC, 0,
&walk_args, process_nexus_node);
}
if (di_phdl != DI_PROM_HANDLE_NIL) {
di_prom_fini(di_phdl);
}
if (di_node != DI_NODE_NIL) {
di_fini(di_node);
}
return (rval);
}
static void
print_bytedump_header(boolean_t do_chardump)
{
static char header1[] = {" "
"0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00"};
static char header2[] = {" "
"-----------------------------------------------"};
static char cheader1[] = {" 0123456789ABCDEF"};
static char cheader2[] = {" ----------------"};
(void) puts("");
(void) printf(header1);
if (do_chardump) {
(void) printf(cheader1);
}
(void) puts("");
(void) printf(header2);
if (do_chardump) {
(void) printf(cheader2);
}
}
#define DUMP_BUF_SIZE 16
#define LINES_BTWN_HEADER 16
static int
bytedump_get(int fd, int cmd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p)
{
typedef union {
uint8_t bytes[DUMP_BUF_SIZE];
uint16_t shorts[DUMP_BUF_SIZE / sizeof (uint16_t)];
uint32_t dwords[DUMP_BUF_SIZE / sizeof (uint32_t)];
uint64_t longs[DUMP_BUF_SIZE / sizeof (uint64_t)];
} buffer_t;
pcitool_reg_t local_prg;
uint32_t dump_end = prg_p->offset + input_args_p->bytedump_amt;
uint32_t dump_curr = prg_p->offset;
int read_size = input_args_p->size;
int wrap_size = DUMP_BUF_SIZE / read_size;
uint64_t print_addr = 0;
int skip_begin;
int skip_end = 0;
int skip_begin2;
int skip_end2;
int lines_since_header = 0;
boolean_t do_chardump = input_args_p->flags & CHARDUMP_FLAG;
boolean_t continue_on_errs = input_args_p->flags & ERRCONT_FLAG;
int rval = SUCCESS;
int next;
int i;
buffer_t buffer;
uint16_t error_mask = 0;
bzero(buffer.bytes, sizeof (uint8_t) * DUMP_BUF_SIZE);
local_prg = *prg_p;
#if (NATIVE_ENDIAN == PCITOOL_ACC_ATTR_ENDN_BIG)
local_prg.acc_attr =
(PCITOOL_ACC_IS_BIG_ENDIAN(local_prg.acc_attr) ?
(local_prg.acc_attr & ~PCITOOL_ACC_ATTR_ENDN_BIG) :
(local_prg.acc_attr | PCITOOL_ACC_ATTR_ENDN_BIG));
#endif
skip_begin = local_prg.offset % DUMP_BUF_SIZE;
next = skip_begin / read_size;
print_bytedump_header(do_chardump);
while (dump_curr < dump_end) {
local_prg.offset = dump_curr;
if (((rval = ioctl(fd, cmd, &local_prg)) != SUCCESS) &&
(!(continue_on_errs))) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"Ioctl failed:\n errno: %s\n status: %s\n",
strerror(errno),
strstatus(local_prg.status));
}
break;
}
if (print_addr == 0) {
if (local_prg.phys_addr == 0) {
print_addr = local_prg.offset -
(local_prg.offset % DUMP_BUF_SIZE);
} else {
print_addr = local_prg.phys_addr -
(local_prg.phys_addr % DUMP_BUF_SIZE);
}
}
if (rval != SUCCESS) {
error_mask |=
((1 << read_size) - 1) << (next * read_size);
} else {
switch (read_size) {
case 1:
buffer.bytes[next] = (uint8_t)local_prg.data;
break;
case 2:
buffer.shorts[next] = (uint16_t)local_prg.data;
break;
case 4:
buffer.dwords[next] = (uint32_t)local_prg.data;
break;
case 8:
buffer.longs[next] = (uint64_t)local_prg.data;
break;
default:
rval = EIO;
break;
}
}
next++;
next %= wrap_size;
dump_curr += read_size;
if (dump_curr >= dump_end) {
if (next != 0) {
bzero(&buffer.bytes[next * read_size],
(wrap_size - next) * read_size);
skip_end = (wrap_size - next) * read_size;
next = 0;
}
}
if (next == 0) {
skip_begin2 = skip_begin;
skip_end2 = skip_end;
(void) printf("\n0x%16.16" PRIx64 ":", print_addr);
for (i = DUMP_BUF_SIZE - 1; i >= 0; i--) {
if (skip_end) {
skip_end--;
(void) printf(" --");
} else if (skip_begin > i) {
skip_begin--;
(void) printf(" --");
} else if (error_mask & (1 << i)) {
(void) printf(" XX");
} else {
(void) printf(" %2.2x",
buffer.bytes[i]);
}
}
if (do_chardump) {
(void) putchar(' ');
for (i = 0; i < DUMP_BUF_SIZE; i++) {
if (skip_begin2) {
skip_begin2--;
(void) printf("-");
} else if (
(DUMP_BUF_SIZE - skip_end2) <= i) {
(void) printf("-");
} else if (error_mask & (1 << i)) {
(void) putchar('X');
} else if (isprint(buffer.bytes[i])) {
(void) putchar(buffer.bytes[i]);
} else {
(void) putchar('@');
}
}
}
if ((++lines_since_header == LINES_BTWN_HEADER) &&
(dump_curr < dump_end)) {
lines_since_header = 0;
(void) puts("");
print_bytedump_header(do_chardump);
}
print_addr += DUMP_BUF_SIZE;
error_mask = 0;
}
}
(void) printf("\n");
return (rval);
}
static uint32_t
set_acc_attr(pcitool_uiargs_t *input_args_p)
{
uint32_t access_attrs;
switch (input_args_p->size) {
case 1:
access_attrs = PCITOOL_ACC_ATTR_SIZE_1;
break;
case 2:
access_attrs = PCITOOL_ACC_ATTR_SIZE_2;
break;
case 4:
access_attrs = PCITOOL_ACC_ATTR_SIZE_4;
break;
case 8:
access_attrs = PCITOOL_ACC_ATTR_SIZE_8;
break;
}
if (input_args_p->big_endian) {
access_attrs |= PCITOOL_ACC_ATTR_ENDN_BIG;
}
return (access_attrs);
}
static int
do_single_access(int fd, int cmd, pcitool_reg_t *prg_p,
pcitool_uiargs_t *input_args_p)
{
boolean_t is_write = B_FALSE;
int rval;
switch (cmd) {
case PCITOOL_NEXUS_SET_REG:
case PCITOOL_DEVICE_SET_REG:
is_write = B_TRUE;
break;
default:
break;
}
if ((rval = ioctl(fd, cmd, prg_p)) != SUCCESS) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"%s ioctl failed:\n errno: %s\n status: %s\n",
is_write ? "write" : "read",
strerror(errno), strstatus(prg_p->status));
}
return (rval);
}
if (IS_VERBOSE(input_args_p->flags)) {
if (prg_p->phys_addr == 0)
prg_p->phys_addr = input_args_p->offset;
(void) printf("Addr:0x%" PRIx64 ", %d-byte %s endian "
"register value: 0x%" PRIx64 "\n",
prg_p->phys_addr, input_args_p->size,
(input_args_p->big_endian ? "big" : "little"), prg_p->data);
} else if (!(is_write)) {
(void) printf("0x%" PRIx64 "\n", prg_p->data);
}
return (rval);
}
static int
do_device_or_nexus(int fd, pcitool_uiargs_t *input_args_p)
{
pcitool_reg_t prg;
uint32_t write_cmd = 0;
uint32_t read_cmd = 0;
int rval = SUCCESS;
if (input_args_p->flags & WRITE_FLAG) {
prg.data = input_args_p->write_value;
if (input_args_p->flags & NEXUS_FLAG) {
write_cmd = PCITOOL_NEXUS_SET_REG;
} else {
write_cmd = PCITOOL_DEVICE_SET_REG;
}
}
if (input_args_p->flags & READ_FLAG) {
if (input_args_p->flags & NEXUS_FLAG) {
read_cmd = PCITOOL_NEXUS_GET_REG;
} else {
read_cmd = PCITOOL_DEVICE_GET_REG;
}
}
if ((input_args_p->flags & (BASE_SPEC_FLAG | NEXUS_FLAG)) ==
(BASE_SPEC_FLAG | NEXUS_FLAG)) {
prg.barnum = PCITOOL_BASE;
prg.phys_addr = input_args_p->base_address;
} else
prg.barnum = input_args_p->bank;
prg.offset = input_args_p->offset;
prg.acc_attr = set_acc_attr(input_args_p);
prg.bus_no = input_args_p->bus;
prg.dev_no = input_args_p->device;
prg.func_no = input_args_p->function;
prg.user_version = PCITOOL_VERSION;
do {
if (input_args_p->flags & BYTEDUMP_FLAG) {
if (IS_VERBOSE(input_args_p->flags)) {
(void) printf(
"\nDoing %d-byte %s endian reads:",
input_args_p->size,
input_args_p->big_endian ?
"big" : "little");
}
rval = bytedump_get(fd, read_cmd, &prg, input_args_p);
} else {
if (write_cmd != 0) {
rval = do_single_access(
fd, write_cmd, &prg, input_args_p);
}
if ((rval == SUCCESS) && (read_cmd != 0)) {
rval = do_single_access(
fd, read_cmd, &prg, input_args_p);
}
}
} while ((IS_LOOP(input_args_p->flags)) && (rval == SUCCESS) &&
(keep_looping));
return (rval != SUCCESS ? errno : SUCCESS);
}
static void
print_intr_info(pcitool_intr_get_t *iget_p)
{
int i;
for (i = 0; i < iget_p->num_devs; i++) {
if (iget_p->flags & PCITOOL_INTR_FLAG_GET_MSI)
(void) printf("0x%x,0x%x: %-10s%d\t %s\n",
iget_p->cpu_id, iget_p->msi & 0xff,
iget_p->dev[i].driver_name, iget_p->dev[i].dev_inst,
iget_p->dev[i].path);
else
(void) printf("0x%x,0x%x: %-10s%d\t %s\n",
iget_p->cpu_id, iget_p->ino & 0xff,
iget_p->dev[i].driver_name, iget_p->dev[i].dev_inst,
iget_p->dev[i].path);
}
}
static int
get_single_interrupt(int fd, pcitool_intr_get_t **iget_pp,
pcitool_uiargs_t *input_args_p)
{
pcitool_intr_get_t *iget_p = *iget_pp;
const char *str_type = NULL;
uint32_t intr;
if (input_args_p->flags & MSI_SPEC_FLAG) {
intr = input_args_p->intr_msi;
str_type = "msi";
} else {
intr = input_args_p->intr_ino;
str_type = "ino";
}
if (ioctl(fd, PCITOOL_DEVICE_GET_INTR, iget_p) != 0) {
if (errno == EIO) {
return (SUCCESS);
}
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr, "Ioctl to get %s 0x%x "
"info failed: %s\n", str_type, intr,
strerror(errno));
if (errno != EFAULT) {
(void) fprintf(stderr, "Pcitool status: %s\n",
strstatus(iget_p->status));
}
}
return (errno);
}
if (iget_p->num_devs == 0) {
return (SUCCESS);
}
if (iget_p->num_devs_ret < iget_p->num_devs) {
iget_p = *iget_pp =
realloc(iget_p, PCITOOL_IGET_SIZE(iget_p->num_devs));
iget_p->num_devs_ret = iget_p->num_devs;
if (ioctl(fd, PCITOOL_DEVICE_GET_INTR, iget_p) != 0) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr, "Ioctl to get %s 0x%x"
"device info failed: %s\n", str_type,
intr, strerror(errno));
if (errno != EFAULT) {
(void) fprintf(stderr,
"Pcitool status: %s\n",
strstatus(iget_p->status));
}
}
return (errno);
}
}
print_intr_info(iget_p);
return (SUCCESS);
}
#define INIT_NUM_DEVS 0
static int
get_interrupts(int fd, pcitool_uiargs_t *input_args_p)
{
int rval = SUCCESS;
int ino, cpu_id;
pcitool_intr_get_t *iget_p = malloc(PCITOOL_IGET_SIZE(INIT_NUM_DEVS));
iget_p->num_devs_ret = INIT_NUM_DEVS;
iget_p->user_version = PCITOOL_VERSION;
if (input_args_p->flags & MSI_SPEC_FLAG) {
iget_p->msi = input_args_p->intr_msi;
iget_p->flags = PCITOOL_INTR_FLAG_GET_MSI;
rval = get_single_interrupt(fd, &iget_p, input_args_p);
} else if (input_args_p->flags & MSI_ALL_FLAG) {
pcitool_intr_info_t intr_info;
intr_info.flags = PCITOOL_INTR_FLAG_GET_MSI;
if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"intr info ioctl failed: %s\n",
strerror(errno));
}
} else {
int msi;
for (msi = 0;
((msi < intr_info.num_intr) && (rval == SUCCESS));
msi++) {
bzero(iget_p, sizeof (pcitool_intr_get_t));
iget_p->num_devs_ret = INIT_NUM_DEVS;
iget_p->user_version = PCITOOL_VERSION;
iget_p->flags = PCITOOL_INTR_FLAG_GET_MSI;
iget_p->msi = msi;
rval = get_single_interrupt(
fd, &iget_p, input_args_p);
}
}
} else if (input_args_p->flags & INO_SPEC_FLAG) {
iget_p->ino = input_args_p->intr_ino;
iget_p->cpu_id = input_args_p->old_cpu;
rval = get_single_interrupt(fd, &iget_p, input_args_p);
} else if (input_args_p->flags & INO_ALL_FLAG) {
pcitool_intr_info_t intr_info;
intr_info.flags = 0;
if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"intr info ioctl failed: %s\n",
strerror(errno));
}
free(iget_p);
return (rval);
}
if (intr_info.ctlr_type == PCITOOL_CTLR_TYPE_APIX) {
for (cpu_id = 0;
((cpu_id < intr_info.num_cpu) && (rval == SUCCESS));
cpu_id++) {
for (ino = 0;
((ino < intr_info.num_intr) &&
(rval == SUCCESS));
ino++) {
bzero(iget_p,
sizeof (pcitool_intr_get_t));
iget_p->num_devs_ret = INIT_NUM_DEVS;
iget_p->user_version = PCITOOL_VERSION;
iget_p->cpu_id = cpu_id;
iget_p->ino = ino;
rval = get_single_interrupt(
fd, &iget_p, input_args_p);
}
}
} else {
for (ino = 0;
(ino < intr_info.num_intr) && (rval == SUCCESS);
ino++) {
bzero(iget_p,
sizeof (pcitool_intr_get_t));
iget_p->num_devs_ret = INIT_NUM_DEVS;
iget_p->user_version = PCITOOL_VERSION;
iget_p->cpu_id = input_args_p->old_cpu;
iget_p->ino = ino;
rval = get_single_interrupt(
fd, &iget_p, input_args_p);
}
}
}
free(iget_p);
return (rval);
}
static int
get_interrupt_ctlr(int fd, pcitool_uiargs_t *input_args_p)
{
pcitool_intr_info_t intr_info;
char *ctlr_type = NULL;
int rval = SUCCESS;
intr_info.flags = 0;
if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) perror("Ioctl to get intr ctlr info failed");
}
rval = errno;
} else {
(void) fputs("Controller type: ", stdout);
switch (intr_info.ctlr_type) {
case PCITOOL_CTLR_TYPE_RISC:
ctlr_type = "RISC";
break;
case PCITOOL_CTLR_TYPE_UPPC:
ctlr_type = "UPPC";
break;
case PCITOOL_CTLR_TYPE_PCPLUSMP:
ctlr_type = "PCPLUSMP";
break;
case PCITOOL_CTLR_TYPE_APIX:
ctlr_type = "APIX";
break;
default:
break;
}
if (ctlr_type == NULL) {
(void) printf("Unknown or new (%d)",
intr_info.ctlr_type);
} else {
(void) fputs(ctlr_type, stdout);
}
#ifdef __x86
if (intr_info.ctlr_type == PCITOOL_CTLR_TYPE_PCPLUSMP)
(void) printf(", IO APIC version: 0x%x, "
"local APIC version: 0x%x\n",
PSMAT_IO_APIC_VER(intr_info.ctlr_version),
PSMAT_LOCAL_APIC_VER(intr_info.ctlr_version));
else
#endif
(void) printf(", version: %2.2x.%2.2x.%2.2x.%2.2x\n",
((intr_info.ctlr_version >> 24) & 0xff),
((intr_info.ctlr_version >> 16) & 0xff),
((intr_info.ctlr_version >> 8) & 0xff),
(intr_info.ctlr_version & 0xff));
}
return (rval);
}
static int
set_interrupts(int fd, pcitool_uiargs_t *input_args_p)
{
pcitool_intr_set_t iset;
const char *str_type = NULL;
uint32_t intr;
int rval = SUCCESS;
if (input_args_p->flags & MSI_SPEC_FLAG) {
iset.msi = intr = input_args_p->intr_msi;
iset.flags = PCITOOL_INTR_FLAG_SET_MSI;
str_type = "msi";
} else {
iset.ino = intr = input_args_p->intr_ino;
iset.flags = 0;
str_type = "ino";
}
iset.cpu_id = input_args_p->intr_cpu;
iset.old_cpu = input_args_p->old_cpu;
iset.user_version = PCITOOL_VERSION;
iset.flags |= (input_args_p->flags & SETGRP_FLAG) ?
PCITOOL_INTR_FLAG_SET_GROUP : 0;
if (ioctl(fd, PCITOOL_DEVICE_SET_INTR, &iset) != 0) {
if (!(IS_QUIET(input_args_p->flags))) {
(void) fprintf(stderr,
"Ioctl to set %s 0x%x failed: %s\n",
str_type, intr, strerror(errno));
(void) fprintf(stderr, "pcitool status: %s\n",
strstatus(iset.status));
}
rval = errno;
} else {
if (input_args_p->flags & SETGRP_FLAG) {
if (iset.flags == PCITOOL_INTR_FLAG_SET_MSI)
(void) printf("0x%x,0x%x => 0x%x,0x%x\n",
iset.cpu_id,
(input_args_p->intr_msi) & 0xff,
input_args_p->intr_cpu, iset.msi);
else
(void) printf("0x%x,0x%x => 0x%x,0x%x\n",
iset.cpu_id,
(input_args_p->intr_ino) & 0xff,
input_args_p->intr_cpu, iset.ino);
} else {
if (iset.flags == PCITOOL_INTR_FLAG_SET_MSI)
(void) printf("0x%x,0x%x -> 0x%x,0x%x\n",
iset.cpu_id,
(input_args_p->intr_msi) & 0xff,
input_args_p->intr_cpu, iset.msi);
else
(void) printf("0x%x,0x%x -> 0x%x,0x%x\n",
iset.cpu_id,
(input_args_p->intr_ino) & 0xff,
input_args_p->intr_cpu, iset.ino);
}
}
return (rval);
}
static int
do_interrupts(int fd, pcitool_uiargs_t *input_args_p)
{
if (input_args_p->flags & READ_FLAG) {
int gic_rval = SUCCESS;
int gi_rval = SUCCESS;
if (input_args_p->flags & SHOWCTLR_FLAG) {
gic_rval = get_interrupt_ctlr(fd, input_args_p);
}
gi_rval = get_interrupts(fd, input_args_p);
return ((gi_rval != SUCCESS) ? gi_rval : gic_rval);
} else {
return (set_interrupts(fd, input_args_p));
}
}
int
main(int argc, char **argv)
{
pcitool_uiargs_t input_args;
int fd;
int rval = SUCCESS;
if (get_commandline_args(argc, argv, &input_args) != SUCCESS) {
return (EINVAL);
}
if (!(input_args.flags & ALL_COMMANDS))
return (SUCCESS);
if (input_args.flags & PROBE_FLAGS) {
rval = do_probe_walk(&input_args,
((input_args.flags & PROBEALL_FLAG) ? NULL : argv[1]));
} else if ((fd = open_node(argv[1], &input_args)) >= 0) {
if (input_args.flags & (NEXUS_FLAG | LEAF_FLAG)) {
(void) signal(SIGINT, signal_handler);
(void) signal(SIGTERM, signal_handler);
rval = do_device_or_nexus(fd, &input_args);
} else if (input_args.flags & INTR_FLAG) {
rval = do_interrupts(fd, &input_args);
} else {
(void) fprintf(stderr, "Nothing to do.\n");
rval = ENOTTY;
}
(void) close(fd);
}
return (rval);
}