pirtool.c revision 330897
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: stable/11/tools/tools/pirtool/pirtool.c 330897 2018-03-14 03:19:51Z eadler $");
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
54void usage(void);
55void banner(void);
56pir_table_t *find_pir_table(unsigned char *base);
57void dump_pir_table(pir_table_t *pir, char *map_addr);
58void pci_print_irqmask(uint16_t irqs);
59void print_irq_line(int entry, pir_entry_t *p, char line, uint8_t link,
60    uint16_t irqs);
61char *lookup_southbridge(uint32_t id);
62
63char *progname = NULL;
64
65int
66main(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
112cleanup:
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
121void
122usage(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
130void
131banner(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
139pir_table_t *
140find_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
183void
184pci_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
203void
204dump_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 */
263void
264print_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 */
286char *
287lookup_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