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 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