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