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