xref: /freebsd/tools/tools/pirtool/pirtool.c (revision b00ab754)
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2002-2006 Bruce M. Simpson.
5  * All rights reserved
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of Bruce M. Simpson nor the names of
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY BRUCE M. SIMPSON AND AFFILIATES
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/memrange.h>
39 #include <sys/stat.h>
40 #include <machine/endian.h>
41 
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <libgen.h>
46 #include <fcntl.h>
47 #include <string.h>
48 #include <unistd.h>
49 
50 #include "pirtable.h"
51 
52 #define _PATH_DEVMEM	"/dev/mem"
53 
54 void usage(void);
55 void banner(void);
56 pir_table_t *find_pir_table(unsigned char *base);
57 void dump_pir_table(pir_table_t *pir, char *map_addr);
58 void pci_print_irqmask(uint16_t irqs);
59 void print_irq_line(int entry, pir_entry_t *p, char line, uint8_t link,
60     uint16_t irqs);
61 char *lookup_southbridge(uint32_t id);
62 
63 char *progname = NULL;
64 
65 int
66 main(int argc, char *argv[])
67 {
68 	int ch, r;
69 	int err = -1;
70 	int mem_fd = -1;
71 	pir_table_t *pir = NULL;
72 	void *map_addr = MAP_FAILED;
73 	char *real_pir;
74 
75 	progname = basename(argv[0]);
76 	while ((ch = getopt(argc, argv, "h")) != -1)
77 		switch (ch) {
78 		case 'h':
79 		default:
80 			usage();
81 	}
82 	argc -= optind;
83 	argv += optind;
84 
85 	if (argc > 0)
86 		usage();
87 
88 	banner();
89 	/*
90 	 * Map the PIR region into our process' linear space.
91 	 */
92 	if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
93 		perror("open");
94 		goto cleanup;
95 	}
96 	map_addr = mmap(NULL, PIR_SIZE, PROT_READ, MAP_SHARED, mem_fd,
97 	    PIR_BASE);
98 	if (map_addr == MAP_FAILED) {
99 		perror("mmap");
100 		goto cleanup;
101 	}
102 	/*
103 	 * Find and print the PIR table.
104 	 */
105 	if ((pir = find_pir_table(map_addr)) == NULL) {
106 		fprintf(stderr, "PIR table signature not found.\r\n");
107 	} else {
108 		dump_pir_table(pir, map_addr);
109 		err = 0;
110 	}
111 
112 cleanup:
113 	if (map_addr != MAP_FAILED)
114 		munmap(map_addr, PIR_SIZE);
115 	if (mem_fd != -1)
116 		close(mem_fd);
117 
118 	exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
119 }
120 
121 void
122 usage(void)
123 {
124 
125 	fprintf(stderr, "usage: %s [-h]\r\n", progname);
126 	fprintf(stderr, "-h\tdisplay this message\r\n", progname);
127 	exit(EXIT_FAILURE);
128 }
129 
130 void
131 banner(void)
132 {
133 
134 	fprintf(stderr, "PIRTOOL (c) 2002-2006 Bruce M. Simpson\r\n");
135 	fprintf(stderr,
136 	    "---------------------------------------------\r\n\r\n");
137 }
138 
139 pir_table_t *
140 find_pir_table(unsigned char *base)
141 {
142 	unsigned int csum = 0;
143 	unsigned char *p, *pend;
144 	pir_table_t *pir = NULL;
145 
146 	/*
147 	 * From Microsoft's PCI IRQ Routing Table Specification 1.0:
148 	 *
149 	 * The PCI IRQ Routing Table can be detected by searching the
150 	 * system memory from F0000h to FFFFFh at every 16-byte boundary
151 	 * for the PCI IRQ routing signature ("$PIR").
152 	 */
153 	pend = base + PIR_SIZE;
154 	for (p = base; p < pend; p += 16) {
155 		if (strncmp(p, "$PIR", 4) == 0) {
156 			pir = (pir_table_t *)p;
157 			break;
158 		}
159 	}
160 
161 	/*
162 	 * Now validate the table:
163 	 * Version: Must be 1.0.
164 	 * Table size: Must be larger than 32 and must be a multiple of 16.
165 	 * Checksum: The entire structure's checksum must be 0.
166 	 */
167 	if (pir && (pir->major == 1) && (pir->minor == 0) &&
168 	    (pir->size > 32) && ((pir->size % 16) == 0)) {
169 		p = (unsigned char *)pir;
170 		pend = p + pir->size;
171 
172 		while (p < pend)
173 			csum += *p++;
174 
175 		if ((csum % 256) != 0)
176 			fprintf(stderr,
177 			    "WARNING: PIR table checksum is invalid.\n");
178 	}
179 
180 	return ((pir_table_t *)pir);
181 }
182 
183 void
184 pci_print_irqmask(uint16_t irqs)
185 {
186 	int i, first;
187 
188 	if (irqs == 0) {
189 		printf("none");
190 		return;
191 	}
192 	first = 1;
193 	for (i = 0; i < 16; i++, irqs >>= 1)
194 		if (irqs & 1) {
195 			if (!first)
196 				printf(" ");
197 			else
198 				first = 0;
199 			printf("%d", i);
200 		}
201 }
202 
203 void
204 dump_pir_table(pir_table_t *pir, char *map_addr)
205 {
206 	int i, num_slots;
207 	pir_entry_t *p, *pend;
208 
209 	num_slots = (pir->size - offsetof(pir_table_t, entry[0])) / 16;
210 
211 	printf( "PCI Interrupt Routing Table at 0x%08lX\r\n"
212 	    "-----------------------------------------\r\n"
213 	    "0x%02x: Signature:          %c%c%c%c\r\n"
214 	    "0x%02x: Version:            %u.%u\r\n"
215 	    "0x%02x: Size:               %u bytes (%u entries)\r\n"
216 	    "0x%02x: Device:             %u:%u:%u\r\n",
217 	    (uint32_t)(((char *)pir - map_addr) + PIR_BASE),
218 	    offsetof(pir_table_t, signature),
219 	    ((char *)&pir->signature)[0],
220 	    ((char *)&pir->signature)[1],
221 	    ((char *)&pir->signature)[2],
222 	    ((char *)&pir->signature)[3],
223 	    offsetof(pir_table_t, minor),
224 	    pir->major, pir->minor,
225 	    offsetof(pir_table_t, size),
226 	    pir->size,
227 	    num_slots,
228 	    offsetof(pir_table_t, bus),
229 	    pir->bus,
230 	    PIR_DEV(pir->devfunc),
231 	    PIR_FUNC(pir->devfunc));
232 	printf(
233 	    "0x%02x: PCI Exclusive IRQs: ",
234 	     offsetof(pir_table_t, excl_irqs));
235 	pci_print_irqmask(pir->excl_irqs);
236 	printf("\r\n"
237 	    "0x%02x: Compatible with:    0x%08X %s\r\n"
238 	    "0x%02x: Miniport Data:      0x%08X\r\n"
239 	    "0x%02x: Checksum:           0x%02X\r\n"
240 	    "\r\n",
241 	    offsetof(pir_table_t, compatible),
242 	    pir->compatible,
243 	    lookup_southbridge(pir->compatible),
244 	    offsetof(pir_table_t, miniport_data),
245 	    pir->miniport_data,
246 	    offsetof(pir_table_t, checksum),
247 	    pir->checksum);
248 
249 	p = pend = &pir->entry[0];
250 	pend += num_slots;
251 	printf("Entry  Location  Bus Device Pin  Link  IRQs\n");
252 	for (i = 0; p < pend; i++, p++) {
253 		print_irq_line(i, p, 'A', p->inta_link, p->inta_irqs);
254 		print_irq_line(i, p, 'B', p->intb_link, p->intb_irqs);
255 		print_irq_line(i, p, 'C', p->intc_link, p->intc_irqs);
256 		print_irq_line(i, p, 'D', p->intd_link, p->intd_irqs);
257 	}
258 }
259 
260 /*
261  * Print interrupt map for a given PCI interrupt line.
262  */
263 void
264 print_irq_line(int entry, pir_entry_t *p, char line, uint8_t link,
265     uint16_t irqs)
266 {
267 
268 	if (link == 0)
269 		return;
270 
271 	printf("%3d    ", entry);
272 	if (p->slot == 0)
273 		printf("embedded ");
274 	else
275 		printf("slot %-3d ", p->slot);
276 
277 	printf(" %3d  %3d    %c   0x%02x  ", p->bus, PIR_DEV(p->devfunc),
278 	    line, link);
279 	pci_print_irqmask(irqs);
280 	printf("\n");
281 }
282 
283 /*
284  * Lookup textual descriptions for commonly-used south-bridges.
285  */
286 char *
287 lookup_southbridge(uint32_t id)
288 {
289 
290 	switch (id) {
291 	case 0x157310b9:
292 		return ("ALi M1573 (Hypertransport)");
293 	case 0x06861106:
294 		return ("VIA VT82C686/686A/686B (Apollo)");
295 	case 0x122E8086:
296 		return ("Intel 82371FB (Triton I/PIIX)");
297 	case 0x26418086:
298 		return ("Intel 82801FBM (ICH6M)");
299 	case 0x70008086:
300 		return ("Intel 82371SB (Natoma/Triton II/PIIX3)");
301 	default:
302 		return ("unknown chipset");
303 	}
304 }
305