1124196Sbms/*
2164124Sbms * Copyright (c) 2002-2006 Bruce M. Simpson.
3124196Sbms * All rights reserved
4124196Sbms *
5124196Sbms * Redistribution and use in source and binary forms, with or without
6124196Sbms * modification, are permitted provided that the following conditions
7124196Sbms * are met:
8124196Sbms * 1. Redistributions of source code must retain the above copyright
9124196Sbms *    notice, this list of conditions and the following disclaimer.
10124196Sbms * 2. Redistributions in binary form must reproduce the above copyright
11124196Sbms *    notice, this list of conditions and the following disclaimer in the
12124196Sbms *    documentation and/or other materials provided with the distribution.
13165935Simp * 3. Neither the name of Bruce M. Simpson nor the names of
14124196Sbms *    contributors may be used to endorse or promote products derived
15124196Sbms *    from this software without specific prior written permission.
16124196Sbms *
17124196Sbms * THIS SOFTWARE IS PROVIDED BY BRUCE M. SIMPSON AND AFFILIATES
18124196Sbms * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19124196Sbms * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20124196Sbms * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21124196Sbms * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22124196Sbms * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23124196Sbms * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24124196Sbms * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25124196Sbms * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26124196Sbms * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27124196Sbms * POSSIBILITY OF SUCH DAMAGE.
28124196Sbms *
29124196Sbms */
30124196Sbms#include <sys/cdefs.h>
31124196Sbms__FBSDID("$FreeBSD: releng/10.3/tools/tools/pirtool/pirtool.c 165935 2007-01-11 05:00:30Z imp $");
32124196Sbms
33124196Sbms#include <sys/types.h>
34124196Sbms#include <sys/ioctl.h>
35124196Sbms#include <sys/mman.h>
36124196Sbms#include <sys/memrange.h>
37124196Sbms#include <sys/stat.h>
38124196Sbms#include <machine/endian.h>
39124196Sbms
40124196Sbms#include <stddef.h>
41124196Sbms#include <stdio.h>
42124196Sbms#include <stdlib.h>
43124196Sbms#include <libgen.h>
44124196Sbms#include <fcntl.h>
45124196Sbms#include <string.h>
46124196Sbms#include <unistd.h>
47124196Sbms
48124196Sbms#include "pirtable.h"
49124196Sbms
50124196Sbms#define _PATH_DEVMEM	"/dev/mem"
51124196Sbms
52124196Sbmsvoid usage(void);
53124196Sbmsvoid banner(void);
54124196Sbmspir_table_t *find_pir_table(unsigned char *base);
55124196Sbmsvoid dump_pir_table(pir_table_t *pir, char *map_addr);
56164123Sbmsvoid pci_print_irqmask(uint16_t irqs);
57164123Sbmsvoid print_irq_line(int entry, pir_entry_t *p, char line, uint8_t link,
58164123Sbms    uint16_t irqs);
59164123Sbmschar *lookup_southbridge(uint32_t id);
60124196Sbms
61124196Sbmschar *progname = NULL;
62124196Sbms
63124196Sbmsint
64124196Sbmsmain(int argc, char *argv[])
65124196Sbms{
66124196Sbms	int ch, r;
67124196Sbms	int err = -1;
68124196Sbms	int mem_fd = -1;
69124196Sbms	pir_table_t *pir = NULL;
70124196Sbms	void *map_addr = MAP_FAILED;
71124196Sbms	char *real_pir;
72124196Sbms
73124196Sbms	progname = basename(argv[0]);
74124196Sbms	while ((ch = getopt(argc, argv, "h")) != -1)
75124196Sbms		switch (ch) {
76124196Sbms		case 'h':
77124196Sbms		default:
78124196Sbms			usage();
79124196Sbms	}
80124196Sbms	argc -= optind;
81124196Sbms	argv += optind;
82124196Sbms
83124196Sbms	if (argc > 0)
84124196Sbms		usage();
85124196Sbms
86124196Sbms	banner();
87124196Sbms	/*
88124196Sbms	 * Map the PIR region into our process' linear space.
89124196Sbms	 */
90124196Sbms	if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
91124196Sbms		perror("open");
92124196Sbms		goto cleanup;
93124196Sbms	}
94124196Sbms	map_addr = mmap(NULL, PIR_SIZE, PROT_READ, MAP_SHARED, mem_fd,
95124196Sbms	    PIR_BASE);
96124196Sbms	if (map_addr == MAP_FAILED) {
97124196Sbms		perror("mmap");
98124196Sbms		goto cleanup;
99124196Sbms	}
100124196Sbms	/*
101124196Sbms	 * Find and print the PIR table.
102124196Sbms	 */
103140583Sbms	if ((pir = find_pir_table(map_addr)) == NULL) {
104124196Sbms		fprintf(stderr, "PIR table signature not found.\r\n");
105124196Sbms	} else {
106124196Sbms		dump_pir_table(pir, map_addr);
107124196Sbms		err = 0;
108124196Sbms	}
109124196Sbms
110124196Sbmscleanup:
111124196Sbms	if (map_addr != MAP_FAILED)
112124196Sbms		munmap(map_addr, PIR_SIZE);
113124196Sbms	if (mem_fd != -1)
114124196Sbms		close(mem_fd);
115124196Sbms
116124196Sbms	exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
117124196Sbms}
118124196Sbms
119124196Sbmsvoid
120124196Sbmsusage(void)
121124196Sbms{
122124196Sbms
123124196Sbms	fprintf(stderr, "usage: %s [-h]\r\n", progname);
124124196Sbms	fprintf(stderr, "-h\tdisplay this message\r\n", progname);
125124196Sbms	exit(EXIT_FAILURE);
126124196Sbms}
127124196Sbms
128124196Sbmsvoid
129124196Sbmsbanner(void)
130124196Sbms{
131124196Sbms
132164122Sbms	fprintf(stderr, "PIRTOOL (c) 2002-2006 Bruce M. Simpson\r\n");
133124196Sbms	fprintf(stderr,
134124196Sbms	    "---------------------------------------------\r\n\r\n");
135124196Sbms}
136124196Sbms
137124196Sbmspir_table_t *
138124196Sbmsfind_pir_table(unsigned char *base)
139124196Sbms{
140124196Sbms	unsigned int csum = 0;
141124196Sbms	unsigned char *p, *pend;
142124196Sbms	pir_table_t *pir = NULL;
143124196Sbms
144124196Sbms	/*
145124196Sbms	 * From Microsoft's PCI IRQ Routing Table Specification 1.0:
146124196Sbms	 *
147124196Sbms	 * The PCI IRQ Routing Table can be detected by searching the
148124196Sbms	 * system memory from F0000h to FFFFFh at every 16-byte boundary
149124196Sbms	 * for the PCI IRQ routing signature ("$PIR").
150124196Sbms	 */
151124196Sbms	pend = base + PIR_SIZE;
152124196Sbms	for (p = base; p < pend; p += 16) {
153124196Sbms		if (strncmp(p, "$PIR", 4) == 0) {
154140583Sbms			pir = (pir_table_t *)p;
155124196Sbms			break;
156124196Sbms		}
157124196Sbms	}
158124196Sbms
159124196Sbms	/*
160124196Sbms	 * Now validate the table:
161124196Sbms	 * Version: Must be 1.0.
162124196Sbms	 * Table size: Must be larger than 32 and must be a multiple of 16.
163124196Sbms	 * Checksum: The entire structure's checksum must be 0.
164124196Sbms	 */
165124196Sbms	if (pir && (pir->major == 1) && (pir->minor == 0) &&
166124196Sbms	    (pir->size > 32) && ((pir->size % 16) == 0)) {
167124196Sbms		p = (unsigned char *)pir;
168124196Sbms		pend = p + pir->size;
169124196Sbms
170124196Sbms		while (p < pend)
171124196Sbms			csum += *p++;
172124196Sbms
173124196Sbms		if ((csum % 256) != 0)
174140583Sbms			fprintf(stderr,
175140583Sbms			    "WARNING: PIR table checksum is invalid.\n");
176124196Sbms	}
177124196Sbms
178124196Sbms	return ((pir_table_t *)pir);
179124196Sbms}
180124196Sbms
181124196Sbmsvoid
182164123Sbmspci_print_irqmask(uint16_t irqs)
183164122Sbms{
184164122Sbms	int i, first;
185164122Sbms
186164122Sbms	if (irqs == 0) {
187164122Sbms		printf("none");
188164122Sbms		return;
189164122Sbms	}
190164122Sbms	first = 1;
191164122Sbms	for (i = 0; i < 16; i++, irqs >>= 1)
192164122Sbms		if (irqs & 1) {
193164122Sbms			if (!first)
194164122Sbms				printf(" ");
195164122Sbms			else
196164122Sbms				first = 0;
197164122Sbms			printf("%d", i);
198164122Sbms		}
199164122Sbms}
200164122Sbms
201164122Sbmsvoid
202124196Sbmsdump_pir_table(pir_table_t *pir, char *map_addr)
203124196Sbms{
204124196Sbms	int i, num_slots;
205124196Sbms	pir_entry_t *p, *pend;
206124196Sbms
207124196Sbms	num_slots = (pir->size - offsetof(pir_table_t, entry[0])) / 16;
208124196Sbms
209124196Sbms	printf( "PCI Interrupt Routing Table at 0x%08lX\r\n"
210124196Sbms	    "-----------------------------------------\r\n"
211124196Sbms	    "0x%02x: Signature:          %c%c%c%c\r\n"
212124196Sbms	    "0x%02x: Version:            %u.%u\r\n"
213124196Sbms	    "0x%02x: Size:               %u bytes (%u entries)\r\n"
214164122Sbms	    "0x%02x: Device:             %u:%u:%u\r\n",
215164123Sbms	    (uint32_t)(((char *)pir - map_addr) + PIR_BASE),
216124196Sbms	    offsetof(pir_table_t, signature),
217124196Sbms	    ((char *)&pir->signature)[0],
218124196Sbms	    ((char *)&pir->signature)[1],
219124196Sbms	    ((char *)&pir->signature)[2],
220124196Sbms	    ((char *)&pir->signature)[3],
221124196Sbms	    offsetof(pir_table_t, minor),
222124196Sbms	    pir->major, pir->minor,
223124196Sbms	    offsetof(pir_table_t, size),
224124196Sbms	    pir->size,
225124196Sbms	    num_slots,
226124196Sbms	    offsetof(pir_table_t, bus),
227124196Sbms	    pir->bus,
228124196Sbms	    PIR_DEV(pir->devfunc),
229164122Sbms	    PIR_FUNC(pir->devfunc));
230164122Sbms	printf(
231164122Sbms	    "0x%02x: PCI Exclusive IRQs: ",
232164122Sbms	     offsetof(pir_table_t, excl_irqs));
233164122Sbms	pci_print_irqmask(pir->excl_irqs);
234164122Sbms	printf("\r\n"
235164122Sbms	    "0x%02x: Compatible with:    0x%08X %s\r\n"
236164122Sbms	    "0x%02x: Miniport Data:      0x%08X\r\n"
237164122Sbms	    "0x%02x: Checksum:           0x%02X\r\n"
238164122Sbms	    "\r\n",
239124196Sbms	    offsetof(pir_table_t, compatible),
240124196Sbms	    pir->compatible,
241124196Sbms	    lookup_southbridge(pir->compatible),
242124196Sbms	    offsetof(pir_table_t, miniport_data),
243124196Sbms	    pir->miniport_data,
244124196Sbms	    offsetof(pir_table_t, checksum),
245124196Sbms	    pir->checksum);
246124196Sbms
247124196Sbms	p = pend = &pir->entry[0];
248124196Sbms	pend += num_slots;
249164122Sbms	printf("Entry  Location  Bus Device Pin  Link  IRQs\n");
250124196Sbms	for (i = 0; p < pend; i++, p++) {
251164122Sbms		print_irq_line(i, p, 'A', p->inta_link, p->inta_irqs);
252164122Sbms		print_irq_line(i, p, 'B', p->intb_link, p->intb_irqs);
253164122Sbms		print_irq_line(i, p, 'C', p->intc_link, p->intc_irqs);
254164122Sbms		print_irq_line(i, p, 'D', p->intd_link, p->intd_irqs);
255124196Sbms	}
256124196Sbms}
257124196Sbms
258124196Sbms/*
259124196Sbms * Print interrupt map for a given PCI interrupt line.
260124196Sbms */
261124196Sbmsvoid
262164123Sbmsprint_irq_line(int entry, pir_entry_t *p, char line, uint8_t link,
263164123Sbms    uint16_t irqs)
264124196Sbms{
265124196Sbms
266164122Sbms	if (link == 0)
267124196Sbms		return;
268124196Sbms
269164122Sbms	printf("%3d    ", entry);
270164122Sbms	if (p->slot == 0)
271164122Sbms		printf("embedded ");
272164122Sbms	else
273164122Sbms		printf("slot %-3d ", p->slot);
274124196Sbms
275164122Sbms	printf(" %3d  %3d    %c   0x%02x  ", p->bus, PIR_DEV(p->devfunc),
276164122Sbms	    line, link);
277164122Sbms	pci_print_irqmask(irqs);
278164122Sbms	printf("\n");
279124196Sbms}
280124196Sbms
281124196Sbms/*
282124196Sbms * Lookup textual descriptions for commonly-used south-bridges.
283124196Sbms */
284124196Sbmschar *
285164123Sbmslookup_southbridge(uint32_t id)
286124196Sbms{
287124196Sbms
288124196Sbms	switch (id) {
289164122Sbms	case 0x157310b9:
290164122Sbms		return ("ALi M1573 (Hypertransport)");
291140585Sbms	case 0x06861106:
292164122Sbms		return ("VIA VT82C686/686A/686B (Apollo)");
293124196Sbms	case 0x122E8086:
294164122Sbms		return ("Intel 82371FB (Triton I/PIIX)");
295164122Sbms	case 0x26418086:
296164122Sbms		return ("Intel 82801FBM (ICH6M)");
297124196Sbms	case 0x70008086:
298164122Sbms		return ("Intel 82371SB (Natoma/Triton II/PIIX3)");
299124196Sbms	default:
300124196Sbms		return ("unknown chipset");
301124196Sbms	}
302124196Sbms}
303