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