1d5ace945SErwin T Tsaur /* 2d5ace945SErwin T Tsaur * CDDL HEADER START 3d5ace945SErwin T Tsaur * 4d5ace945SErwin T Tsaur * The contents of this file are subject to the terms of the 5d5ace945SErwin T Tsaur * Common Development and Distribution License (the "License"). 6d5ace945SErwin T Tsaur * You may not use this file except in compliance with the License. 7d5ace945SErwin T Tsaur * 8d5ace945SErwin T Tsaur * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9d5ace945SErwin T Tsaur * or http://www.opensolaris.org/os/licensing. 10d5ace945SErwin T Tsaur * See the License for the specific language governing permissions 11d5ace945SErwin T Tsaur * and limitations under the License. 12d5ace945SErwin T Tsaur * 13d5ace945SErwin T Tsaur * When distributing Covered Code, include this CDDL HEADER in each 14d5ace945SErwin T Tsaur * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15d5ace945SErwin T Tsaur * If applicable, add the following below this CDDL HEADER, with the 16d5ace945SErwin T Tsaur * fields enclosed by brackets "[]" replaced with your own identifying 17d5ace945SErwin T Tsaur * information: Portions Copyright [yyyy] [name of copyright owner] 18d5ace945SErwin T Tsaur * 19d5ace945SErwin T Tsaur * CDDL HEADER END 20d5ace945SErwin T Tsaur */ 21d5ace945SErwin T Tsaur /* 22d5ace945SErwin T Tsaur * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23d5ace945SErwin T Tsaur * Use is subject to license terms. 24d5ace945SErwin T Tsaur */ 25d5ace945SErwin T Tsaur 26d5ace945SErwin T Tsaur /* This file is the main module for the pcitool. */ 27d5ace945SErwin T Tsaur 28d5ace945SErwin T Tsaur #include <stdio.h> 29d5ace945SErwin T Tsaur #include <stdlib.h> 30d5ace945SErwin T Tsaur #include <unistd.h> 31d5ace945SErwin T Tsaur #include <sys/inttypes.h> 32d5ace945SErwin T Tsaur #include <signal.h> 33d5ace945SErwin T Tsaur #include <sys/types.h> 34d5ace945SErwin T Tsaur #include <sys/param.h> 35d5ace945SErwin T Tsaur #include <fcntl.h> 36d5ace945SErwin T Tsaur #include <strings.h> 37d5ace945SErwin T Tsaur #include <ctype.h> 38d5ace945SErwin T Tsaur #include <errno.h> 39d5ace945SErwin T Tsaur #include <libdevinfo.h> 40d5ace945SErwin T Tsaur #include <sys/sunddi.h> 41d5ace945SErwin T Tsaur 42d5ace945SErwin T Tsaur #ifdef __x86 43d5ace945SErwin T Tsaur #include <sys/apic_ctlr.h> 44d5ace945SErwin T Tsaur #endif 45d5ace945SErwin T Tsaur 46d5ace945SErwin T Tsaur #include <sys/pci.h> 47d5ace945SErwin T Tsaur #include <sys/pci_tools.h> 48d5ace945SErwin T Tsaur 49d5ace945SErwin T Tsaur #include "pcitool_ui.h" 50d5ace945SErwin T Tsaur 51d5ace945SErwin T Tsaur /* First 16 longs of device PCI config header. */ 52d5ace945SErwin T Tsaur typedef union { 53d5ace945SErwin T Tsaur uint8_t bytes[16 * sizeof (uint32_t)]; 54d5ace945SErwin T Tsaur uint32_t dwords[16]; 55d5ace945SErwin T Tsaur } pci_conf_hdr_t; 56d5ace945SErwin T Tsaur 57d5ace945SErwin T Tsaur /* Used by probe printing functions. */ 58d5ace945SErwin T Tsaur typedef struct { 59d5ace945SErwin T Tsaur uint16_t cfg_offset; /* Offset of data within config space. */ 60d5ace945SErwin T Tsaur uint8_t size; /* Size of desired data field. */ 61d5ace945SErwin T Tsaur char *abbrev_hdr; /* Abbreviated header for this data. */ 62d5ace945SErwin T Tsaur char *full_hdr; /* Full header for this data, verbose option. */ 63d5ace945SErwin T Tsaur } field_type_t; 64d5ace945SErwin T Tsaur 65d5ace945SErwin T Tsaur /* Used to package many args into one arg for probe di_node walk function. */ 66d5ace945SErwin T Tsaur typedef struct { 67d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p; 68d5ace945SErwin T Tsaur char *pathname; 69d5ace945SErwin T Tsaur di_prom_handle_t di_phdl; 70d5ace945SErwin T Tsaur } probe_walk_args_t; 71d5ace945SErwin T Tsaur 72d5ace945SErwin T Tsaur /* 73d5ace945SErwin T Tsaur * Read config space in native processor endianness. Endian-neutral 74d5ace945SErwin T Tsaur * processing can then take place. On big endian machines, MSB and LSB 75d5ace945SErwin T Tsaur * of little endian data end up switched if read as little endian. 76d5ace945SErwin T Tsaur * They are in correct order if read as big endian. 77d5ace945SErwin T Tsaur */ 78d5ace945SErwin T Tsaur #if defined(__sparc) 79d5ace945SErwin T Tsaur #define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_BIG 80d5ace945SErwin T Tsaur #elif defined(__x86) 81d5ace945SErwin T Tsaur #define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_LTL 82d5ace945SErwin T Tsaur #else 83d5ace945SErwin T Tsaur #error "ISA is neither __sparc nor __x86" 84d5ace945SErwin T Tsaur #endif 85d5ace945SErwin T Tsaur 86d5ace945SErwin T Tsaur /* status error lookup table. */ 87d5ace945SErwin T Tsaur static struct { 88d5ace945SErwin T Tsaur pcitool_errno_t value; 89d5ace945SErwin T Tsaur char *string; 90d5ace945SErwin T Tsaur } pcitool_stat_str[] = { 91d5ace945SErwin T Tsaur { PCITOOL_SUCCESS, 92d5ace945SErwin T Tsaur "No error status returned from driver" }, 93d5ace945SErwin T Tsaur { PCITOOL_INVALID_CPUID, 94d5ace945SErwin T Tsaur "CPU is non-existent or not online" }, 95d5ace945SErwin T Tsaur { PCITOOL_INVALID_INO, 96d5ace945SErwin T Tsaur "INO is out of range or invalid" }, 9709b1eac2SEvan Yan { PCITOOL_INVALID_MSI, 9809b1eac2SEvan Yan "MSI is out of range or invalid" }, 99d5ace945SErwin T Tsaur { PCITOOL_PENDING_INTRTIMEOUT, 100d5ace945SErwin T Tsaur "Timeout waiting for pending interrupts to clear" }, 101d5ace945SErwin T Tsaur { PCITOOL_REGPROP_NOTWELLFORMED, 102d5ace945SErwin T Tsaur "Reg property has invalid format" }, 103d5ace945SErwin T Tsaur { PCITOOL_INVALID_ADDRESS, 104d5ace945SErwin T Tsaur "Address out of range or invalid" }, 105d5ace945SErwin T Tsaur { PCITOOL_NOT_ALIGNED, 106d5ace945SErwin T Tsaur "Improper address alignment for access attempted" }, 107d5ace945SErwin T Tsaur { PCITOOL_OUT_OF_RANGE, 108d5ace945SErwin T Tsaur "Argument out of range" }, 109d5ace945SErwin T Tsaur { PCITOOL_END_OF_RANGE, 110d5ace945SErwin T Tsaur "End of address range" }, 111d5ace945SErwin T Tsaur { PCITOOL_ROM_DISABLED, 112d5ace945SErwin T Tsaur "Device ROM is disabled. Cannot read" }, 113d5ace945SErwin T Tsaur { PCITOOL_ROM_WRITE, 114d5ace945SErwin T Tsaur "Write to ROM not allowed" }, 115d5ace945SErwin T Tsaur { PCITOOL_IO_ERROR, 116d5ace945SErwin T Tsaur "IO error encountered" }, 117d5ace945SErwin T Tsaur { PCITOOL_INVALID_SIZE, 118d5ace945SErwin T Tsaur "Size is invalid for this platform" }, 119d5ace945SErwin T Tsaur { 0, NULL } 120d5ace945SErwin T Tsaur }; 121d5ace945SErwin T Tsaur 122d5ace945SErwin T Tsaur 123d5ace945SErwin T Tsaur /* Used with ^C handler to stop looping in repeat mode in do_device_or_nexus. */ 124d5ace945SErwin T Tsaur static boolean_t keep_looping = B_TRUE; 125d5ace945SErwin T Tsaur 126d5ace945SErwin T Tsaur static void signal_handler(int dummy); 127d5ace945SErwin T Tsaur static char *strstatus(pcitool_errno_t pcitool_status); 128d5ace945SErwin T Tsaur static int open_node(char *device, pcitool_uiargs_t *input_args_p); 129d5ace945SErwin T Tsaur static void print_probe_value(pci_conf_hdr_t *config_hdr_p, uint16_t offset, 130d5ace945SErwin T Tsaur uint8_t size); 131d5ace945SErwin T Tsaur static void print_probe_info_verbose(pci_conf_hdr_t *config_hdr_p, 132d5ace945SErwin T Tsaur pcitool_reg_t *info_p); 133d5ace945SErwin T Tsaur static void print_probe_info_nonverbose(pci_conf_hdr_t *config_hdr_p, 134d5ace945SErwin T Tsaur pcitool_reg_t *info_p); 135d5ace945SErwin T Tsaur static void print_probe_info(pci_conf_hdr_t *config_hdr_p, 136d5ace945SErwin T Tsaur pcitool_reg_t *info_p, boolean_t verbose); 137d5ace945SErwin T Tsaur static int get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, 138d5ace945SErwin T Tsaur uint8_t func_no, pci_conf_hdr_t *config_hdr_p); 139*26947304SEvan Yan static int supports_ari(int fd, uint8_t bus_no); 140d5ace945SErwin T Tsaur static int probe_dev(int fd, pcitool_reg_t *prg_p, 141d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p); 142d5ace945SErwin T Tsaur static int do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl, 143d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p); 144d5ace945SErwin T Tsaur static int process_nexus_node(di_node_t node, di_minor_t minor, void *arg); 145d5ace945SErwin T Tsaur static int do_probe_walk(pcitool_uiargs_t *input_args_p, char *pathname); 146d5ace945SErwin T Tsaur static void print_bytedump_header(boolean_t do_chardump); 147d5ace945SErwin T Tsaur static int bytedump_get(int fd, int cmd, pcitool_reg_t *prg_p, 148d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p); 149d5ace945SErwin T Tsaur static uint32_t set_acc_attr(pcitool_uiargs_t *input_args_p); 150d5ace945SErwin T Tsaur static int do_single_access(int fd, int cmd, pcitool_reg_t *prg_p, 151d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p); 152d5ace945SErwin T Tsaur static int do_device_or_nexus(int fd, pcitool_uiargs_t *input_args_p); 153d5ace945SErwin T Tsaur static void print_intr_info(pcitool_intr_get_t *iget_p); 154d5ace945SErwin T Tsaur static int get_single_interrupt(int fd, pcitool_intr_get_t **iget_pp, 155d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p); 156d5ace945SErwin T Tsaur static int get_interrupts(int fd, pcitool_uiargs_t *input_args_p); 157d5ace945SErwin T Tsaur static int set_interrupts(int fd, pcitool_uiargs_t *input_args_p); 158d5ace945SErwin T Tsaur static int do_interrupts(int fd, pcitool_uiargs_t *input_args_p); 159d5ace945SErwin T Tsaur 160d5ace945SErwin T Tsaur 161d5ace945SErwin T Tsaur /* *************** General ************** */ 162d5ace945SErwin T Tsaur 163d5ace945SErwin T Tsaur /* 164d5ace945SErwin T Tsaur * Handler for ^C to stop looping. 165d5ace945SErwin T Tsaur */ 166d5ace945SErwin T Tsaur /*ARGSUSED*/ 167d5ace945SErwin T Tsaur static void 168d5ace945SErwin T Tsaur signal_handler(int dummy) 169d5ace945SErwin T Tsaur { 170d5ace945SErwin T Tsaur keep_looping = B_FALSE; 171d5ace945SErwin T Tsaur } 172d5ace945SErwin T Tsaur 173d5ace945SErwin T Tsaur 174d5ace945SErwin T Tsaur /* 175d5ace945SErwin T Tsaur * Print string based on PCItool status returned from driver. 176d5ace945SErwin T Tsaur */ 177d5ace945SErwin T Tsaur static char * 178d5ace945SErwin T Tsaur strstatus(pcitool_errno_t pcitool_status) 179d5ace945SErwin T Tsaur { 180d5ace945SErwin T Tsaur int i; 181d5ace945SErwin T Tsaur 182d5ace945SErwin T Tsaur for (i = 0; pcitool_stat_str[i].string != NULL; i++) { 183d5ace945SErwin T Tsaur if (pcitool_stat_str[i].value == pcitool_status) { 184d5ace945SErwin T Tsaur 185d5ace945SErwin T Tsaur return (pcitool_stat_str[i].string); 186d5ace945SErwin T Tsaur } 187d5ace945SErwin T Tsaur } 188d5ace945SErwin T Tsaur 189d5ace945SErwin T Tsaur return ("Unknown status returned from driver."); 190d5ace945SErwin T Tsaur } 191d5ace945SErwin T Tsaur 192d5ace945SErwin T Tsaur 193d5ace945SErwin T Tsaur static int 194d5ace945SErwin T Tsaur open_node(char *device, pcitool_uiargs_t *input_args_p) 195d5ace945SErwin T Tsaur { 196d5ace945SErwin T Tsaur int fd; 197d5ace945SErwin T Tsaur char *path; /* For building full nexus pathname. */ 198d5ace945SErwin T Tsaur int stringsize; /* Device name size. */ 199d5ace945SErwin T Tsaur char *prefix; 200d5ace945SErwin T Tsaur char *suffix; 201d5ace945SErwin T Tsaur char *format; 202d5ace945SErwin T Tsaur 203d5ace945SErwin T Tsaur static char slash_devices[] = {"/devices"}; 204d5ace945SErwin T Tsaur static char wcolon[] = {"%s%s:%s"}; 205d5ace945SErwin T Tsaur static char wocolon[] = {"%s%s%s"}; 206d5ace945SErwin T Tsaur 207d5ace945SErwin T Tsaur /* Check for names starting with /devices. */ 208d5ace945SErwin T Tsaur prefix = (strstr(device, slash_devices) == device) ? "" : slash_devices; 209d5ace945SErwin T Tsaur 210d5ace945SErwin T Tsaur format = wcolon; 211d5ace945SErwin T Tsaur if (input_args_p->flags & INTR_FLAG) { 212d5ace945SErwin T Tsaur if (strstr(device, PCI_MINOR_INTR) == 213d5ace945SErwin T Tsaur device + (strlen(device) - strlen(PCI_MINOR_INTR))) { 214d5ace945SErwin T Tsaur suffix = ""; 215d5ace945SErwin T Tsaur format = wocolon; 216d5ace945SErwin T Tsaur } else { 217d5ace945SErwin T Tsaur suffix = PCI_MINOR_INTR; 218d5ace945SErwin T Tsaur } 219d5ace945SErwin T Tsaur } else { 220d5ace945SErwin T Tsaur if (strstr(device, PCI_MINOR_REG) == 221d5ace945SErwin T Tsaur device + (strlen(device) - strlen(PCI_MINOR_REG))) { 222d5ace945SErwin T Tsaur suffix = ""; 223d5ace945SErwin T Tsaur format = wocolon; 224d5ace945SErwin T Tsaur } else { 225d5ace945SErwin T Tsaur suffix = PCI_MINOR_REG; 226d5ace945SErwin T Tsaur } 227d5ace945SErwin T Tsaur } 228d5ace945SErwin T Tsaur 229d5ace945SErwin T Tsaur /* 230d5ace945SErwin T Tsaur * Build nexus pathname. 231d5ace945SErwin T Tsaur * User specified /pci@1f,700000 becomes /devices/pci@1f,700000:intr 232d5ace945SErwin T Tsaur * for interrupt nodes, and ...:reg for register nodes. 233d5ace945SErwin T Tsaur * 234d5ace945SErwin T Tsaur * ...The 2 at the end leaves room for a : and the terminating NULL. 235d5ace945SErwin T Tsaur */ 236d5ace945SErwin T Tsaur stringsize = strlen(prefix) + strlen(device) + strlen(suffix) + 2; 237d5ace945SErwin T Tsaur path = malloc(stringsize); 238d5ace945SErwin T Tsaur 239d5ace945SErwin T Tsaur /*LINTED*/ 240d5ace945SErwin T Tsaur (void) snprintf(path, stringsize, format, prefix, device, suffix); 241d5ace945SErwin T Tsaur 242d5ace945SErwin T Tsaur /* Open the nexus. */ 243d5ace945SErwin T Tsaur if ((fd = open(path, O_RDWR)) == -1) { 244d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 245d5ace945SErwin T Tsaur (void) fprintf(stderr, 246d5ace945SErwin T Tsaur "Could not open nexus node %s: %s\n", 247d5ace945SErwin T Tsaur path, strerror(errno)); 248d5ace945SErwin T Tsaur } 249d5ace945SErwin T Tsaur } 250d5ace945SErwin T Tsaur 251d5ace945SErwin T Tsaur return (fd); 252d5ace945SErwin T Tsaur } 253d5ace945SErwin T Tsaur 254d5ace945SErwin T Tsaur 255d5ace945SErwin T Tsaur /* ****************** Probe **************** */ 256d5ace945SErwin T Tsaur 257d5ace945SErwin T Tsaur /* The following are used by the probe printing functions. */ 258d5ace945SErwin T Tsaur 259d5ace945SErwin T Tsaur /* Header 0 and 1 config space headers have these fields. */ 260d5ace945SErwin T Tsaur static field_type_t first_fields[] = { 261d5ace945SErwin T Tsaur { PCI_CONF_VENID, 2, "Vend", "Vendor ID" }, 262d5ace945SErwin T Tsaur { PCI_CONF_DEVID, 2, "Dev ", "Device ID" }, 263d5ace945SErwin T Tsaur { PCI_CONF_COMM, 2, "Cmd ", "Command" }, 264d5ace945SErwin T Tsaur { PCI_CONF_STAT, 2, "Stat", "Status" }, 265d5ace945SErwin T Tsaur { PCI_CONF_REVID, 1, "Rv", "Revision ID" }, 266d5ace945SErwin T Tsaur { PCI_CONF_PROGCLASS, 3, "Class ", "Class Code" }, 267d5ace945SErwin T Tsaur { PCI_CONF_CACHE_LINESZ, 1, "Ca", "Cache Line Size" }, 268d5ace945SErwin T Tsaur { PCI_CONF_LATENCY_TIMER, 1, "LT", "Latency Timer" }, 269d5ace945SErwin T Tsaur { PCI_CONF_HEADER, 1, "Hd", "Header Type" }, 270d5ace945SErwin T Tsaur { PCI_CONF_BIST, 1, "BI", "BIST" }, 271d5ace945SErwin T Tsaur { 0, 0, NULL, NULL } 272d5ace945SErwin T Tsaur }; 273d5ace945SErwin T Tsaur 274d5ace945SErwin T Tsaur /* Header 0 (for regular devices) have these fields. */ 275d5ace945SErwin T Tsaur static field_type_t last_dev_fields[] = { 276d5ace945SErwin T Tsaur { PCI_CONF_BASE0, 4, "BAR0", "Base Address Register 0 (@10)" }, 277d5ace945SErwin T Tsaur { PCI_CONF_BASE1, 4, "BAR1", "Base Address Register 1 (@14)" }, 278d5ace945SErwin T Tsaur { PCI_CONF_BASE2, 4, "BAR2", "Base Address Register 2 (@18)" }, 279d5ace945SErwin T Tsaur { PCI_CONF_BASE3, 4, "BAR3", "Base Address Register 3 (@1C)" }, 280d5ace945SErwin T Tsaur { PCI_CONF_BASE4, 4, "BAR4", "Base Address Register 4 (@20)" }, 281d5ace945SErwin T Tsaur { PCI_CONF_BASE5, 4, "BAR5", "Base Address Register 5 (@24)" }, 282d5ace945SErwin T Tsaur { PCI_CONF_ROM, 4, "ROM", "Expansion ROM Base Address Register (@30)" }, 283d5ace945SErwin T Tsaur { 0, 0, NULL, NULL } 284d5ace945SErwin T Tsaur }; 285d5ace945SErwin T Tsaur 286d5ace945SErwin T Tsaur /* Header 1 (PCI-PCI bridge devices) have these fields. */ 287d5ace945SErwin T Tsaur static field_type_t last_pcibrg_fields[] = { 288d5ace945SErwin T Tsaur { PCI_CONF_BASE0, 4, "BAR0", "Base Address Register 0 (@10)" }, 289d5ace945SErwin T Tsaur { PCI_CONF_BASE1, 4, "BAR1", "Base Address Register 1 (@14)" }, 290d5ace945SErwin T Tsaur { PCI_BCNF_ROM, 4, "ROM", "Expansion ROM Base Address Register (@38)" }, 291d5ace945SErwin T Tsaur { 0, 0, NULL, NULL } 292d5ace945SErwin T Tsaur }; 293d5ace945SErwin T Tsaur 294d5ace945SErwin T Tsaur /* Header 2 (PCI-Cardbus bridge devices) have these fields. */ 295d5ace945SErwin T Tsaur static field_type_t last_cbbrg_fields[] = { 296d5ace945SErwin T Tsaur { PCI_CBUS_SOCK_REG, 4, "SCKT", "Socket/ExCA Base Address (@10)" }, 297d5ace945SErwin T Tsaur { 0, 0, NULL, NULL } 298d5ace945SErwin T Tsaur }; 299d5ace945SErwin T Tsaur 300d5ace945SErwin T Tsaur #define FMT_SIZE 7 301d5ace945SErwin T Tsaur 302d5ace945SErwin T Tsaur static void 303d5ace945SErwin T Tsaur print_probe_value(pci_conf_hdr_t *config_hdr_p, uint16_t offset, uint8_t size) 304d5ace945SErwin T Tsaur { 305d5ace945SErwin T Tsaur 306d5ace945SErwin T Tsaur char format[FMT_SIZE]; 307d5ace945SErwin T Tsaur 308d5ace945SErwin T Tsaur 309d5ace945SErwin T Tsaur /* Size cannot be any larger than 4 bytes. This is not checked. */ 310d5ace945SErwin T Tsaur uint32_t value = 0; 311d5ace945SErwin T Tsaur 312d5ace945SErwin T Tsaur /* Build format of print, "%<size*2>.<size*2>x" */ 313d5ace945SErwin T Tsaur (void) snprintf(format, FMT_SIZE, "%%%d.%dx ", size * 2, size * 2); 314d5ace945SErwin T Tsaur 315d5ace945SErwin T Tsaur while (size-- > 0) { 316d5ace945SErwin T Tsaur value = (value << 8) + config_hdr_p->bytes[offset + size]; 317d5ace945SErwin T Tsaur } 318d5ace945SErwin T Tsaur 319d5ace945SErwin T Tsaur /*LINTED*/ 320d5ace945SErwin T Tsaur (void) printf(format, value); 321d5ace945SErwin T Tsaur } 322d5ace945SErwin T Tsaur 323d5ace945SErwin T Tsaur static void 324d5ace945SErwin T Tsaur print_probe_info_verbose(pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p) 325d5ace945SErwin T Tsaur { 326d5ace945SErwin T Tsaur field_type_t *last_fields = NULL; 327d5ace945SErwin T Tsaur int i; 328d5ace945SErwin T Tsaur 329d5ace945SErwin T Tsaur (void) printf("\n" 330d5ace945SErwin T Tsaur "Bus Number: %x Device Number: %x Function Number: %x\n", 331d5ace945SErwin T Tsaur info_p->bus_no, info_p->dev_no, info_p->func_no); 332d5ace945SErwin T Tsaur if (info_p->phys_addr != 0) { 333d5ace945SErwin T Tsaur (void) printf("Physical Address: 0x%" PRIx64 " \n", 334d5ace945SErwin T Tsaur info_p->phys_addr); 335d5ace945SErwin T Tsaur } 336d5ace945SErwin T Tsaur 337d5ace945SErwin T Tsaur switch (config_hdr_p->bytes[PCI_CONF_HEADER] & PCI_HEADER_TYPE_M) { 338d5ace945SErwin T Tsaur 339d5ace945SErwin T Tsaur case PCI_HEADER_ZERO: /* Header type 0 is a regular device. */ 340d5ace945SErwin T Tsaur last_fields = last_dev_fields; 341d5ace945SErwin T Tsaur break; 342d5ace945SErwin T Tsaur 343d5ace945SErwin T Tsaur case PCI_HEADER_PPB: /* Header type 1 is a PCI-PCI bridge. */ 344d5ace945SErwin T Tsaur last_fields = last_pcibrg_fields; 345d5ace945SErwin T Tsaur (void) printf("PCI-PCI bridge\n"); 346d5ace945SErwin T Tsaur break; 347d5ace945SErwin T Tsaur 348d5ace945SErwin T Tsaur case PCI_HEADER_CARDBUS: /* Header type 2 is a cardbus bridge */ 349d5ace945SErwin T Tsaur last_fields = last_cbbrg_fields; 350d5ace945SErwin T Tsaur (void) printf("PCI-Cardbus bridge\n"); 351d5ace945SErwin T Tsaur break; 352d5ace945SErwin T Tsaur 353d5ace945SErwin T Tsaur default: 354d5ace945SErwin T Tsaur (void) printf("Unknown device\n"); 355d5ace945SErwin T Tsaur break; 356d5ace945SErwin T Tsaur } 357d5ace945SErwin T Tsaur 358d5ace945SErwin T Tsaur if (last_fields != NULL) { 359d5ace945SErwin T Tsaur 360d5ace945SErwin T Tsaur for (i = 0; first_fields[i].size != 0; i++) { 361d5ace945SErwin T Tsaur (void) printf("%s: ", first_fields[i].full_hdr); 362d5ace945SErwin T Tsaur print_probe_value(config_hdr_p, 363d5ace945SErwin T Tsaur first_fields[i].cfg_offset, first_fields[i].size); 364d5ace945SErwin T Tsaur (void) putchar('\n'); 365d5ace945SErwin T Tsaur } 366d5ace945SErwin T Tsaur 367d5ace945SErwin T Tsaur for (i = 0; last_fields[i].size != 0; i++) { 368d5ace945SErwin T Tsaur (void) printf("%s: ", last_fields[i].full_hdr); 369d5ace945SErwin T Tsaur print_probe_value(config_hdr_p, 370d5ace945SErwin T Tsaur last_fields[i].cfg_offset, last_fields[i].size); 371d5ace945SErwin T Tsaur (void) putchar('\n'); 372d5ace945SErwin T Tsaur } 373d5ace945SErwin T Tsaur } 374d5ace945SErwin T Tsaur } 375d5ace945SErwin T Tsaur 376d5ace945SErwin T Tsaur static void 377d5ace945SErwin T Tsaur print_probe_info_nonverbose(pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p) 378d5ace945SErwin T Tsaur { 379d5ace945SErwin T Tsaur int i; 380d5ace945SErwin T Tsaur 381d5ace945SErwin T Tsaur (void) printf("%2.2x %2.2x %1.1x ", 382d5ace945SErwin T Tsaur info_p->bus_no, info_p->dev_no, info_p->func_no); 383d5ace945SErwin T Tsaur for (i = 0; first_fields[i].size != 0; i++) { 384d5ace945SErwin T Tsaur print_probe_value(config_hdr_p, 385d5ace945SErwin T Tsaur first_fields[i].cfg_offset, first_fields[i].size); 386d5ace945SErwin T Tsaur } 387d5ace945SErwin T Tsaur (void) putchar('\n'); 388d5ace945SErwin T Tsaur } 389d5ace945SErwin T Tsaur 390d5ace945SErwin T Tsaur 391d5ace945SErwin T Tsaur /* 392d5ace945SErwin T Tsaur * Print device information retrieved during probe mode. 393d5ace945SErwin T Tsaur * Takes the PCI config header, plus address information retrieved from the 394d5ace945SErwin T Tsaur * driver. 395d5ace945SErwin T Tsaur * 396d5ace945SErwin T Tsaur * When called with config_hdr_p == NULL, this function just prints a header 397d5ace945SErwin T Tsaur * when not in verbose mode. 398d5ace945SErwin T Tsaur */ 399d5ace945SErwin T Tsaur 400d5ace945SErwin T Tsaur static void 401d5ace945SErwin T Tsaur print_probe_info( 402d5ace945SErwin T Tsaur pci_conf_hdr_t *config_hdr_p, pcitool_reg_t *info_p, boolean_t verbose) 403d5ace945SErwin T Tsaur { 404d5ace945SErwin T Tsaur int i; 405d5ace945SErwin T Tsaur 406d5ace945SErwin T Tsaur /* Print header if not in verbose mode. */ 407d5ace945SErwin T Tsaur if (config_hdr_p == NULL) { 408d5ace945SErwin T Tsaur if (!verbose) { 409d5ace945SErwin T Tsaur 410d5ace945SErwin T Tsaur /* Bus dev func not from tble */ 411d5ace945SErwin T Tsaur (void) printf("B D F "); 412d5ace945SErwin T Tsaur 413d5ace945SErwin T Tsaur for (i = 0; first_fields[i].size != 0; i++) { 414d5ace945SErwin T Tsaur (void) printf("%s ", 415d5ace945SErwin T Tsaur first_fields[i].abbrev_hdr); 416d5ace945SErwin T Tsaur } 417d5ace945SErwin T Tsaur (void) putchar('\n'); 418d5ace945SErwin T Tsaur } 419d5ace945SErwin T Tsaur 420d5ace945SErwin T Tsaur return; 421d5ace945SErwin T Tsaur } 422d5ace945SErwin T Tsaur 423d5ace945SErwin T Tsaur if (verbose) { 424d5ace945SErwin T Tsaur print_probe_info_verbose(config_hdr_p, info_p); 425d5ace945SErwin T Tsaur } else { 426d5ace945SErwin T Tsaur print_probe_info_nonverbose(config_hdr_p, info_p); 427d5ace945SErwin T Tsaur } 428d5ace945SErwin T Tsaur } 429d5ace945SErwin T Tsaur 430d5ace945SErwin T Tsaur 431d5ace945SErwin T Tsaur /* 432d5ace945SErwin T Tsaur * Retrieve first 16 dwords of device's config header, except for the first 433d5ace945SErwin T Tsaur * dword. First 16 dwords are defined by the PCI specification. 434d5ace945SErwin T Tsaur */ 435d5ace945SErwin T Tsaur static int 436d5ace945SErwin T Tsaur get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no, 437d5ace945SErwin T Tsaur pci_conf_hdr_t *config_hdr_p) 438d5ace945SErwin T Tsaur { 439d5ace945SErwin T Tsaur pcitool_reg_t cfg_prg; 440d5ace945SErwin T Tsaur int i; 441d5ace945SErwin T Tsaur int rval = SUCCESS; 442d5ace945SErwin T Tsaur 443d5ace945SErwin T Tsaur /* Prepare a local pcitool_reg_t so as to not disturb the caller's. */ 444d5ace945SErwin T Tsaur cfg_prg.offset = 0; 445d5ace945SErwin T Tsaur cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; 446d5ace945SErwin T Tsaur cfg_prg.bus_no = bus_no; 447d5ace945SErwin T Tsaur cfg_prg.dev_no = dev_no; 448d5ace945SErwin T Tsaur cfg_prg.func_no = func_no; 449d5ace945SErwin T Tsaur cfg_prg.barnum = 0; 450d5ace945SErwin T Tsaur cfg_prg.user_version = PCITOOL_VERSION; 451d5ace945SErwin T Tsaur 452d5ace945SErwin T Tsaur /* Get dwords 1-15 of config space. They must be read as uint32_t. */ 453d5ace945SErwin T Tsaur for (i = 1; i < (sizeof (pci_conf_hdr_t) / sizeof (uint32_t)); i++) { 454d5ace945SErwin T Tsaur cfg_prg.offset += sizeof (uint32_t); 455d5ace945SErwin T Tsaur if ((rval = 456d5ace945SErwin T Tsaur ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != SUCCESS) { 457d5ace945SErwin T Tsaur break; 458d5ace945SErwin T Tsaur } 459d5ace945SErwin T Tsaur config_hdr_p->dwords[i] = (uint32_t)cfg_prg.data; 460d5ace945SErwin T Tsaur } 461d5ace945SErwin T Tsaur return (rval); 462d5ace945SErwin T Tsaur } 463d5ace945SErwin T Tsaur 464*26947304SEvan Yan static int 465*26947304SEvan Yan supports_ari(int fd, uint8_t bus_no) 466*26947304SEvan Yan { 467*26947304SEvan Yan pcitool_reg_t cfg_prg; 468*26947304SEvan Yan int deadcount = 0; 469*26947304SEvan Yan uint32_t data, hdr_next_ptr, hdr_cap_id; 470*26947304SEvan Yan uint8_t dev_no = 0; 471*26947304SEvan Yan uint8_t func_no = 0; 472*26947304SEvan Yan 473*26947304SEvan Yan /* Prepare a local pcitool_reg_t so as to not disturb the caller's. */ 474*26947304SEvan Yan cfg_prg.bus_no = bus_no; 475*26947304SEvan Yan cfg_prg.dev_no = dev_no; 476*26947304SEvan Yan cfg_prg.func_no = func_no; 477*26947304SEvan Yan cfg_prg.barnum = 0; 478*26947304SEvan Yan cfg_prg.user_version = PCITOOL_VERSION; 479*26947304SEvan Yan cfg_prg.offset = PCI_CONF_COMM; 480*26947304SEvan Yan cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + PCITOOL_ACC_ATTR_ENDN_LTL; 481*26947304SEvan Yan 482*26947304SEvan Yan if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) { 483*26947304SEvan Yan return (FAILURE); 484*26947304SEvan Yan } 485*26947304SEvan Yan 486*26947304SEvan Yan data = (uint32_t)cfg_prg.data; 487*26947304SEvan Yan 488*26947304SEvan Yan if (!((data >> 16) & PCI_STAT_CAP)) 489*26947304SEvan Yan return (FAILURE); 490*26947304SEvan Yan 491*26947304SEvan Yan cfg_prg.offset = PCI_CONF_CAP_PTR; 492*26947304SEvan Yan if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) { 493*26947304SEvan Yan return (FAILURE); 494*26947304SEvan Yan } 495*26947304SEvan Yan data = (uint32_t)cfg_prg.data; 496*26947304SEvan Yan hdr_next_ptr = data & 0xff; 497*26947304SEvan Yan hdr_cap_id = 0; 498*26947304SEvan Yan 499*26947304SEvan Yan /* 500*26947304SEvan Yan * Find the PCIe capability. 501*26947304SEvan Yan */ 502*26947304SEvan Yan while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) && 503*26947304SEvan Yan (hdr_cap_id != PCI_CAP_ID_PCI_E)) { 504*26947304SEvan Yan 505*26947304SEvan Yan if (hdr_next_ptr < 0x40) 506*26947304SEvan Yan break; 507*26947304SEvan Yan 508*26947304SEvan Yan cfg_prg.offset = hdr_next_ptr; 509*26947304SEvan Yan 510*26947304SEvan Yan if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) 511*26947304SEvan Yan return (FAILURE); 512*26947304SEvan Yan 513*26947304SEvan Yan data = (uint32_t)cfg_prg.data; 514*26947304SEvan Yan 515*26947304SEvan Yan hdr_next_ptr = (data >> 8) & 0xFF; 516*26947304SEvan Yan hdr_cap_id = data & 0xFF; 517*26947304SEvan Yan 518*26947304SEvan Yan if (deadcount++ > 100) 519*26947304SEvan Yan return (FAILURE); 520*26947304SEvan Yan } 521*26947304SEvan Yan 522*26947304SEvan Yan if (hdr_cap_id != PCI_CAP_ID_PCI_E) 523*26947304SEvan Yan return (FAILURE); 524*26947304SEvan Yan 525*26947304SEvan Yan /* Found a PCIe Capability */ 526*26947304SEvan Yan 527*26947304SEvan Yan hdr_next_ptr = 0x100; 528*26947304SEvan Yan hdr_cap_id = 0; 529*26947304SEvan Yan 530*26947304SEvan Yan /* 531*26947304SEvan Yan * Now find the ARI Capability. 532*26947304SEvan Yan */ 533*26947304SEvan Yan while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) && 534*26947304SEvan Yan (hdr_cap_id != 0xe)) { 535*26947304SEvan Yan 536*26947304SEvan Yan if (hdr_next_ptr < 0x40) 537*26947304SEvan Yan break; 538*26947304SEvan Yan 539*26947304SEvan Yan cfg_prg.offset = hdr_next_ptr; 540*26947304SEvan Yan 541*26947304SEvan Yan if (ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg) != SUCCESS) { 542*26947304SEvan Yan return (FAILURE); 543*26947304SEvan Yan } 544*26947304SEvan Yan data = (uint32_t)cfg_prg.data; 545*26947304SEvan Yan 546*26947304SEvan Yan hdr_next_ptr = (data >> 20) & 0xFFF; 547*26947304SEvan Yan hdr_cap_id = data & 0xFFFF; 548*26947304SEvan Yan 549*26947304SEvan Yan if (deadcount++ > 100) 550*26947304SEvan Yan return (FAILURE); 551*26947304SEvan Yan } 552*26947304SEvan Yan 553*26947304SEvan Yan if (hdr_cap_id != 0xe) 554*26947304SEvan Yan return (FAILURE); 555*26947304SEvan Yan 556*26947304SEvan Yan return (SUCCESS); 557*26947304SEvan Yan } 558*26947304SEvan Yan 559d5ace945SErwin T Tsaur /* 560d5ace945SErwin T Tsaur * Identify problematic southbridges. These have device id 0x5249 and 561d5ace945SErwin T Tsaur * vendor id 0x10b9. Check for revision ID 0 and class code 060400 as well. 562d5ace945SErwin T Tsaur * Values are little endian, so they are reversed for SPARC. 563d5ace945SErwin T Tsaur * 564d5ace945SErwin T Tsaur * Check for these southbridges on all architectures, as the issue is a 565d5ace945SErwin T Tsaur * southbridge issue, independent of processor. 566d5ace945SErwin T Tsaur * 567d5ace945SErwin T Tsaur * If one of these is found during probing, skip probing other devs/funcs on 568d5ace945SErwin T Tsaur * the rest of the bus, since the southbridge and all devs underneath will 569d5ace945SErwin T Tsaur * otherwise disappear. 570d5ace945SErwin T Tsaur */ 571d5ace945SErwin T Tsaur #if (NATIVE_ENDIAN == PCITOOL_ACC_ATTR_ENDN_BIG) 572d5ace945SErwin T Tsaur #define U45_SB_DEVID_VID 0xb9104952 573d5ace945SErwin T Tsaur #define U45_SB_CLASS_RID 0x00000406 574d5ace945SErwin T Tsaur #else 575d5ace945SErwin T Tsaur #define U45_SB_DEVID_VID 0x524910b9 576d5ace945SErwin T Tsaur #define U45_SB_CLASS_RID 0x06040000 577d5ace945SErwin T Tsaur #endif 578d5ace945SErwin T Tsaur 579d5ace945SErwin T Tsaur /* 580d5ace945SErwin T Tsaur * Probe device's functions. Modifies many fields in the prg_p. 581d5ace945SErwin T Tsaur */ 582d5ace945SErwin T Tsaur static int 583d5ace945SErwin T Tsaur probe_dev(int fd, pcitool_reg_t *prg_p, pcitool_uiargs_t *input_args_p) 584d5ace945SErwin T Tsaur { 585d5ace945SErwin T Tsaur pci_conf_hdr_t config_hdr; 586*26947304SEvan Yan boolean_t multi_function_device = B_FALSE; 587d5ace945SErwin T Tsaur int func; 588d5ace945SErwin T Tsaur int first_func = 0; 589d5ace945SErwin T Tsaur int last_func = PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT; 590d5ace945SErwin T Tsaur int rval = SUCCESS; 591d5ace945SErwin T Tsaur 592d5ace945SErwin T Tsaur if (input_args_p->flags & FUNC_SPEC_FLAG) { 593d5ace945SErwin T Tsaur first_func = last_func = input_args_p->function; 594*26947304SEvan Yan } else if (supports_ari(fd, prg_p->bus_no) == SUCCESS) { 595*26947304SEvan Yan multi_function_device = B_TRUE; 596*26947304SEvan Yan if (!(input_args_p->flags & DEV_SPEC_FLAG)) 597*26947304SEvan Yan last_func = 255; 598d5ace945SErwin T Tsaur } 599d5ace945SErwin T Tsaur 600d5ace945SErwin T Tsaur /* 601d5ace945SErwin T Tsaur * Loop through at least func=first_func. Continue looping through 602d5ace945SErwin T Tsaur * functions if there are no errors and the device is a multi-function 603d5ace945SErwin T Tsaur * device. 604d5ace945SErwin T Tsaur * 605d5ace945SErwin T Tsaur * (Note, if first_func == 0, header will show whether multifunction 606d5ace945SErwin T Tsaur * device and set multi_function_device. If first_func != 0, then we 607d5ace945SErwin T Tsaur * will force the loop as the user wants a specific function to be 608d5ace945SErwin T Tsaur * checked. 609d5ace945SErwin T Tsaur */ 610*26947304SEvan Yan for (func = first_func; ((func <= last_func) && 611d5ace945SErwin T Tsaur ((func == first_func) || (multi_function_device))); 612d5ace945SErwin T Tsaur func++) { 613*26947304SEvan Yan if (last_func > 7) { 614*26947304SEvan Yan prg_p->func_no = func & 0x7; 615*26947304SEvan Yan prg_p->dev_no = (func >> 3) & 0x1f; 616*26947304SEvan Yan } else 617d5ace945SErwin T Tsaur prg_p->func_no = func; 618d5ace945SErwin T Tsaur 619d5ace945SErwin T Tsaur /* 620d5ace945SErwin T Tsaur * Four things can happen here: 621d5ace945SErwin T Tsaur * 622d5ace945SErwin T Tsaur * 1) ioctl comes back as EFAULT and prg_p->status is 623d5ace945SErwin T Tsaur * PCITOOL_INVALID_ADDRESS. There is no device at this 624d5ace945SErwin T Tsaur * location. 625d5ace945SErwin T Tsaur * 626d5ace945SErwin T Tsaur * 2) ioctl comes back successful and the data comes back as 627d5ace945SErwin T Tsaur * zero. Config space is mapped but no device responded. 628d5ace945SErwin T Tsaur * 629d5ace945SErwin T Tsaur * 3) ioctl comes back successful and the data comes back as 630d5ace945SErwin T Tsaur * non-zero. We've found a device. 631d5ace945SErwin T Tsaur * 632d5ace945SErwin T Tsaur * 4) Some other error occurs in an ioctl. 633d5ace945SErwin T Tsaur */ 634d5ace945SErwin T Tsaur 635d5ace945SErwin T Tsaur prg_p->status = PCITOOL_SUCCESS; 636d5ace945SErwin T Tsaur prg_p->offset = 0; 637d5ace945SErwin T Tsaur prg_p->data = 0; 638d5ace945SErwin T Tsaur prg_p->user_version = PCITOOL_VERSION; 639d5ace945SErwin T Tsaur if (((rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) || 640d5ace945SErwin T Tsaur (prg_p->data == 0xffffffff)) { 641d5ace945SErwin T Tsaur 642d5ace945SErwin T Tsaur /* 643d5ace945SErwin T Tsaur * Accept errno == EINVAL along with status of 644d5ace945SErwin T Tsaur * PCITOOL_OUT_OF_RANGE because some systems 645d5ace945SErwin T Tsaur * don't implement the full range of config space. 646d5ace945SErwin T Tsaur * Leave the loop quietly in this case. 647d5ace945SErwin T Tsaur */ 648d5ace945SErwin T Tsaur if ((errno == EINVAL) || 649d5ace945SErwin T Tsaur (prg_p->status == PCITOOL_OUT_OF_RANGE)) { 650d5ace945SErwin T Tsaur break; 651d5ace945SErwin T Tsaur } 652d5ace945SErwin T Tsaur 653d5ace945SErwin T Tsaur /* 654d5ace945SErwin T Tsaur * Exit silently with ENXIO as this means that there are 655d5ace945SErwin T Tsaur * no devices under the pci root nexus. 656d5ace945SErwin T Tsaur */ 657d5ace945SErwin T Tsaur else if ((errno == ENXIO) && 658d5ace945SErwin T Tsaur (prg_p->status == PCITOOL_IO_ERROR)) { 659d5ace945SErwin T Tsaur break; 660d5ace945SErwin T Tsaur } 661d5ace945SErwin T Tsaur 662d5ace945SErwin T Tsaur /* 663d5ace945SErwin T Tsaur * Expect errno == EFAULT along with status of 664d5ace945SErwin T Tsaur * PCITOOL_INVALID_ADDRESS because there won't be 665d5ace945SErwin T Tsaur * devices at each stop. Quit on any other error. 666d5ace945SErwin T Tsaur */ 667d5ace945SErwin T Tsaur else if (((errno != EFAULT) || 668d5ace945SErwin T Tsaur (prg_p->status != PCITOOL_INVALID_ADDRESS)) && 669d5ace945SErwin T Tsaur (prg_p->data != 0xffffffff)) { 670d5ace945SErwin T Tsaur 671d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 672d5ace945SErwin T Tsaur (void) fprintf(stderr, 673d5ace945SErwin T Tsaur "Ioctl error: %s\n", 674d5ace945SErwin T Tsaur strerror(errno)); 675d5ace945SErwin T Tsaur } 676d5ace945SErwin T Tsaur break; 677d5ace945SErwin T Tsaur 678d5ace945SErwin T Tsaur /* 679d5ace945SErwin T Tsaur * If no function at this location, 680d5ace945SErwin T Tsaur * just advance to the next function. 681d5ace945SErwin T Tsaur */ 682d5ace945SErwin T Tsaur } else { 683d5ace945SErwin T Tsaur rval = SUCCESS; 684d5ace945SErwin T Tsaur } 685d5ace945SErwin T Tsaur 686d5ace945SErwin T Tsaur /* 687d5ace945SErwin T Tsaur * Data came back as 0. 688d5ace945SErwin T Tsaur * Treat as unresponsive device amd check next device. 689d5ace945SErwin T Tsaur */ 690d5ace945SErwin T Tsaur } else if (prg_p->data == 0) { 691d5ace945SErwin T Tsaur rval = SUCCESS; 692d5ace945SErwin T Tsaur /* Found something. */ 693d5ace945SErwin T Tsaur } else { 694d5ace945SErwin T Tsaur config_hdr.dwords[0] = (uint32_t)prg_p->data; 695d5ace945SErwin T Tsaur 696d5ace945SErwin T Tsaur /* Get the rest of the PCI header. */ 697d5ace945SErwin T Tsaur if ((rval = get_config_header(fd, prg_p->bus_no, 698d5ace945SErwin T Tsaur prg_p->dev_no, prg_p->func_no, &config_hdr)) != 699d5ace945SErwin T Tsaur SUCCESS) { 700d5ace945SErwin T Tsaur break; 701d5ace945SErwin T Tsaur } 702d5ace945SErwin T Tsaur 703d5ace945SErwin T Tsaur /* Print the found information. */ 704d5ace945SErwin T Tsaur print_probe_info(&config_hdr, prg_p, 705d5ace945SErwin T Tsaur IS_VERBOSE(input_args_p->flags)); 706d5ace945SErwin T Tsaur 707d5ace945SErwin T Tsaur /* 708d5ace945SErwin T Tsaur * Special case for the type of Southbridge found on 709d5ace945SErwin T Tsaur * Ultra-45 and other sun4u fire workstations. 710d5ace945SErwin T Tsaur */ 711d5ace945SErwin T Tsaur if ((config_hdr.dwords[0] == U45_SB_DEVID_VID) && 712d5ace945SErwin T Tsaur (config_hdr.dwords[2] == U45_SB_CLASS_RID)) { 713d5ace945SErwin T Tsaur rval = ECANCELED; 714d5ace945SErwin T Tsaur break; 715d5ace945SErwin T Tsaur } 716d5ace945SErwin T Tsaur 717d5ace945SErwin T Tsaur /* 718d5ace945SErwin T Tsaur * Accomodate devices which state their 719d5ace945SErwin T Tsaur * multi-functionality only in their function 0 config 720d5ace945SErwin T Tsaur * space. Note multi-functionality throughout probing 721d5ace945SErwin T Tsaur * of all of this device's functions. 722d5ace945SErwin T Tsaur */ 723d5ace945SErwin T Tsaur if (config_hdr.bytes[PCI_CONF_HEADER] & 724d5ace945SErwin T Tsaur PCI_HEADER_MULTI) { 725d5ace945SErwin T Tsaur multi_function_device = B_TRUE; 726d5ace945SErwin T Tsaur } 727d5ace945SErwin T Tsaur } 728d5ace945SErwin T Tsaur } 729d5ace945SErwin T Tsaur 730d5ace945SErwin T Tsaur return (rval); 731d5ace945SErwin T Tsaur } 732d5ace945SErwin T Tsaur 733d5ace945SErwin T Tsaur 734d5ace945SErwin T Tsaur /* 735d5ace945SErwin T Tsaur * Probe a given nexus config space for devices. 736d5ace945SErwin T Tsaur * 737d5ace945SErwin T Tsaur * fd is the file descriptor of the nexus. 738d5ace945SErwin T Tsaur * input_args contains commandline options as specified by the user. 739d5ace945SErwin T Tsaur */ 740d5ace945SErwin T Tsaur static int 741d5ace945SErwin T Tsaur do_probe(int fd, di_node_t di_node, di_prom_handle_t di_phdl, 742d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p) 743d5ace945SErwin T Tsaur { 744d5ace945SErwin T Tsaur pcitool_reg_t prg; 745d5ace945SErwin T Tsaur int bus; 746d5ace945SErwin T Tsaur int dev; 747d5ace945SErwin T Tsaur int last_bus = PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT; 748d5ace945SErwin T Tsaur int last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT; 749d5ace945SErwin T Tsaur int first_bus = 0; 750d5ace945SErwin T Tsaur int first_dev = 0; 751d5ace945SErwin T Tsaur int rval = SUCCESS; 752d5ace945SErwin T Tsaur 753d5ace945SErwin T Tsaur prg.barnum = 0; /* Config space. */ 754d5ace945SErwin T Tsaur 755d5ace945SErwin T Tsaur /* Must read in 4-byte quantities. */ 756d5ace945SErwin T Tsaur prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; 757d5ace945SErwin T Tsaur 758d5ace945SErwin T Tsaur prg.data = 0; 759d5ace945SErwin T Tsaur 760d5ace945SErwin T Tsaur /* If an explicit bus was specified by the user, go with it. */ 761d5ace945SErwin T Tsaur if (input_args_p->flags & BUS_SPEC_FLAG) { 762d5ace945SErwin T Tsaur first_bus = last_bus = input_args_p->bus; 763d5ace945SErwin T Tsaur 764d5ace945SErwin T Tsaur } else if (input_args_p->flags & PROBERNG_FLAG) { 765d5ace945SErwin T Tsaur /* Otherwise get the bus range from properties. */ 766d5ace945SErwin T Tsaur int len; 767d5ace945SErwin T Tsaur uint32_t *rangebuf = NULL; 768d5ace945SErwin T Tsaur 769d5ace945SErwin T Tsaur len = di_prop_lookup_ints(DDI_DEV_T_ANY, di_node, 770d5ace945SErwin T Tsaur "bus-range", (int **)&rangebuf); 771d5ace945SErwin T Tsaur 772d5ace945SErwin T Tsaur /* Try PROM property */ 773d5ace945SErwin T Tsaur if (len <= 0) { 774d5ace945SErwin T Tsaur len = di_prom_prop_lookup_ints(di_phdl, di_node, 775d5ace945SErwin T Tsaur "bus-range", (int **)&rangebuf); 776d5ace945SErwin T Tsaur } 777d5ace945SErwin T Tsaur 778d5ace945SErwin T Tsaur /* Take full range for default if cannot get property. */ 779d5ace945SErwin T Tsaur if (len > 0) { 780d5ace945SErwin T Tsaur first_bus = rangebuf[0]; 781d5ace945SErwin T Tsaur last_bus = rangebuf[1]; 782d5ace945SErwin T Tsaur } 783d5ace945SErwin T Tsaur } 784d5ace945SErwin T Tsaur 785d5ace945SErwin T Tsaur /* Take full range for default if not PROBERNG and not BUS_SPEC. */ 786d5ace945SErwin T Tsaur 787d5ace945SErwin T Tsaur if (last_bus == first_bus) { 788d5ace945SErwin T Tsaur if (input_args_p->flags & DEV_SPEC_FLAG) { 789d5ace945SErwin T Tsaur /* Explicit device given. Not probing a whole bus. */ 790d5ace945SErwin T Tsaur (void) puts(""); 791d5ace945SErwin T Tsaur } else { 792d5ace945SErwin T Tsaur (void) printf("*********** Probing bus %x " 793d5ace945SErwin T Tsaur "***********\n\n", first_bus); 794d5ace945SErwin T Tsaur } 795d5ace945SErwin T Tsaur } else { 796d5ace945SErwin T Tsaur (void) printf("*********** Probing buses %x through %x " 797d5ace945SErwin T Tsaur "***********\n\n", first_bus, last_bus); 798d5ace945SErwin T Tsaur } 799d5ace945SErwin T Tsaur 800d5ace945SErwin T Tsaur /* Print header. */ 801d5ace945SErwin T Tsaur print_probe_info(NULL, NULL, IS_VERBOSE(input_args_p->flags)); 802d5ace945SErwin T Tsaur 803d5ace945SErwin T Tsaur /* Device number explicitly specified. */ 804d5ace945SErwin T Tsaur if (input_args_p->flags & DEV_SPEC_FLAG) { 805d5ace945SErwin T Tsaur first_dev = last_dev = input_args_p->device; 806d5ace945SErwin T Tsaur } 807d5ace945SErwin T Tsaur 808d5ace945SErwin T Tsaur /* 809d5ace945SErwin T Tsaur * Loop through all valid bus / dev / func combinations to check for 810d5ace945SErwin T Tsaur * all devices, with the following exceptions: 811d5ace945SErwin T Tsaur * 812d5ace945SErwin T Tsaur * When nothing is found at function 0 of a bus / dev combination, skip 813d5ace945SErwin T Tsaur * the other functions of that bus / dev combination. 814d5ace945SErwin T Tsaur * 815d5ace945SErwin T Tsaur * When a found device's function 0 is probed and it is determined that 816d5ace945SErwin T Tsaur * it is not a multifunction device, skip probing of that device's 817d5ace945SErwin T Tsaur * other functions. 818d5ace945SErwin T Tsaur */ 819d5ace945SErwin T Tsaur for (bus = first_bus; ((bus <= last_bus) && (rval == SUCCESS)); bus++) { 820d5ace945SErwin T Tsaur prg.bus_no = bus; 821*26947304SEvan Yan 822*26947304SEvan Yan /* Device number explicitly specified. */ 823*26947304SEvan Yan if (input_args_p->flags & DEV_SPEC_FLAG) { 824*26947304SEvan Yan first_dev = last_dev = input_args_p->device; 825*26947304SEvan Yan } else if (supports_ari(fd, bus) == SUCCESS) { 826*26947304SEvan Yan last_dev = 0; 827*26947304SEvan Yan first_dev = 0; 828*26947304SEvan Yan } else { 829*26947304SEvan Yan last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT; 830*26947304SEvan Yan } 831*26947304SEvan Yan 832d5ace945SErwin T Tsaur for (dev = first_dev; 833d5ace945SErwin T Tsaur ((dev <= last_dev) && (rval == SUCCESS)); dev++) { 834d5ace945SErwin T Tsaur prg.dev_no = dev; 835d5ace945SErwin T Tsaur rval = probe_dev(fd, &prg, input_args_p); 836d5ace945SErwin T Tsaur } 837d5ace945SErwin T Tsaur 838d5ace945SErwin T Tsaur /* 839d5ace945SErwin T Tsaur * Ultra-45 southbridge workaround: 840d5ace945SErwin T Tsaur * ECANCELED tells to skip to the next bus. 841d5ace945SErwin T Tsaur */ 842d5ace945SErwin T Tsaur if (rval == ECANCELED) { 843d5ace945SErwin T Tsaur rval = SUCCESS; 844d5ace945SErwin T Tsaur } 845d5ace945SErwin T Tsaur } 846d5ace945SErwin T Tsaur 847d5ace945SErwin T Tsaur return (rval); 848d5ace945SErwin T Tsaur } 849d5ace945SErwin T Tsaur 850d5ace945SErwin T Tsaur /* 851d5ace945SErwin T Tsaur * This function is called-back from di_walk_minor() when any PROBE is processed 852d5ace945SErwin T Tsaur */ 853d5ace945SErwin T Tsaur /*ARGSUSED*/ 854d5ace945SErwin T Tsaur static int 855d5ace945SErwin T Tsaur process_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) 856d5ace945SErwin T Tsaur { 857d5ace945SErwin T Tsaur int fd; 858d5ace945SErwin T Tsaur char *trunc; 859d5ace945SErwin T Tsaur probe_walk_args_t *walk_args_p = (probe_walk_args_t *)arg; 860d5ace945SErwin T Tsaur char *pathname = walk_args_p->pathname; 861d5ace945SErwin T Tsaur char *nexus_path = di_devfs_minor_path(minor); 862d5ace945SErwin T Tsaur 863d5ace945SErwin T Tsaur if (nexus_path == NULL) { 864d5ace945SErwin T Tsaur (void) fprintf(stderr, "Error getting nexus path: %s\n", 865d5ace945SErwin T Tsaur strerror(errno)); 866d5ace945SErwin T Tsaur return (DI_WALK_CONTINUE); 867d5ace945SErwin T Tsaur } 868d5ace945SErwin T Tsaur 869d5ace945SErwin T Tsaur /* 870d5ace945SErwin T Tsaur * Display this node if pathname not specified (as all nodes are 871d5ace945SErwin T Tsaur * displayed) or if the current node matches the single specified 872d5ace945SErwin T Tsaur * pathname. Pathname form: xxx, nexus form: xxx:reg 873d5ace945SErwin T Tsaur */ 874d5ace945SErwin T Tsaur if ((pathname != NULL) && 875d5ace945SErwin T Tsaur ((strstr(nexus_path, pathname) != nexus_path) || 876d5ace945SErwin T Tsaur (strlen(nexus_path) != 877d5ace945SErwin T Tsaur (strlen(pathname) + strlen(PCI_MINOR_REG) + 1)))) { 878d5ace945SErwin T Tsaur di_devfs_path_free(nexus_path); 879d5ace945SErwin T Tsaur return (DI_WALK_CONTINUE); 880d5ace945SErwin T Tsaur } 881d5ace945SErwin T Tsaur 882d5ace945SErwin T Tsaur if ((fd = open_node(nexus_path, walk_args_p->input_args_p)) >= 0) { 883d5ace945SErwin T Tsaur 884d5ace945SErwin T Tsaur /* Strip off the suffix at the end of the nexus path. */ 885d5ace945SErwin T Tsaur if ((trunc = strstr(nexus_path, PCI_MINOR_REG)) != NULL) { 886d5ace945SErwin T Tsaur trunc--; /* Get the : just before too. */ 887d5ace945SErwin T Tsaur *trunc = '\0'; 888d5ace945SErwin T Tsaur } 889d5ace945SErwin T Tsaur 890d5ace945SErwin T Tsaur /* Show header only if no explicit nexus node name given. */ 891d5ace945SErwin T Tsaur (void) puts(""); 892d5ace945SErwin T Tsaur if (pathname == NULL) { 893d5ace945SErwin T Tsaur (void) printf("********** Devices in tree under %s " 894d5ace945SErwin T Tsaur "**********\n", nexus_path); 895d5ace945SErwin T Tsaur } 896d5ace945SErwin T Tsaur 897d5ace945SErwin T Tsaur /* 898d5ace945SErwin T Tsaur * Exit silently with ENXIO as this means that there are 899d5ace945SErwin T Tsaur * no devices under the pci root nexus. 900d5ace945SErwin T Tsaur */ 901d5ace945SErwin T Tsaur if ((do_probe(fd, di_node, walk_args_p->di_phdl, 902d5ace945SErwin T Tsaur walk_args_p->input_args_p) != SUCCESS) && 903d5ace945SErwin T Tsaur (errno != ENXIO)) { 904d5ace945SErwin T Tsaur (void) fprintf(stderr, "Error probing node %s: %s\n", 905d5ace945SErwin T Tsaur nexus_path, strerror(errno)); 906d5ace945SErwin T Tsaur } 907d5ace945SErwin T Tsaur 908d5ace945SErwin T Tsaur (void) close(fd); 909d5ace945SErwin T Tsaur } 910d5ace945SErwin T Tsaur di_devfs_path_free(nexus_path); 911d5ace945SErwin T Tsaur 912d5ace945SErwin T Tsaur /* 913d5ace945SErwin T Tsaur * If node was explicitly specified, it has just been displayed 914d5ace945SErwin T Tsaur * and no more looping is required. 915d5ace945SErwin T Tsaur * Otherwise, keep looping for more nodes. 916d5ace945SErwin T Tsaur */ 917d5ace945SErwin T Tsaur return ((pathname == NULL) ? DI_WALK_CONTINUE : DI_WALK_TERMINATE); 918d5ace945SErwin T Tsaur } 919d5ace945SErwin T Tsaur 920d5ace945SErwin T Tsaur 921d5ace945SErwin T Tsaur /* 922d5ace945SErwin T Tsaur * Start of probe. If pathname is NULL, search all devices. 923d5ace945SErwin T Tsaur * 924d5ace945SErwin T Tsaur * di_walk_minor() walks all DDI_NT_REGACC (PCItool register access) nodes 925d5ace945SErwin T Tsaur * and calls process_nexus_node on them. process_nexus_node will then check 926d5ace945SErwin T Tsaur * the pathname for a match, unless it is NULL which works like a wildcard. 927d5ace945SErwin T Tsaur */ 928d5ace945SErwin T Tsaur static int 929d5ace945SErwin T Tsaur do_probe_walk(pcitool_uiargs_t *input_args_p, char *pathname) 930d5ace945SErwin T Tsaur { 931d5ace945SErwin T Tsaur di_node_t di_node; 932d5ace945SErwin T Tsaur di_prom_handle_t di_phdl = DI_PROM_HANDLE_NIL; 933d5ace945SErwin T Tsaur probe_walk_args_t walk_args; 934d5ace945SErwin T Tsaur 935d5ace945SErwin T Tsaur int rval = SUCCESS; 936d5ace945SErwin T Tsaur 937d5ace945SErwin T Tsaur if ((di_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 938d5ace945SErwin T Tsaur (void) fprintf(stderr, "di_init() failed: %s\n", 939d5ace945SErwin T Tsaur strerror(errno)); 940d5ace945SErwin T Tsaur rval = errno; 941d5ace945SErwin T Tsaur 942d5ace945SErwin T Tsaur } else if ((input_args_p->flags & PROBERNG_FLAG) && 943d5ace945SErwin T Tsaur ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)) { 944d5ace945SErwin T Tsaur (void) fprintf(stderr, "di_prom_init failed: %s\n", 945d5ace945SErwin T Tsaur strerror(errno)); 946d5ace945SErwin T Tsaur rval = errno; 947d5ace945SErwin T Tsaur 948d5ace945SErwin T Tsaur } else { 949d5ace945SErwin T Tsaur walk_args.input_args_p = input_args_p; 950d5ace945SErwin T Tsaur walk_args.di_phdl = di_phdl; 951d5ace945SErwin T Tsaur walk_args.pathname = pathname; 952d5ace945SErwin T Tsaur (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, 953d5ace945SErwin T Tsaur &walk_args, process_nexus_node); 954d5ace945SErwin T Tsaur } 955d5ace945SErwin T Tsaur 956d5ace945SErwin T Tsaur if (di_phdl != DI_PROM_HANDLE_NIL) { 957d5ace945SErwin T Tsaur di_prom_fini(di_phdl); 958d5ace945SErwin T Tsaur } 959d5ace945SErwin T Tsaur 960d5ace945SErwin T Tsaur if (di_node != DI_NODE_NIL) { 961d5ace945SErwin T Tsaur di_fini(di_node); 962d5ace945SErwin T Tsaur } 963d5ace945SErwin T Tsaur 964d5ace945SErwin T Tsaur return (rval); 965d5ace945SErwin T Tsaur } 966d5ace945SErwin T Tsaur 967d5ace945SErwin T Tsaur 968d5ace945SErwin T Tsaur /* **************** Byte dump specific **************** */ 969d5ace945SErwin T Tsaur 970d5ace945SErwin T Tsaur static void 971d5ace945SErwin T Tsaur print_bytedump_header(boolean_t do_chardump) 972d5ace945SErwin T Tsaur { 973d5ace945SErwin T Tsaur static char header1[] = {" " 974d5ace945SErwin T Tsaur "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00"}; 975d5ace945SErwin T Tsaur static char header2[] = {" " 976d5ace945SErwin T Tsaur "-----------------------------------------------"}; 977d5ace945SErwin T Tsaur static char cheader1[] = {" 0123456789ABCDEF"}; 978d5ace945SErwin T Tsaur static char cheader2[] = {" ----------------"}; 979d5ace945SErwin T Tsaur 980d5ace945SErwin T Tsaur (void) puts(""); 981d5ace945SErwin T Tsaur (void) printf(header1); 982d5ace945SErwin T Tsaur if (do_chardump) { 983d5ace945SErwin T Tsaur (void) printf(cheader1); 984d5ace945SErwin T Tsaur } 985d5ace945SErwin T Tsaur (void) puts(""); 986d5ace945SErwin T Tsaur (void) printf(header2); 987d5ace945SErwin T Tsaur if (do_chardump) { 988d5ace945SErwin T Tsaur (void) printf(cheader2); 989d5ace945SErwin T Tsaur } 990d5ace945SErwin T Tsaur } 991d5ace945SErwin T Tsaur 992d5ace945SErwin T Tsaur 993d5ace945SErwin T Tsaur /* Number of bytes per line in a dump. */ 994d5ace945SErwin T Tsaur #define DUMP_BUF_SIZE 16 995d5ace945SErwin T Tsaur #define LINES_BTWN_HEADER 16 996d5ace945SErwin T Tsaur 997d5ace945SErwin T Tsaur /* 998d5ace945SErwin T Tsaur * Retrieve several bytes over several reads, and print a formatted byte-dump 999d5ace945SErwin T Tsaur * 1000d5ace945SErwin T Tsaur * fd is the nexus by which device is accessed. 1001d5ace945SErwin T Tsaur * prg provided has bus, dev, func, bank, initial offset already specified, 1002d5ace945SErwin T Tsaur * as well as size and endian attributes. 1003d5ace945SErwin T Tsaur * 1004d5ace945SErwin T Tsaur * No checking is made that this is a read operation, although only read 1005d5ace945SErwin T Tsaur * operations are allowed. 1006d5ace945SErwin T Tsaur */ 1007d5ace945SErwin T Tsaur static int 1008d5ace945SErwin T Tsaur bytedump_get(int fd, int cmd, pcitool_reg_t *prg_p, 1009d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p) 1010d5ace945SErwin T Tsaur { 1011d5ace945SErwin T Tsaur typedef union { 1012d5ace945SErwin T Tsaur uint8_t bytes[DUMP_BUF_SIZE]; 1013d5ace945SErwin T Tsaur uint16_t shorts[DUMP_BUF_SIZE / sizeof (uint16_t)]; 1014d5ace945SErwin T Tsaur uint32_t dwords[DUMP_BUF_SIZE / sizeof (uint32_t)]; 1015d5ace945SErwin T Tsaur uint64_t longs[DUMP_BUF_SIZE / sizeof (uint64_t)]; 1016d5ace945SErwin T Tsaur } buffer_t; 1017d5ace945SErwin T Tsaur 1018d5ace945SErwin T Tsaur /* 1019d5ace945SErwin T Tsaur * Local copy of pcitool_reg_t, since offset and phys_addrs are 1020d5ace945SErwin T Tsaur * modified. 1021d5ace945SErwin T Tsaur */ 1022d5ace945SErwin T Tsaur pcitool_reg_t local_prg; 1023d5ace945SErwin T Tsaur 1024d5ace945SErwin T Tsaur /* Loop parameters. */ 1025d5ace945SErwin T Tsaur uint32_t dump_end = prg_p->offset + input_args_p->bytedump_amt; 1026d5ace945SErwin T Tsaur uint32_t dump_curr = prg_p->offset; 1027d5ace945SErwin T Tsaur 1028d5ace945SErwin T Tsaur int read_size = input_args_p->size; 1029d5ace945SErwin T Tsaur 1030d5ace945SErwin T Tsaur /* How many stores to the buffer before it is full. */ 1031d5ace945SErwin T Tsaur int wrap_size = DUMP_BUF_SIZE / read_size; 1032d5ace945SErwin T Tsaur 1033d5ace945SErwin T Tsaur /* Address prints at the beginning of each line. */ 1034d5ace945SErwin T Tsaur uint64_t print_addr = 0; 1035d5ace945SErwin T Tsaur 1036d5ace945SErwin T Tsaur /* Skip this num bytes at the beginning of the first dump. */ 1037d5ace945SErwin T Tsaur int skip_begin; 1038d5ace945SErwin T Tsaur 1039d5ace945SErwin T Tsaur /* Skip this num bytes at the end of the last dump. */ 1040d5ace945SErwin T Tsaur int skip_end = 0; 1041d5ace945SErwin T Tsaur 1042d5ace945SErwin T Tsaur /* skip_begin and skip_end are needed twice. */ 1043d5ace945SErwin T Tsaur int skip_begin2; 1044d5ace945SErwin T Tsaur int skip_end2; 1045d5ace945SErwin T Tsaur 1046d5ace945SErwin T Tsaur /* Number of lines between headers */ 1047d5ace945SErwin T Tsaur int lines_since_header = 0; 1048d5ace945SErwin T Tsaur 1049d5ace945SErwin T Tsaur boolean_t do_chardump = input_args_p->flags & CHARDUMP_FLAG; 1050d5ace945SErwin T Tsaur boolean_t continue_on_errs = input_args_p->flags & ERRCONT_FLAG; 1051d5ace945SErwin T Tsaur 1052d5ace945SErwin T Tsaur int rval = SUCCESS; /* Return status. */ 1053d5ace945SErwin T Tsaur 1054d5ace945SErwin T Tsaur int next; 1055d5ace945SErwin T Tsaur int i; 1056d5ace945SErwin T Tsaur 1057d5ace945SErwin T Tsaur buffer_t buffer; 1058d5ace945SErwin T Tsaur uint16_t error_mask = 0; /* 1 bit/byte in buf. Err when set */ 1059d5ace945SErwin T Tsaur 1060d5ace945SErwin T Tsaur bzero(buffer.bytes, sizeof (uint8_t) * DUMP_BUF_SIZE); 1061d5ace945SErwin T Tsaur 1062d5ace945SErwin T Tsaur local_prg = *prg_p; /* Make local copy. */ 1063d5ace945SErwin T Tsaur 1064d5ace945SErwin T Tsaur /* 1065d5ace945SErwin T Tsaur * Flip the bytes to proper order if reading on a big endian machine. 1066d5ace945SErwin T Tsaur * Do this by reading big as little and vs. 1067d5ace945SErwin T Tsaur */ 1068d5ace945SErwin T Tsaur #if (NATIVE_ENDIAN == PCITOOL_ACC_ATTR_ENDN_BIG) 1069d5ace945SErwin T Tsaur local_prg.acc_attr = 1070d5ace945SErwin T Tsaur (PCITOOL_ACC_IS_BIG_ENDIAN(local_prg.acc_attr) ? 1071d5ace945SErwin T Tsaur (local_prg.acc_attr & ~PCITOOL_ACC_ATTR_ENDN_BIG) : 1072d5ace945SErwin T Tsaur (local_prg.acc_attr | PCITOOL_ACC_ATTR_ENDN_BIG)); 1073d5ace945SErwin T Tsaur #endif 1074d5ace945SErwin T Tsaur 1075d5ace945SErwin T Tsaur /* 1076d5ace945SErwin T Tsaur * Get offset into buffer for first store. Assumes the buffer size is 1077d5ace945SErwin T Tsaur * a multiple of the read size. "next" is the next buffer index to do 1078d5ace945SErwin T Tsaur * a store. 1079d5ace945SErwin T Tsaur */ 1080d5ace945SErwin T Tsaur skip_begin = local_prg.offset % DUMP_BUF_SIZE; 1081d5ace945SErwin T Tsaur next = skip_begin / read_size; 1082d5ace945SErwin T Tsaur 1083d5ace945SErwin T Tsaur print_bytedump_header(do_chardump); 1084d5ace945SErwin T Tsaur 1085d5ace945SErwin T Tsaur while (dump_curr < dump_end) { 1086d5ace945SErwin T Tsaur 1087d5ace945SErwin T Tsaur /* For reading from the next location. */ 1088d5ace945SErwin T Tsaur local_prg.offset = dump_curr; 1089d5ace945SErwin T Tsaur 1090d5ace945SErwin T Tsaur /* Access the device. Abort on error. */ 1091d5ace945SErwin T Tsaur if (((rval = ioctl(fd, cmd, &local_prg)) != SUCCESS) && 1092d5ace945SErwin T Tsaur (!(continue_on_errs))) { 1093d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 1094d5ace945SErwin T Tsaur (void) fprintf(stderr, 1095d5ace945SErwin T Tsaur "Ioctl failed:\n errno: %s\n status: %s\n", 1096d5ace945SErwin T Tsaur strerror(errno), 1097d5ace945SErwin T Tsaur strstatus(local_prg.status)); 1098d5ace945SErwin T Tsaur } 1099d5ace945SErwin T Tsaur break; 1100d5ace945SErwin T Tsaur } 1101d5ace945SErwin T Tsaur 1102d5ace945SErwin T Tsaur /* 1103d5ace945SErwin T Tsaur * Initialize print_addr first time through, in case printing 1104d5ace945SErwin T Tsaur * is starting in the middle of the buffer. Also reinitialize 1105d5ace945SErwin T Tsaur * when wrap. 1106d5ace945SErwin T Tsaur */ 1107d5ace945SErwin T Tsaur if (print_addr == 0) { 1108d5ace945SErwin T Tsaur 1109d5ace945SErwin T Tsaur /* 1110d5ace945SErwin T Tsaur * X86 config space doesn't return phys addr. 1111d5ace945SErwin T Tsaur * Use offset instead in this case. 1112d5ace945SErwin T Tsaur */ 1113d5ace945SErwin T Tsaur if (local_prg.phys_addr == 0) { /* No phys addr ret */ 1114d5ace945SErwin T Tsaur print_addr = local_prg.offset - 1115d5ace945SErwin T Tsaur (local_prg.offset % DUMP_BUF_SIZE); 1116d5ace945SErwin T Tsaur } else { 1117d5ace945SErwin T Tsaur print_addr = local_prg.phys_addr - 1118d5ace945SErwin T Tsaur (local_prg.phys_addr % DUMP_BUF_SIZE); 1119d5ace945SErwin T Tsaur } 1120d5ace945SErwin T Tsaur } 1121d5ace945SErwin T Tsaur 1122d5ace945SErwin T Tsaur /* 1123d5ace945SErwin T Tsaur * Read error occurred. 1124d5ace945SErwin T Tsaur * Shift the right number of error bits ((1 << read_size) - 1) 1125d5ace945SErwin T Tsaur * into the right place (next * read_size) 1126d5ace945SErwin T Tsaur */ 1127d5ace945SErwin T Tsaur if (rval != SUCCESS) { /* Read error occurred */ 1128d5ace945SErwin T Tsaur error_mask |= 1129d5ace945SErwin T Tsaur ((1 << read_size) - 1) << (next * read_size); 1130d5ace945SErwin T Tsaur 1131d5ace945SErwin T Tsaur } else { /* Save data to the buffer. */ 1132d5ace945SErwin T Tsaur 1133d5ace945SErwin T Tsaur switch (read_size) { 1134d5ace945SErwin T Tsaur case 1: 1135d5ace945SErwin T Tsaur buffer.bytes[next] = (uint8_t)local_prg.data; 1136d5ace945SErwin T Tsaur break; 1137d5ace945SErwin T Tsaur case 2: 1138d5ace945SErwin T Tsaur buffer.shorts[next] = (uint16_t)local_prg.data; 1139d5ace945SErwin T Tsaur break; 1140d5ace945SErwin T Tsaur case 4: 1141d5ace945SErwin T Tsaur buffer.dwords[next] = (uint32_t)local_prg.data; 1142d5ace945SErwin T Tsaur break; 1143d5ace945SErwin T Tsaur case 8: 1144d5ace945SErwin T Tsaur buffer.longs[next] = (uint64_t)local_prg.data; 1145d5ace945SErwin T Tsaur break; 1146d5ace945SErwin T Tsaur default: 1147d5ace945SErwin T Tsaur rval = EIO; 1148d5ace945SErwin T Tsaur break; 1149d5ace945SErwin T Tsaur } 1150d5ace945SErwin T Tsaur } 1151d5ace945SErwin T Tsaur next++; 1152d5ace945SErwin T Tsaur 1153d5ace945SErwin T Tsaur /* Increment index for next store, and wrap. */ 1154d5ace945SErwin T Tsaur next %= wrap_size; 1155d5ace945SErwin T Tsaur dump_curr += read_size; 1156d5ace945SErwin T Tsaur 1157d5ace945SErwin T Tsaur /* Zero out the remainder of the buffer if done. */ 1158d5ace945SErwin T Tsaur if (dump_curr >= dump_end) { 1159d5ace945SErwin T Tsaur if (next != 0) { 1160d5ace945SErwin T Tsaur bzero(&buffer.bytes[next * read_size], 1161d5ace945SErwin T Tsaur (wrap_size - next) * read_size); 1162d5ace945SErwin T Tsaur skip_end = (wrap_size - next) * read_size; 1163d5ace945SErwin T Tsaur next = 0; /* For printing below. */ 1164d5ace945SErwin T Tsaur } 1165d5ace945SErwin T Tsaur } 1166d5ace945SErwin T Tsaur 1167d5ace945SErwin T Tsaur /* Dump the buffer if full or if done. */ 1168d5ace945SErwin T Tsaur if (next == 0) { 1169d5ace945SErwin T Tsaur 1170d5ace945SErwin T Tsaur skip_begin2 = skip_begin; 1171d5ace945SErwin T Tsaur skip_end2 = skip_end; 1172d5ace945SErwin T Tsaur 1173d5ace945SErwin T Tsaur (void) printf("\n0x%16.16" PRIx64 ":", print_addr); 1174d5ace945SErwin T Tsaur for (i = DUMP_BUF_SIZE - 1; i >= 0; i--) { 1175d5ace945SErwin T Tsaur if (skip_end) { 1176d5ace945SErwin T Tsaur skip_end--; 1177d5ace945SErwin T Tsaur (void) printf(" --"); 1178d5ace945SErwin T Tsaur } else if (skip_begin > i) { 1179d5ace945SErwin T Tsaur skip_begin--; 1180d5ace945SErwin T Tsaur (void) printf(" --"); 1181d5ace945SErwin T Tsaur } else if (error_mask & (1 << i)) { 1182d5ace945SErwin T Tsaur (void) printf(" XX"); 1183d5ace945SErwin T Tsaur } else { 1184d5ace945SErwin T Tsaur (void) printf(" %2.2x", 1185d5ace945SErwin T Tsaur buffer.bytes[i]); 1186d5ace945SErwin T Tsaur } 1187d5ace945SErwin T Tsaur } 1188d5ace945SErwin T Tsaur 1189d5ace945SErwin T Tsaur if (do_chardump) { 1190d5ace945SErwin T Tsaur (void) putchar(' '); 1191d5ace945SErwin T Tsaur for (i = 0; i < DUMP_BUF_SIZE; i++) { 1192d5ace945SErwin T Tsaur if (skip_begin2) { 1193d5ace945SErwin T Tsaur skip_begin2--; 1194d5ace945SErwin T Tsaur (void) printf("-"); 1195d5ace945SErwin T Tsaur } else if ( 1196d5ace945SErwin T Tsaur (DUMP_BUF_SIZE - skip_end2) <= i) { 1197d5ace945SErwin T Tsaur (void) printf("-"); 1198d5ace945SErwin T Tsaur } else if (error_mask & (1 << i)) { 1199d5ace945SErwin T Tsaur (void) putchar('X'); 1200d5ace945SErwin T Tsaur } else if (isprint(buffer.bytes[i])) { 1201d5ace945SErwin T Tsaur (void) putchar(buffer.bytes[i]); 1202d5ace945SErwin T Tsaur } else { 1203d5ace945SErwin T Tsaur (void) putchar('@'); 1204d5ace945SErwin T Tsaur } 1205d5ace945SErwin T Tsaur } 1206d5ace945SErwin T Tsaur } 1207d5ace945SErwin T Tsaur 1208d5ace945SErwin T Tsaur if ((++lines_since_header == LINES_BTWN_HEADER) && 1209d5ace945SErwin T Tsaur (dump_curr < dump_end)) { 1210d5ace945SErwin T Tsaur lines_since_header = 0; 1211d5ace945SErwin T Tsaur (void) puts(""); 1212d5ace945SErwin T Tsaur print_bytedump_header(do_chardump); 1213d5ace945SErwin T Tsaur } 1214d5ace945SErwin T Tsaur 1215d5ace945SErwin T Tsaur print_addr += DUMP_BUF_SIZE; 1216d5ace945SErwin T Tsaur error_mask = 0; 1217d5ace945SErwin T Tsaur } 1218d5ace945SErwin T Tsaur } 1219d5ace945SErwin T Tsaur (void) printf("\n"); 1220d5ace945SErwin T Tsaur 1221d5ace945SErwin T Tsaur return (rval); 1222d5ace945SErwin T Tsaur } 1223d5ace945SErwin T Tsaur 1224d5ace945SErwin T Tsaur 1225d5ace945SErwin T Tsaur /* ************** Device and nexus access commands ************** */ 1226d5ace945SErwin T Tsaur 1227d5ace945SErwin T Tsaur /* 1228d5ace945SErwin T Tsaur * Helper function to set access attributes. Assumes size is valid. 1229d5ace945SErwin T Tsaur */ 1230d5ace945SErwin T Tsaur static uint32_t 1231d5ace945SErwin T Tsaur set_acc_attr(pcitool_uiargs_t *input_args_p) 1232d5ace945SErwin T Tsaur { 1233d5ace945SErwin T Tsaur uint32_t access_attrs; 1234d5ace945SErwin T Tsaur 1235d5ace945SErwin T Tsaur switch (input_args_p->size) { 1236d5ace945SErwin T Tsaur case 1: 1237d5ace945SErwin T Tsaur access_attrs = PCITOOL_ACC_ATTR_SIZE_1; 1238d5ace945SErwin T Tsaur break; 1239d5ace945SErwin T Tsaur case 2: 1240d5ace945SErwin T Tsaur access_attrs = PCITOOL_ACC_ATTR_SIZE_2; 1241d5ace945SErwin T Tsaur break; 1242d5ace945SErwin T Tsaur case 4: 1243d5ace945SErwin T Tsaur access_attrs = PCITOOL_ACC_ATTR_SIZE_4; 1244d5ace945SErwin T Tsaur break; 1245d5ace945SErwin T Tsaur case 8: 1246d5ace945SErwin T Tsaur access_attrs = PCITOOL_ACC_ATTR_SIZE_8; 1247d5ace945SErwin T Tsaur break; 1248d5ace945SErwin T Tsaur } 1249d5ace945SErwin T Tsaur 1250d5ace945SErwin T Tsaur if (input_args_p->big_endian) { 1251d5ace945SErwin T Tsaur access_attrs |= PCITOOL_ACC_ATTR_ENDN_BIG; 1252d5ace945SErwin T Tsaur } 1253d5ace945SErwin T Tsaur 1254d5ace945SErwin T Tsaur return (access_attrs); 1255d5ace945SErwin T Tsaur } 1256d5ace945SErwin T Tsaur 1257d5ace945SErwin T Tsaur static int 1258d5ace945SErwin T Tsaur do_single_access(int fd, int cmd, pcitool_reg_t *prg_p, 1259d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p) 1260d5ace945SErwin T Tsaur { 1261d5ace945SErwin T Tsaur boolean_t is_write = B_FALSE; 1262d5ace945SErwin T Tsaur int rval; 1263d5ace945SErwin T Tsaur 1264d5ace945SErwin T Tsaur switch (cmd) { 1265d5ace945SErwin T Tsaur case PCITOOL_NEXUS_SET_REG: 1266d5ace945SErwin T Tsaur case PCITOOL_DEVICE_SET_REG: 1267d5ace945SErwin T Tsaur is_write = B_TRUE; 1268d5ace945SErwin T Tsaur break; 1269d5ace945SErwin T Tsaur default: 1270d5ace945SErwin T Tsaur break; 1271d5ace945SErwin T Tsaur } 1272d5ace945SErwin T Tsaur 1273d5ace945SErwin T Tsaur /* Do the access. Return on error. */ 1274d5ace945SErwin T Tsaur if ((rval = ioctl(fd, cmd, prg_p)) != SUCCESS) { 1275d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 1276d5ace945SErwin T Tsaur (void) fprintf(stderr, 1277d5ace945SErwin T Tsaur "%s ioctl failed:\n errno: %s\n status: %s\n", 1278d5ace945SErwin T Tsaur is_write ? "write" : "read", 1279d5ace945SErwin T Tsaur strerror(errno), strstatus(prg_p->status)); 1280d5ace945SErwin T Tsaur } 1281d5ace945SErwin T Tsaur 1282d5ace945SErwin T Tsaur return (rval); 1283d5ace945SErwin T Tsaur } 1284d5ace945SErwin T Tsaur 1285d5ace945SErwin T Tsaur /* Print on all verbose requests. */ 1286d5ace945SErwin T Tsaur if (IS_VERBOSE(input_args_p->flags)) { 1287d5ace945SErwin T Tsaur 1288d5ace945SErwin T Tsaur /* 1289d5ace945SErwin T Tsaur * Return offset on platforms which return phys_addr == 0 1290d5ace945SErwin T Tsaur * for config space. 1291d5ace945SErwin T Tsaur */ 1292d5ace945SErwin T Tsaur if (prg_p->phys_addr == 0) 1293d5ace945SErwin T Tsaur prg_p->phys_addr = input_args_p->offset; 1294d5ace945SErwin T Tsaur 1295d5ace945SErwin T Tsaur (void) printf("Addr:0x%" PRIx64 ", %d-byte %s endian " 1296d5ace945SErwin T Tsaur "register value: 0x%" PRIx64 "\n", 1297d5ace945SErwin T Tsaur prg_p->phys_addr, input_args_p->size, 1298d5ace945SErwin T Tsaur (input_args_p->big_endian ? "big" : "little"), prg_p->data); 1299d5ace945SErwin T Tsaur 1300d5ace945SErwin T Tsaur /* Non-verbose, read requests. */ 1301d5ace945SErwin T Tsaur } else if (!(is_write)) { 1302d5ace945SErwin T Tsaur (void) printf("0x%" PRIx64 "\n", prg_p->data); 1303d5ace945SErwin T Tsaur } 1304d5ace945SErwin T Tsaur 1305d5ace945SErwin T Tsaur return (rval); 1306d5ace945SErwin T Tsaur } 1307d5ace945SErwin T Tsaur 1308d5ace945SErwin T Tsaur 1309d5ace945SErwin T Tsaur /* 1310d5ace945SErwin T Tsaur * fd is the file descriptor of the nexus to access, either to get its 1311d5ace945SErwin T Tsaur * registers or to access a device through that nexus. 1312d5ace945SErwin T Tsaur * 1313d5ace945SErwin T Tsaur * input args are commandline arguments specified by the user. 1314d5ace945SErwin T Tsaur */ 1315d5ace945SErwin T Tsaur static int 1316d5ace945SErwin T Tsaur do_device_or_nexus(int fd, pcitool_uiargs_t *input_args_p) 1317d5ace945SErwin T Tsaur { 1318d5ace945SErwin T Tsaur pcitool_reg_t prg; /* Request details given to the driver. */ 1319d5ace945SErwin T Tsaur uint32_t write_cmd = 0; /* Command given to the driver. */ 1320d5ace945SErwin T Tsaur uint32_t read_cmd = 0; /* Command given to the driver. */ 1321d5ace945SErwin T Tsaur int rval = SUCCESS; /* Return status. */ 1322d5ace945SErwin T Tsaur 1323d5ace945SErwin T Tsaur if (input_args_p->flags & WRITE_FLAG) { 1324d5ace945SErwin T Tsaur prg.data = input_args_p->write_value; 1325d5ace945SErwin T Tsaur if (input_args_p->flags & NEXUS_FLAG) { 1326d5ace945SErwin T Tsaur write_cmd = PCITOOL_NEXUS_SET_REG; 1327d5ace945SErwin T Tsaur } else { 1328d5ace945SErwin T Tsaur write_cmd = PCITOOL_DEVICE_SET_REG; 1329d5ace945SErwin T Tsaur } 1330d5ace945SErwin T Tsaur } 1331d5ace945SErwin T Tsaur if (input_args_p->flags & READ_FLAG) { 1332d5ace945SErwin T Tsaur if (input_args_p->flags & NEXUS_FLAG) { 1333d5ace945SErwin T Tsaur read_cmd = PCITOOL_NEXUS_GET_REG; 1334d5ace945SErwin T Tsaur } else { 1335d5ace945SErwin T Tsaur read_cmd = PCITOOL_DEVICE_GET_REG; 1336d5ace945SErwin T Tsaur } 1337d5ace945SErwin T Tsaur } 1338d5ace945SErwin T Tsaur 1339d5ace945SErwin T Tsaur /* Finish initializing access details for driver. */ 1340d5ace945SErwin T Tsaur 1341d5ace945SErwin T Tsaur /* 1342d5ace945SErwin T Tsaur * For nexus, barnum is the exact bank number, unless it is 0xFF, which 1343d5ace945SErwin T Tsaur * indicates that it is inactive and a base_address should be read from 1344d5ace945SErwin T Tsaur * the input_args instead. 1345d5ace945SErwin T Tsaur * 1346d5ace945SErwin T Tsaur * For devices, barnum is the offset to the desired BAR, or 0 for 1347d5ace945SErwin T Tsaur * config space. 1348d5ace945SErwin T Tsaur */ 1349d5ace945SErwin T Tsaur if ((input_args_p->flags & (BASE_SPEC_FLAG | NEXUS_FLAG)) == 1350d5ace945SErwin T Tsaur (BASE_SPEC_FLAG | NEXUS_FLAG)) { 1351d5ace945SErwin T Tsaur prg.barnum = PCITOOL_BASE; 1352d5ace945SErwin T Tsaur prg.phys_addr = input_args_p->base_address; 1353d5ace945SErwin T Tsaur } else 1354d5ace945SErwin T Tsaur prg.barnum = input_args_p->bank; 1355d5ace945SErwin T Tsaur 1356d5ace945SErwin T Tsaur prg.offset = input_args_p->offset; 1357d5ace945SErwin T Tsaur prg.acc_attr = set_acc_attr(input_args_p); 1358d5ace945SErwin T Tsaur prg.bus_no = input_args_p->bus; 1359d5ace945SErwin T Tsaur prg.dev_no = input_args_p->device; 1360d5ace945SErwin T Tsaur prg.func_no = input_args_p->function; 1361d5ace945SErwin T Tsaur prg.user_version = PCITOOL_VERSION; 1362d5ace945SErwin T Tsaur 1363d5ace945SErwin T Tsaur do { 1364d5ace945SErwin T Tsaur /* Do a bytedump if desired, or else do single ioctl access. */ 1365d5ace945SErwin T Tsaur if (input_args_p->flags & BYTEDUMP_FLAG) { 1366d5ace945SErwin T Tsaur 1367d5ace945SErwin T Tsaur if (IS_VERBOSE(input_args_p->flags)) { 1368d5ace945SErwin T Tsaur (void) printf( 1369d5ace945SErwin T Tsaur "\nDoing %d-byte %s endian reads:", 1370d5ace945SErwin T Tsaur input_args_p->size, 1371d5ace945SErwin T Tsaur input_args_p->big_endian ? 1372d5ace945SErwin T Tsaur "big" : "little"); 1373d5ace945SErwin T Tsaur } 1374d5ace945SErwin T Tsaur rval = bytedump_get(fd, read_cmd, &prg, input_args_p); 1375d5ace945SErwin T Tsaur 1376d5ace945SErwin T Tsaur } else { 1377d5ace945SErwin T Tsaur 1378d5ace945SErwin T Tsaur /* Single write and/or read. */ 1379d5ace945SErwin T Tsaur if (write_cmd != 0) { 1380d5ace945SErwin T Tsaur rval = do_single_access( 1381d5ace945SErwin T Tsaur fd, write_cmd, &prg, input_args_p); 1382d5ace945SErwin T Tsaur } 1383d5ace945SErwin T Tsaur 1384d5ace945SErwin T Tsaur if ((rval == SUCCESS) && (read_cmd != 0)) { 1385d5ace945SErwin T Tsaur rval = do_single_access( 1386d5ace945SErwin T Tsaur fd, read_cmd, &prg, input_args_p); 1387d5ace945SErwin T Tsaur } 1388d5ace945SErwin T Tsaur } 1389d5ace945SErwin T Tsaur } while ((IS_LOOP(input_args_p->flags)) && (rval == SUCCESS) && 1390d5ace945SErwin T Tsaur (keep_looping)); 1391d5ace945SErwin T Tsaur 1392d5ace945SErwin T Tsaur return (rval != SUCCESS ? errno : SUCCESS); 1393d5ace945SErwin T Tsaur } 1394d5ace945SErwin T Tsaur 1395d5ace945SErwin T Tsaur /* *************** Interrupt routing ************** */ 1396d5ace945SErwin T Tsaur 1397d5ace945SErwin T Tsaur /* 1398d5ace945SErwin T Tsaur * Display interrupt information. 1399d5ace945SErwin T Tsaur * iget is filled in with the info to display 1400d5ace945SErwin T Tsaur */ 1401d5ace945SErwin T Tsaur static void 1402d5ace945SErwin T Tsaur print_intr_info(pcitool_intr_get_t *iget_p) 1403d5ace945SErwin T Tsaur { 1404d5ace945SErwin T Tsaur int i; 1405d5ace945SErwin T Tsaur 140609b1eac2SEvan Yan if (iget_p->flags & PCITOOL_INTR_FLAG_GET_MSI) 140709b1eac2SEvan Yan (void) printf("\nmsi 0x%x mapped to cpu 0x%x\n", 140809b1eac2SEvan Yan iget_p->msi, iget_p->cpu_id); 140909b1eac2SEvan Yan else 141009b1eac2SEvan Yan (void) printf("\nino 0x%x mapped to cpu 0x%x\n", 1411d5ace945SErwin T Tsaur iget_p->ino, iget_p->cpu_id); 141209b1eac2SEvan Yan 1413d5ace945SErwin T Tsaur for (i = 0; i < iget_p->num_devs; i++) { 1414d5ace945SErwin T Tsaur (void) printf("Device: %s\n", iget_p->dev[i].path); 1415d5ace945SErwin T Tsaur (void) printf(" Driver: %s, instance %d\n", 1416d5ace945SErwin T Tsaur iget_p->dev[i].driver_name, iget_p->dev[i].dev_inst); 1417d5ace945SErwin T Tsaur } 1418d5ace945SErwin T Tsaur } 1419d5ace945SErwin T Tsaur 1420d5ace945SErwin T Tsaur /* 1421d5ace945SErwin T Tsaur * Interrupt command support. 1422d5ace945SErwin T Tsaur * 1423d5ace945SErwin T Tsaur * fd is the file descriptor of the nexus being probed. 1424d5ace945SErwin T Tsaur * input_args are commandline options entered by the user. 1425d5ace945SErwin T Tsaur */ 1426d5ace945SErwin T Tsaur static int 1427d5ace945SErwin T Tsaur get_single_interrupt(int fd, pcitool_intr_get_t **iget_pp, 1428d5ace945SErwin T Tsaur pcitool_uiargs_t *input_args_p) 1429d5ace945SErwin T Tsaur { 1430d5ace945SErwin T Tsaur pcitool_intr_get_t *iget_p = *iget_pp; 143109b1eac2SEvan Yan const char *str_type = NULL; 143209b1eac2SEvan Yan uint32_t intr; 143309b1eac2SEvan Yan 143409b1eac2SEvan Yan if (input_args_p->flags & MSI_SPEC_FLAG) { 143509b1eac2SEvan Yan intr = input_args_p->intr_msi; 143609b1eac2SEvan Yan str_type = "msi"; 143709b1eac2SEvan Yan } else { 143809b1eac2SEvan Yan intr = input_args_p->intr_ino; 143909b1eac2SEvan Yan str_type = "ino"; 144009b1eac2SEvan Yan } 1441d5ace945SErwin T Tsaur 1442d5ace945SErwin T Tsaur /* 144309b1eac2SEvan Yan * Check if interrupts are active on this ino/msi. Get as much 1444d5ace945SErwin T Tsaur * device info as there is room for at the moment. If there 1445d5ace945SErwin T Tsaur * is not enough room for all devices, will call again with a 1446d5ace945SErwin T Tsaur * larger buffer. 1447d5ace945SErwin T Tsaur */ 1448d5ace945SErwin T Tsaur if (ioctl(fd, PCITOOL_DEVICE_GET_INTR, iget_p) != 0) { 1449d5ace945SErwin T Tsaur /* 1450d5ace945SErwin T Tsaur * Let EIO errors silently slip through, as 1451d5ace945SErwin T Tsaur * some inos may not be viewable by design. 1452d5ace945SErwin T Tsaur * We don't want to stop or print an error for these. 1453d5ace945SErwin T Tsaur */ 1454d5ace945SErwin T Tsaur if (errno == EIO) { 1455d5ace945SErwin T Tsaur return (SUCCESS); 1456d5ace945SErwin T Tsaur } 1457d5ace945SErwin T Tsaur 1458d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 145909b1eac2SEvan Yan (void) fprintf(stderr, "Ioctl to get %s 0x%x " 146009b1eac2SEvan Yan "info failed: %s\n", str_type, intr, 146109b1eac2SEvan Yan strerror(errno)); 146209b1eac2SEvan Yan 1463d5ace945SErwin T Tsaur if (errno != EFAULT) { 1464d5ace945SErwin T Tsaur (void) fprintf(stderr, "Pcitool status: %s\n", 1465d5ace945SErwin T Tsaur strstatus(iget_p->status)); 1466d5ace945SErwin T Tsaur } 1467d5ace945SErwin T Tsaur } 1468d5ace945SErwin T Tsaur return (errno); 1469d5ace945SErwin T Tsaur } 1470d5ace945SErwin T Tsaur 1471d5ace945SErwin T Tsaur /* Nothing to report for this interrupt. */ 1472d5ace945SErwin T Tsaur if (iget_p->num_devs == 0) { 1473d5ace945SErwin T Tsaur return (SUCCESS); 1474d5ace945SErwin T Tsaur } 1475d5ace945SErwin T Tsaur 1476d5ace945SErwin T Tsaur /* Need more room to return additional device info. */ 1477d5ace945SErwin T Tsaur if (iget_p->num_devs_ret < iget_p->num_devs) { 1478d5ace945SErwin T Tsaur iget_p = *iget_pp = 1479d5ace945SErwin T Tsaur realloc(iget_p, PCITOOL_IGET_SIZE(iget_p->num_devs)); 1480d5ace945SErwin T Tsaur iget_p->num_devs_ret = iget_p->num_devs; 148109b1eac2SEvan Yan 1482d5ace945SErwin T Tsaur if (ioctl(fd, PCITOOL_DEVICE_GET_INTR, iget_p) != 0) { 1483d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 148409b1eac2SEvan Yan (void) fprintf(stderr, "Ioctl to get %s 0x%x" 148509b1eac2SEvan Yan "device info failed: %s\n", str_type, 148609b1eac2SEvan Yan intr, strerror(errno)); 1487d5ace945SErwin T Tsaur if (errno != EFAULT) { 1488d5ace945SErwin T Tsaur (void) fprintf(stderr, 1489d5ace945SErwin T Tsaur "Pcitool status: %s\n", 1490d5ace945SErwin T Tsaur strstatus(iget_p->status)); 1491d5ace945SErwin T Tsaur } 1492d5ace945SErwin T Tsaur } 1493d5ace945SErwin T Tsaur return (errno); 1494d5ace945SErwin T Tsaur } 1495d5ace945SErwin T Tsaur } 1496d5ace945SErwin T Tsaur 1497d5ace945SErwin T Tsaur print_intr_info(iget_p); 1498d5ace945SErwin T Tsaur return (SUCCESS); 1499d5ace945SErwin T Tsaur } 1500d5ace945SErwin T Tsaur 1501d5ace945SErwin T Tsaur #define INIT_NUM_DEVS 0 1502d5ace945SErwin T Tsaur 1503d5ace945SErwin T Tsaur static int 1504d5ace945SErwin T Tsaur get_interrupts(int fd, pcitool_uiargs_t *input_args_p) 1505d5ace945SErwin T Tsaur { 1506d5ace945SErwin T Tsaur int rval = SUCCESS; /* Return status. */ 1507d5ace945SErwin T Tsaur 1508d5ace945SErwin T Tsaur /* 1509d5ace945SErwin T Tsaur * Start with a struct with space for info of INIT_NUM_DEVS devs 1510d5ace945SErwin T Tsaur * to be returned. 1511d5ace945SErwin T Tsaur */ 1512d5ace945SErwin T Tsaur pcitool_intr_get_t *iget_p = malloc(PCITOOL_IGET_SIZE(INIT_NUM_DEVS)); 1513d5ace945SErwin T Tsaur 1514d5ace945SErwin T Tsaur iget_p->num_devs_ret = INIT_NUM_DEVS; 1515d5ace945SErwin T Tsaur iget_p->user_version = PCITOOL_VERSION; 1516d5ace945SErwin T Tsaur 151709b1eac2SEvan Yan /* Explicit MSI requested. */ 151809b1eac2SEvan Yan if (input_args_p->flags & MSI_SPEC_FLAG) { 151909b1eac2SEvan Yan iget_p->msi = input_args_p->intr_msi; 152009b1eac2SEvan Yan iget_p->flags = PCITOOL_INTR_FLAG_GET_MSI; 1521d5ace945SErwin T Tsaur rval = get_single_interrupt(fd, &iget_p, input_args_p); 152209b1eac2SEvan Yan /* Return all MSIs. */ 152309b1eac2SEvan Yan } else if (input_args_p->flags & MSI_ALL_FLAG) { 1524d5ace945SErwin T Tsaur pcitool_intr_info_t intr_info; 152509b1eac2SEvan Yan intr_info.flags = PCITOOL_INTR_FLAG_GET_MSI; 1526d5ace945SErwin T Tsaur 1527d5ace945SErwin T Tsaur if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) { 1528d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 1529d5ace945SErwin T Tsaur (void) fprintf(stderr, 1530d5ace945SErwin T Tsaur "intr info ioctl failed: %s\n", 1531d5ace945SErwin T Tsaur strerror(errno)); 1532d5ace945SErwin T Tsaur } 1533d5ace945SErwin T Tsaur } else { 153409b1eac2SEvan Yan int msi; 1535d5ace945SErwin T Tsaur 153609b1eac2SEvan Yan /* 153709b1eac2SEvan Yan * Search through all interrupts. 153809b1eac2SEvan Yan * Display info on enabled ones. 153909b1eac2SEvan Yan */ 154009b1eac2SEvan Yan for (msi = 0; 154109b1eac2SEvan Yan ((msi < intr_info.num_intr) && (rval == SUCCESS)); 154209b1eac2SEvan Yan msi++) { 154309b1eac2SEvan Yan bzero(iget_p, sizeof (pcitool_intr_get_t)); 154409b1eac2SEvan Yan iget_p->num_devs_ret = INIT_NUM_DEVS; 154509b1eac2SEvan Yan iget_p->user_version = PCITOOL_VERSION; 154609b1eac2SEvan Yan iget_p->flags = PCITOOL_INTR_FLAG_GET_MSI; 154709b1eac2SEvan Yan iget_p->msi = msi; 154809b1eac2SEvan Yan rval = get_single_interrupt( 154909b1eac2SEvan Yan fd, &iget_p, input_args_p); 155009b1eac2SEvan Yan } 155109b1eac2SEvan Yan } 155209b1eac2SEvan Yan /* Explicit INO requested. */ 155309b1eac2SEvan Yan } else if (input_args_p->flags & INO_SPEC_FLAG) { 155409b1eac2SEvan Yan iget_p->ino = input_args_p->intr_ino; 155509b1eac2SEvan Yan rval = get_single_interrupt(fd, &iget_p, input_args_p); 155609b1eac2SEvan Yan /* Return all INOs. */ 155709b1eac2SEvan Yan } else if (input_args_p->flags & INO_ALL_FLAG) { 155809b1eac2SEvan Yan pcitool_intr_info_t intr_info; 155909b1eac2SEvan Yan intr_info.flags = 0; 156009b1eac2SEvan Yan 156109b1eac2SEvan Yan if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) { 156209b1eac2SEvan Yan if (!(IS_QUIET(input_args_p->flags))) { 156309b1eac2SEvan Yan (void) fprintf(stderr, 156409b1eac2SEvan Yan "intr info ioctl failed: %s\n", 156509b1eac2SEvan Yan strerror(errno)); 156609b1eac2SEvan Yan } 156709b1eac2SEvan Yan } else { 1568d5ace945SErwin T Tsaur int ino; 1569d5ace945SErwin T Tsaur 1570d5ace945SErwin T Tsaur /* 1571d5ace945SErwin T Tsaur * Search through all interrupts. 1572d5ace945SErwin T Tsaur * Display info on enabled ones. 1573d5ace945SErwin T Tsaur */ 1574d5ace945SErwin T Tsaur for (ino = 0; 1575d5ace945SErwin T Tsaur ((ino < intr_info.num_intr) && (rval == SUCCESS)); 1576d5ace945SErwin T Tsaur ino++) { 157709b1eac2SEvan Yan bzero(iget_p, sizeof (pcitool_intr_get_t)); 157809b1eac2SEvan Yan iget_p->num_devs_ret = INIT_NUM_DEVS; 157909b1eac2SEvan Yan iget_p->user_version = PCITOOL_VERSION; 1580d5ace945SErwin T Tsaur iget_p->ino = ino; 1581d5ace945SErwin T Tsaur rval = get_single_interrupt( 1582d5ace945SErwin T Tsaur fd, &iget_p, input_args_p); 1583d5ace945SErwin T Tsaur } 1584d5ace945SErwin T Tsaur } 1585d5ace945SErwin T Tsaur } 1586d5ace945SErwin T Tsaur 1587d5ace945SErwin T Tsaur free(iget_p); 1588d5ace945SErwin T Tsaur 1589d5ace945SErwin T Tsaur return (rval); 1590d5ace945SErwin T Tsaur } 1591d5ace945SErwin T Tsaur 1592d5ace945SErwin T Tsaur 1593d5ace945SErwin T Tsaur static int 1594d5ace945SErwin T Tsaur get_interrupt_ctlr(int fd, pcitool_uiargs_t *input_args_p) 1595d5ace945SErwin T Tsaur { 1596d5ace945SErwin T Tsaur pcitool_intr_info_t intr_info; 1597d5ace945SErwin T Tsaur char *ctlr_type = NULL; 1598d5ace945SErwin T Tsaur int rval = SUCCESS; 1599d5ace945SErwin T Tsaur 160009b1eac2SEvan Yan intr_info.flags = 0; 1601d5ace945SErwin T Tsaur if (ioctl(fd, PCITOOL_SYSTEM_INTR_INFO, &intr_info) != 0) { 1602d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 1603d5ace945SErwin T Tsaur (void) perror("Ioctl to get intr ctlr info failed"); 1604d5ace945SErwin T Tsaur } 1605d5ace945SErwin T Tsaur rval = errno; 1606d5ace945SErwin T Tsaur 1607d5ace945SErwin T Tsaur } else { 1608d5ace945SErwin T Tsaur (void) fputs("Controller type: ", stdout); 1609d5ace945SErwin T Tsaur switch (intr_info.ctlr_type) { 1610d5ace945SErwin T Tsaur case PCITOOL_CTLR_TYPE_RISC: 1611d5ace945SErwin T Tsaur ctlr_type = "RISC"; 1612d5ace945SErwin T Tsaur break; 1613d5ace945SErwin T Tsaur case PCITOOL_CTLR_TYPE_UPPC: 1614d5ace945SErwin T Tsaur ctlr_type = "UPPC"; 1615d5ace945SErwin T Tsaur break; 1616d5ace945SErwin T Tsaur case PCITOOL_CTLR_TYPE_PCPLUSMP: 1617d5ace945SErwin T Tsaur ctlr_type = "PCPLUSMP"; 1618d5ace945SErwin T Tsaur break; 1619d5ace945SErwin T Tsaur default: 1620d5ace945SErwin T Tsaur break; 1621d5ace945SErwin T Tsaur } 1622d5ace945SErwin T Tsaur 1623d5ace945SErwin T Tsaur if (ctlr_type == NULL) { 1624d5ace945SErwin T Tsaur (void) printf("Unknown or new (%d)", 1625d5ace945SErwin T Tsaur intr_info.ctlr_type); 1626d5ace945SErwin T Tsaur } else { 1627d5ace945SErwin T Tsaur (void) fputs(ctlr_type, stdout); 1628d5ace945SErwin T Tsaur } 1629d5ace945SErwin T Tsaur 1630d5ace945SErwin T Tsaur #ifdef __x86 1631d5ace945SErwin T Tsaur if (intr_info.ctlr_type == PCITOOL_CTLR_TYPE_PCPLUSMP) 1632d5ace945SErwin T Tsaur (void) printf(", IO APIC version: 0x%x, " 1633d5ace945SErwin T Tsaur "local APIC version: 0x%x\n", 1634d5ace945SErwin T Tsaur PSMAT_IO_APIC_VER(intr_info.ctlr_version), 1635d5ace945SErwin T Tsaur PSMAT_LOCAL_APIC_VER(intr_info.ctlr_version)); 1636d5ace945SErwin T Tsaur else 1637d5ace945SErwin T Tsaur #endif /* __x86 */ 1638d5ace945SErwin T Tsaur (void) printf(", version: %2.2x.%2.2x.%2.2x.%2.2x\n", 1639d5ace945SErwin T Tsaur ((intr_info.ctlr_version >> 24) & 0xff), 1640d5ace945SErwin T Tsaur ((intr_info.ctlr_version >> 16) & 0xff), 1641d5ace945SErwin T Tsaur ((intr_info.ctlr_version >> 8) & 0xff), 1642d5ace945SErwin T Tsaur (intr_info.ctlr_version & 0xff)); 1643d5ace945SErwin T Tsaur } 1644d5ace945SErwin T Tsaur 1645d5ace945SErwin T Tsaur return (rval); 1646d5ace945SErwin T Tsaur } 1647d5ace945SErwin T Tsaur 1648d5ace945SErwin T Tsaur /* 1649d5ace945SErwin T Tsaur * 1650d5ace945SErwin T Tsaur * fd is the file descriptor of the nexus being changed. 1651d5ace945SErwin T Tsaur * input_args are commandline options entered by the user. 1652d5ace945SErwin T Tsaur */ 1653d5ace945SErwin T Tsaur static int 1654d5ace945SErwin T Tsaur set_interrupts(int fd, pcitool_uiargs_t *input_args_p) 1655d5ace945SErwin T Tsaur { 165609b1eac2SEvan Yan pcitool_intr_set_t iset; 165709b1eac2SEvan Yan const char *str_type = NULL; 165809b1eac2SEvan Yan uint32_t intr; 1659d5ace945SErwin T Tsaur int rval = SUCCESS; /* Return status. */ 1660d5ace945SErwin T Tsaur 1661d5ace945SErwin T Tsaur /* Load interrupt number and cpu from commandline. */ 166209b1eac2SEvan Yan if (input_args_p->flags & MSI_SPEC_FLAG) { 166309b1eac2SEvan Yan iset.msi = intr = input_args_p->intr_msi; 166409b1eac2SEvan Yan iset.flags = PCITOOL_INTR_FLAG_SET_MSI; 166509b1eac2SEvan Yan str_type = "msi"; 166609b1eac2SEvan Yan } else { 166709b1eac2SEvan Yan iset.ino = intr = input_args_p->intr_ino; 166809b1eac2SEvan Yan iset.flags = 0; 166909b1eac2SEvan Yan str_type = "ino"; 167009b1eac2SEvan Yan } 167109b1eac2SEvan Yan 1672d5ace945SErwin T Tsaur iset.cpu_id = input_args_p->intr_cpu; 1673d5ace945SErwin T Tsaur iset.user_version = PCITOOL_VERSION; 167409b1eac2SEvan Yan iset.flags |= (input_args_p->flags & SETGRP_FLAG) ? 167509b1eac2SEvan Yan PCITOOL_INTR_FLAG_SET_GROUP : 0; 1676d5ace945SErwin T Tsaur 1677d5ace945SErwin T Tsaur /* Do the deed. */ 1678d5ace945SErwin T Tsaur if (ioctl(fd, PCITOOL_DEVICE_SET_INTR, &iset) != 0) { 1679d5ace945SErwin T Tsaur if (!(IS_QUIET(input_args_p->flags))) { 1680d5ace945SErwin T Tsaur (void) fprintf(stderr, 168109b1eac2SEvan Yan "Ioctl to set %s 0x%x failed: %s\n", 168209b1eac2SEvan Yan str_type, intr, strerror(errno)); 1683d5ace945SErwin T Tsaur (void) fprintf(stderr, "pcitool status: %s\n", 1684d5ace945SErwin T Tsaur strstatus(iset.status)); 1685d5ace945SErwin T Tsaur } 1686d5ace945SErwin T Tsaur rval = errno; 1687d5ace945SErwin T Tsaur } else { 1688d5ace945SErwin T Tsaur if (input_args_p->flags & SETGRP_FLAG) { 168909b1eac2SEvan Yan (void) printf("\nInterrupts on %s group starting " 169009b1eac2SEvan Yan "at %s 0x%x reassigned:", str_type, str_type, intr); 1691d5ace945SErwin T Tsaur } else { 169209b1eac2SEvan Yan (void) printf("\nInterrupts on %s 0x%x reassigned:", 169309b1eac2SEvan Yan str_type, intr); 1694d5ace945SErwin T Tsaur } 169509b1eac2SEvan Yan (void) printf(" Old cpu: 0x%x, New cpu: 0x%x\n", iset.cpu_id, 1696d5ace945SErwin T Tsaur input_args_p->intr_cpu); 1697d5ace945SErwin T Tsaur } 1698d5ace945SErwin T Tsaur 1699d5ace945SErwin T Tsaur return (rval); 1700d5ace945SErwin T Tsaur } 1701d5ace945SErwin T Tsaur 1702d5ace945SErwin T Tsaur 1703d5ace945SErwin T Tsaur static int 1704d5ace945SErwin T Tsaur do_interrupts(int fd, pcitool_uiargs_t *input_args_p) 1705d5ace945SErwin T Tsaur { 1706d5ace945SErwin T Tsaur if (input_args_p->flags & READ_FLAG) { 1707d5ace945SErwin T Tsaur 1708d5ace945SErwin T Tsaur int gic_rval; 1709d5ace945SErwin T Tsaur int gi_rval; 1710d5ace945SErwin T Tsaur 1711d5ace945SErwin T Tsaur if (input_args_p->flags & SHOWCTLR_FLAG) { 1712d5ace945SErwin T Tsaur gic_rval = get_interrupt_ctlr(fd, input_args_p); 1713d5ace945SErwin T Tsaur } 1714d5ace945SErwin T Tsaur 1715d5ace945SErwin T Tsaur gi_rval = get_interrupts(fd, input_args_p); 1716d5ace945SErwin T Tsaur return ((gi_rval != SUCCESS) ? gi_rval : gic_rval); 1717d5ace945SErwin T Tsaur 1718d5ace945SErwin T Tsaur } else { 1719d5ace945SErwin T Tsaur 1720d5ace945SErwin T Tsaur return (set_interrupts(fd, input_args_p)); 1721d5ace945SErwin T Tsaur } 1722d5ace945SErwin T Tsaur } 1723d5ace945SErwin T Tsaur 1724d5ace945SErwin T Tsaur 1725d5ace945SErwin T Tsaur /* *********** Where it all begins... ************* */ 1726d5ace945SErwin T Tsaur 1727d5ace945SErwin T Tsaur int 1728d5ace945SErwin T Tsaur main(int argc, char **argv) 1729d5ace945SErwin T Tsaur { 1730d5ace945SErwin T Tsaur pcitool_uiargs_t input_args; /* Commandline args. */ 1731d5ace945SErwin T Tsaur int fd; /* Nexus file descriptor. */ 1732d5ace945SErwin T Tsaur int rval = SUCCESS; /* Return status value. */ 1733d5ace945SErwin T Tsaur 1734d5ace945SErwin T Tsaur 1735d5ace945SErwin T Tsaur /* Get commandline args and options from user. */ 1736d5ace945SErwin T Tsaur if (get_commandline_args(argc, argv, &input_args) != SUCCESS) { 1737d5ace945SErwin T Tsaur return (EINVAL); 1738d5ace945SErwin T Tsaur } 1739d5ace945SErwin T Tsaur 1740d5ace945SErwin T Tsaur /* Help. */ 1741d5ace945SErwin T Tsaur if (!(input_args.flags & ALL_COMMANDS)) 1742d5ace945SErwin T Tsaur return (SUCCESS); 1743d5ace945SErwin T Tsaur 1744d5ace945SErwin T Tsaur /* 1745d5ace945SErwin T Tsaur * Probe mode. 1746d5ace945SErwin T Tsaur * Nexus is provided as argv[1] unless PROBEALL mode. 1747d5ace945SErwin T Tsaur */ 1748d5ace945SErwin T Tsaur if (input_args.flags & PROBE_FLAGS) { 1749d5ace945SErwin T Tsaur rval = do_probe_walk(&input_args, 1750d5ace945SErwin T Tsaur ((input_args.flags & PROBEALL_FLAG) ? NULL : argv[1])); 1751d5ace945SErwin T Tsaur 1752d5ace945SErwin T Tsaur } else if ((fd = open_node(argv[1], &input_args)) >= 0) { 1753d5ace945SErwin T Tsaur if (input_args.flags & (NEXUS_FLAG | LEAF_FLAG)) { 1754d5ace945SErwin T Tsaur (void) signal(SIGINT, signal_handler); 1755d5ace945SErwin T Tsaur (void) signal(SIGTERM, signal_handler); 1756d5ace945SErwin T Tsaur rval = do_device_or_nexus(fd, &input_args); 1757d5ace945SErwin T Tsaur } else if (input_args.flags & INTR_FLAG) { 1758d5ace945SErwin T Tsaur rval = do_interrupts(fd, &input_args); 1759d5ace945SErwin T Tsaur } else { 1760d5ace945SErwin T Tsaur /* Should never see this. */ 1761d5ace945SErwin T Tsaur (void) fprintf(stderr, "Nothing to do.\n"); 1762d5ace945SErwin T Tsaur rval = ENOTTY; 1763d5ace945SErwin T Tsaur } 1764d5ace945SErwin T Tsaur 1765d5ace945SErwin T Tsaur (void) close(fd); 1766d5ace945SErwin T Tsaur } 1767d5ace945SErwin T Tsaur 1768d5ace945SErwin T Tsaur return (rval); 1769d5ace945SErwin T Tsaur } 1770