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