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