pciroms.c revision 167167
1167167Sbms/* 2167167Sbms * Copyright (c) 2007 Bruce M. Simpson. 3167167Sbms * All rights reserved 4167167Sbms * 5167167Sbms * Redistribution and use in source and binary forms, with or without 6167167Sbms * modification, are permitted provided that the following conditions 7167167Sbms * are met: 8167167Sbms * 1. Redistributions of source code must retain the above copyright 9167167Sbms * notice, this list of conditions and the following disclaimer. 10167167Sbms * 2. Redistributions in binary form must reproduce the above copyright 11167167Sbms * notice, this list of conditions and the following disclaimer in the 12167167Sbms * documentation and/or other materials provided with the distribution. 13167167Sbms * 3. All advertising materials mentioning features or use of this software 14167167Sbms * must display the following acknowledgement: 15167167Sbms * This product includes software developed by Bruce M. Simpson. 16167167Sbms * 4. Neither the name of Bruce M. Simpson nor the names of 17167167Sbms * contributors may be used to endorse or promote products derived 18167167Sbms * from this software without specific prior written permission. 19167167Sbms * 20167167Sbms * THIS SOFTWARE IS PROVIDED BY BRUCE M. SIMPSON AND AFFILIATES 21167167Sbms * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22167167Sbms * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23167167Sbms * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24167167Sbms * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25167167Sbms * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26167167Sbms * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27167167Sbms * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28167167Sbms * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29167167Sbms * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30167167Sbms * POSSIBILITY OF SUCH DAMAGE. 31167167Sbms * 32167167Sbms */ 33167167Sbms 34167167Sbms#include <sys/cdefs.h> 35167167Sbms__FBSDID("$FreeBSD: head/tools/tools/pciroms/pciroms.c 167167 2007-03-02 13:53:23Z bms $"); 36167167Sbms 37167167Sbms#include <sys/types.h> 38167167Sbms#include <sys/ioctl.h> 39167167Sbms#include <sys/pciio.h> 40167167Sbms#include <sys/mman.h> 41167167Sbms#include <sys/memrange.h> 42167167Sbms#include <sys/stat.h> 43167167Sbms#include <machine/endian.h> 44167167Sbms 45167167Sbms#include <stddef.h> 46167167Sbms#include <inttypes.h> 47167167Sbms#include <stdio.h> 48167167Sbms#include <stdlib.h> 49167167Sbms#include <libgen.h> 50167167Sbms#include <fcntl.h> 51167167Sbms#include <string.h> 52167167Sbms#include <unistd.h> 53167167Sbms 54167167Sbms#define _PATH_DEVPCI "/dev/pci" 55167167Sbms#define _PATH_DEVMEM "/dev/mem" 56167167Sbms 57167167Sbms#define PCI_CFG_CMD 0x04 /* command register */ 58167167Sbms#define PCI_CFG_ROM_BAR 0x30 /* rom base register */ 59167167Sbms 60167167Sbms#define PCI_ROM_ADDR_MASK 0xFFFFFC00 /* the 21 MSBs form the BAR */ 61167167Sbms#define PCI_ROM_RESERVED_MASK 0x03FE /* mask for reserved bits */ 62167167Sbms#define PCI_ROM_ACTIVATE 0x01 /* mask for activation bit */ 63167167Sbms 64167167Sbms#define PCI_CMD_MEM_SPACE 0x02 /* memory space bit */ 65167167Sbms#define PCI_HDRTYPE_MFD 0x80 /* MFD bit in HDRTYPE reg. */ 66167167Sbms 67167167Sbms#define MAX_PCI_DEVS 64 /* # of devices in system */ 68167167Sbms 69167167Sbmstypedef enum { 70167167Sbms PRINT = 0, 71167167Sbms SAVE = 1 72167167Sbms} action_t; 73167167Sbms 74167167Sbms/* 75167167Sbms * This is set to a safe physical base address in PCI range for my Vaio. 76167167Sbms * YOUR MACHINE *WILL* VARY, I SUGGEST YOU LOOK UP YOUR MACHINE'S MEMORY 77167167Sbms * MAP IN DETAIL IF YOU PLAN ON SAVING ROMS. 78167167Sbms * 79167167Sbms * This is the hole between the APIC and the BIOS (FED00000-FEDFFFFF); 80167167Sbms * should be a safe range on the i815 Solano chipset. 81167167Sbms */ 82167167Sbms#define PCI_DEFAULT_ROM_ADDR 0xFED00000 83167167Sbms 84167167Sbmsstatic char *progname = NULL; 85167167Sbmsstatic uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR; 86167167Sbms 87167167Sbmsstatic void usage(void); 88167167Sbmsstatic void banner(void); 89167167Sbmsstatic void pci_enum_devs(int pci_fd, action_t action); 90167167Sbmsstatic uint32_t pci_testrombar(int pci_fd, struct pci_conf *dev); 91167167Sbmsstatic int pci_enable_bars(int pci_fd, struct pci_conf *dev, 92167167Sbms uint16_t *oldcmd); 93167167Sbmsstatic int pci_disable_bars(int pci_fd, struct pci_conf *dev, 94167167Sbms uint16_t *oldcmd); 95167167Sbmsstatic int pci_save_rom(char *filename, int romsize); 96167167Sbms 97167167Sbmsint 98167167Sbmsmain(int argc, char *argv[]) 99167167Sbms{ 100167167Sbms int pci_fd; 101167167Sbms int err; 102167167Sbms int ch; 103167167Sbms action_t action; 104167167Sbms char *base_addr_string; 105167167Sbms char *ep; 106167167Sbms 107167167Sbms err = -1; 108167167Sbms pci_fd = -1; 109167167Sbms action = PRINT; 110167167Sbms base_addr_string = NULL; 111167167Sbms ep = NULL; 112167167Sbms progname = basename(argv[0]); 113167167Sbms 114167167Sbms while ((ch = getopt(argc, argv, "sb:h")) != -1) 115167167Sbms switch (ch) { 116167167Sbms case 's': 117167167Sbms action = SAVE; 118167167Sbms break; 119167167Sbms case 'b': 120167167Sbms base_addr_string = optarg; 121167167Sbms break; 122167167Sbms case 'h': 123167167Sbms default: 124167167Sbms usage(); 125167167Sbms } 126167167Sbms argc -= optind; 127167167Sbms argv += optind; 128167167Sbms 129167167Sbms if (base_addr_string != NULL) { 130167167Sbms uintmax_t base_addr_max; 131167167Sbms 132167167Sbms base_addr_max = strtoumax(base_addr_string, &ep, 16); 133167167Sbms if (*ep != '\0') { 134167167Sbms fprintf(stderr, "Invalid base address.\r\n"); 135167167Sbms usage(); 136167167Sbms } 137167167Sbms /* XXX: TODO: deal with 64-bit PCI. */ 138167167Sbms base_addr = (uintptr_t)base_addr_max; 139167167Sbms base_addr &= ~PCI_ROM_RESERVED_MASK; 140167167Sbms } 141167167Sbms 142167167Sbms if (argc > 0) 143167167Sbms usage(); 144167167Sbms 145167167Sbms if ((pci_fd = open(_PATH_DEVPCI, O_RDWR)) == -1) { 146167167Sbms perror("open"); 147167167Sbms goto cleanup; 148167167Sbms } 149167167Sbms 150167167Sbms banner(); 151167167Sbms pci_enum_devs(pci_fd, action); 152167167Sbms 153167167Sbms err = 0; 154167167Sbmscleanup: 155167167Sbms if (pci_fd != -1) 156167167Sbms close(pci_fd); 157167167Sbms 158167167Sbms exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE); 159167167Sbms} 160167167Sbms 161167167Sbmsstatic void 162167167Sbmsusage(void) 163167167Sbms{ 164167167Sbms 165167167Sbms fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname); 166167167Sbms exit(EXIT_FAILURE); 167167167Sbms} 168167167Sbms 169167167Sbmsstatic void 170167167Sbmsbanner(void) 171167167Sbms{ 172167167Sbms 173167167Sbms fprintf(stderr, 174167167Sbms "WARNING: You are advised to run this program in single\r\n" 175167167Sbms "user mode, with few or no processes running.\r\n\r\n"); 176167167Sbms} 177167167Sbms 178167167Sbms/* 179167167Sbms * Enumerate PCI device list to a limit of MAX_PCI_DEVS devices. 180167167Sbms */ 181167167Sbmsstatic void 182167167Sbmspci_enum_devs(int pci_fd, action_t action) 183167167Sbms{ 184167167Sbms struct pci_conf devs[MAX_PCI_DEVS]; 185167167Sbms char filename[16]; 186167167Sbms struct pci_conf_io pc; 187167167Sbms struct pci_conf *p; 188167167Sbms int result; 189167167Sbms int romsize; 190167167Sbms uint16_t oldcmd; 191167167Sbms 192167167Sbms result = -1; 193167167Sbms romsize = 0; 194167167Sbms 195167167Sbms bzero(&pc, sizeof(pc)); 196167167Sbms pc.match_buf_len = sizeof(devs); 197167167Sbms pc.matches = devs; 198167167Sbms 199167167Sbms if (ioctl(pci_fd, PCIOCGETCONF, &pc) == -1) { 200167167Sbms perror("ioctl PCIOCGETCONF"); 201167167Sbms return; 202167167Sbms } 203167167Sbms 204167167Sbms if (pc.status == PCI_GETCONF_ERROR) { 205167167Sbms fprintf(stderr, 206167167Sbms "Error fetching PCI device list from kernel.\r\n"); 207167167Sbms return; 208167167Sbms } 209167167Sbms 210167167Sbms if (pc.status == PCI_GETCONF_MORE_DEVS) { 211167167Sbms fprintf(stderr, 212167167Sbms"More than %d devices exist. Only the first %d will be inspected.\r\n", 213167167Sbms MAX_PCI_DEVS, MAX_PCI_DEVS); 214167167Sbms } 215167167Sbms 216167167Sbms for (p = devs ; p < &devs[pc.num_matches]; p++) { 217167167Sbms 218167167Sbms /* No PCI bridges; only PCI devices. */ 219167167Sbms if (p->pc_hdr != 0x00) 220167167Sbms continue; 221167167Sbms 222167167Sbms romsize = pci_testrombar(pci_fd, p); 223167167Sbms 224167167Sbms switch (action) { 225167167Sbms case PRINT: 226167167Sbms printf("Bus %02Xh Device %02Xh Function %02Xh: ", 227167167Sbms p->pc_sel.pc_bus, p->pc_sel.pc_dev, 228167167Sbms p->pc_sel.pc_func); 229167167Sbms printf((romsize ? "%dKB ROM aperture detected." 230167167Sbms : "No ROM present."), romsize/1024); 231167167Sbms printf("\r\n"); 232167167Sbms break; 233167167Sbms case SAVE: 234167167Sbms if (romsize == 0) 235167167Sbms continue; /* XXX */ 236167167Sbms 237167167Sbms snprintf(filename, sizeof(filename), "%08X.rom", 238167167Sbms ((p->pc_device << 16) | p->pc_vendor)); 239167167Sbms 240167167Sbms fprintf(stderr, "Saving %dKB ROM image to %s...\r\n", 241167167Sbms romsize, filename); 242167167Sbms 243167167Sbms if (pci_enable_bars(pci_fd, p, &oldcmd) == 0) 244167167Sbms result = pci_save_rom(filename, romsize); 245167167Sbms 246167167Sbms pci_disable_bars(pci_fd, p, &oldcmd); 247167167Sbms 248167167Sbms if (result == 0) { 249167167Sbms fprintf(stderr, "Done.\r\n"); 250167167Sbms } else { 251167167Sbms fprintf(stderr, 252167167Sbms"An error occurred whilst saving the ROM.\r\n"); 253167167Sbms } 254167167Sbms break; 255167167Sbms } /* switch */ 256167167Sbms } /* for */ 257167167Sbms} 258167167Sbms 259167167Sbms/* 260167167Sbms * Return: size of ROM aperture off dev, 0 if no ROM exists. 261167167Sbms */ 262167167Sbmsstatic uint32_t 263167167Sbmspci_testrombar(int pci_fd, struct pci_conf *dev) 264167167Sbms{ 265167167Sbms struct pci_io io; 266167167Sbms uint32_t romsize; 267167167Sbms 268167167Sbms romsize = 0; 269167167Sbms 270167167Sbms /* 271167167Sbms * Only attempt to discover ROMs on Header Type 0x00 devices. 272167167Sbms */ 273167167Sbms if (dev->pc_hdr != 0x00) 274167167Sbms return romsize; 275167167Sbms 276167167Sbms /* 277167167Sbms * Activate ROM BAR 278167167Sbms */ 279167167Sbms io.pi_sel = dev->pc_sel; 280167167Sbms io.pi_reg = PCI_CFG_ROM_BAR; 281167167Sbms io.pi_width = 4; 282167167Sbms io.pi_data = 0xFFFFFFFF; 283167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 284167167Sbms return romsize; 285167167Sbms 286167167Sbms /* 287167167Sbms * Read back ROM BAR and compare with mask 288167167Sbms */ 289167167Sbms if (ioctl(pci_fd, PCIOCREAD, &io) == -1) 290167167Sbms return 0; 291167167Sbms 292167167Sbms /* 293167167Sbms * Calculate ROM aperture if one was set. 294167167Sbms */ 295167167Sbms if (io.pi_data & PCI_ROM_ADDR_MASK) 296167167Sbms romsize = -(io.pi_data & PCI_ROM_ADDR_MASK); 297167167Sbms 298167167Sbms /* 299167167Sbms * Disable the ROM BAR when done. 300167167Sbms */ 301167167Sbms io.pi_data = 0; 302167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 303167167Sbms return 0; 304167167Sbms 305167167Sbms return romsize; 306167167Sbms} 307167167Sbms 308167167Sbmsstatic int 309167167Sbmspci_save_rom(char *filename, int romsize) 310167167Sbms{ 311167167Sbms int fd, mem_fd, err; 312167167Sbms void *map_addr; 313167167Sbms 314167167Sbms fd = err = mem_fd = -1; 315167167Sbms map_addr = MAP_FAILED; 316167167Sbms 317167167Sbms if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) { 318167167Sbms perror("open"); 319167167Sbms return -1; 320167167Sbms } 321167167Sbms 322167167Sbms map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE, 323167167Sbms mem_fd, base_addr); 324167167Sbms 325167167Sbms /* Dump ROM aperture to a file. */ 326167167Sbms if ((fd = open(filename, O_CREAT|O_RDWR|O_TRUNC|O_NOFOLLOW, 327167167Sbms S_IRUSR|S_IWUSR)) == -1) { 328167167Sbms perror("open"); 329167167Sbms goto cleanup; 330167167Sbms } 331167167Sbms 332167167Sbms if (write(fd, map_addr, romsize) != romsize) 333167167Sbms perror("write"); 334167167Sbms 335167167Sbms err = 0; 336167167Sbmscleanup: 337167167Sbms if (fd != -1) 338167167Sbms close(fd); 339167167Sbms 340167167Sbms if (map_addr != MAP_FAILED) 341167167Sbms munmap((void *)base_addr, romsize); 342167167Sbms 343167167Sbms if (mem_fd != -1) 344167167Sbms close(mem_fd); 345167167Sbms 346167167Sbms return err; 347167167Sbms} 348167167Sbms 349167167Sbmsstatic int 350167167Sbmspci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd) 351167167Sbms{ 352167167Sbms struct pci_io io; 353167167Sbms 354167167Sbms /* Don't grok bridges. */ 355167167Sbms if (dev->pc_hdr != 0x00) 356167167Sbms return -1; 357167167Sbms 358167167Sbms /* Save command register. */ 359167167Sbms io.pi_sel = dev->pc_sel; 360167167Sbms io.pi_reg = PCI_CFG_CMD; 361167167Sbms io.pi_width = 2; 362167167Sbms if (ioctl(pci_fd, PCIOCREAD, &io) == -1) 363167167Sbms return -1; 364167167Sbms *oldcmd = (uint16_t)io.pi_data; 365167167Sbms 366167167Sbms io.pi_data |= PCI_CMD_MEM_SPACE; 367167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 368167167Sbms return -1; 369167167Sbms 370167167Sbms /* 371167167Sbms * Activate ROM BAR and map at the specified base address. 372167167Sbms */ 373167167Sbms io.pi_sel = dev->pc_sel; 374167167Sbms io.pi_reg = PCI_CFG_ROM_BAR; 375167167Sbms io.pi_width = 4; 376167167Sbms io.pi_data = (base_addr | PCI_ROM_ACTIVATE); 377167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 378167167Sbms return -1; 379167167Sbms 380167167Sbms return 0; 381167167Sbms} 382167167Sbms 383167167Sbmsstatic int 384167167Sbmspci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd) 385167167Sbms{ 386167167Sbms struct pci_io io; 387167167Sbms 388167167Sbms /* 389167167Sbms * Clear ROM BAR to deactivate the mapping. 390167167Sbms */ 391167167Sbms io.pi_sel = dev->pc_sel; 392167167Sbms io.pi_reg = PCI_CFG_ROM_BAR; 393167167Sbms io.pi_width = 4; 394167167Sbms io.pi_data = 0; 395167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 396167167Sbms return 0; 397167167Sbms 398167167Sbms /* 399167167Sbms * Restore state of the command register. 400167167Sbms */ 401167167Sbms io.pi_sel = dev->pc_sel; 402167167Sbms io.pi_reg = PCI_CFG_CMD; 403167167Sbms io.pi_width = 2; 404167167Sbms io.pi_data = *oldcmd; 405167167Sbms if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) { 406167167Sbms perror("ioctl"); 407167167Sbms return 0; 408167167Sbms } 409167167Sbms 410167167Sbms return 0; 411167167Sbms} 412