1 /* 2 * Copyright (c) 2007 Bruce M. Simpson. 3 * All rights reserved 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/ioctl.h> 29 #include <sys/pciio.h> 30 #include <sys/mman.h> 31 #include <sys/memrange.h> 32 #include <sys/stat.h> 33 #include <machine/endian.h> 34 35 #include <stddef.h> 36 #include <inttypes.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <libgen.h> 40 #include <fcntl.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #define _PATH_DEVPCI "/dev/pci" 45 #define _PATH_DEVMEM "/dev/mem" 46 47 #define PCI_CFG_CMD 0x04 /* command register */ 48 #define PCI_CFG_ROM_BAR 0x30 /* rom base register */ 49 50 #define PCI_ROM_ADDR_MASK 0xFFFFFC00 /* the 21 MSBs form the BAR */ 51 #define PCI_ROM_RESERVED_MASK 0x03FE /* mask for reserved bits */ 52 #define PCI_ROM_ACTIVATE 0x01 /* mask for activation bit */ 53 54 #define PCI_CMD_MEM_SPACE 0x02 /* memory space bit */ 55 #define PCI_HDRTYPE_MFD 0x80 /* MFD bit in HDRTYPE reg. */ 56 57 #define MAX_PCI_DEVS 64 /* # of devices in system */ 58 59 typedef enum { 60 PRINT = 0, 61 SAVE = 1 62 } action_t; 63 64 /* 65 * This is set to a safe physical base address in PCI range for my Vaio. 66 * YOUR MACHINE *WILL* VARY, I SUGGEST YOU LOOK UP YOUR MACHINE'S MEMORY 67 * MAP IN DETAIL IF YOU PLAN ON SAVING ROMS. 68 * 69 * This is the hole between the APIC and the BIOS (FED00000-FEDFFFFF); 70 * should be a safe range on the i815 Solano chipset. 71 */ 72 #define PCI_DEFAULT_ROM_ADDR 0xFED00000 73 74 static char *progname = NULL; 75 static uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR; 76 77 static void usage(void); 78 static void banner(void); 79 static void pci_enum_devs(int pci_fd, action_t action); 80 static uint32_t pci_testrombar(int pci_fd, struct pci_conf *dev); 81 static int pci_enable_bars(int pci_fd, struct pci_conf *dev, 82 uint16_t *oldcmd); 83 static int pci_disable_bars(int pci_fd, struct pci_conf *dev, 84 uint16_t *oldcmd); 85 static int pci_save_rom(char *filename, int romsize); 86 87 int 88 main(int argc, char *argv[]) 89 { 90 int pci_fd; 91 int err; 92 int ch; 93 action_t action; 94 char *base_addr_string; 95 char *ep; 96 97 err = -1; 98 pci_fd = -1; 99 action = PRINT; 100 base_addr_string = NULL; 101 ep = NULL; 102 progname = basename(argv[0]); 103 104 while ((ch = getopt(argc, argv, "sb:h")) != -1) 105 switch (ch) { 106 case 's': 107 action = SAVE; 108 break; 109 case 'b': 110 base_addr_string = optarg; 111 break; 112 case 'h': 113 default: 114 usage(); 115 } 116 argc -= optind; 117 argv += optind; 118 119 if (base_addr_string != NULL) { 120 uintmax_t base_addr_max; 121 122 base_addr_max = strtoumax(base_addr_string, &ep, 16); 123 if (*ep != '\0') { 124 fprintf(stderr, "Invalid base address.\r\n"); 125 usage(); 126 } 127 /* XXX: TODO: deal with 64-bit PCI. */ 128 base_addr = (uintptr_t)base_addr_max; 129 base_addr &= ~PCI_ROM_RESERVED_MASK; 130 } 131 132 if (argc > 0) 133 usage(); 134 135 if ((pci_fd = open(_PATH_DEVPCI, O_RDWR)) == -1) { 136 perror("open"); 137 goto cleanup; 138 } 139 140 banner(); 141 pci_enum_devs(pci_fd, action); 142 143 err = 0; 144 cleanup: 145 if (pci_fd != -1) 146 close(pci_fd); 147 148 exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE); 149 } 150 151 static void 152 usage(void) 153 { 154 155 fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname); 156 exit(EXIT_FAILURE); 157 } 158 159 static void 160 banner(void) 161 { 162 163 fprintf(stderr, 164 "WARNING: You are advised to run this program in single\r\n" 165 "user mode, with few or no processes running.\r\n\r\n"); 166 } 167 168 /* 169 * Enumerate PCI device list to a limit of MAX_PCI_DEVS devices. 170 */ 171 static void 172 pci_enum_devs(int pci_fd, action_t action) 173 { 174 struct pci_conf devs[MAX_PCI_DEVS]; 175 char filename[16]; 176 struct pci_conf_io pc; 177 struct pci_conf *p; 178 int result; 179 int romsize; 180 uint16_t oldcmd; 181 182 result = -1; 183 romsize = 0; 184 185 bzero(&pc, sizeof(pc)); 186 pc.match_buf_len = sizeof(devs); 187 pc.matches = devs; 188 189 if (ioctl(pci_fd, PCIOCGETCONF, &pc) == -1) { 190 perror("ioctl PCIOCGETCONF"); 191 return; 192 } 193 194 if (pc.status == PCI_GETCONF_ERROR) { 195 fprintf(stderr, 196 "Error fetching PCI device list from kernel.\r\n"); 197 return; 198 } 199 200 if (pc.status == PCI_GETCONF_MORE_DEVS) { 201 fprintf(stderr, 202 "More than %d devices exist. Only the first %d will be inspected.\r\n", 203 MAX_PCI_DEVS, MAX_PCI_DEVS); 204 } 205 206 for (p = devs ; p < &devs[pc.num_matches]; p++) { 207 208 /* No PCI bridges; only PCI devices. */ 209 if (p->pc_hdr != 0x00) 210 continue; 211 212 romsize = pci_testrombar(pci_fd, p); 213 214 switch (action) { 215 case PRINT: 216 printf( 217 "Domain %04Xh Bus %02Xh Device %02Xh Function %02Xh: ", 218 p->pc_sel.pc_domain, p->pc_sel.pc_bus, 219 p->pc_sel.pc_dev, p->pc_sel.pc_func); 220 printf((romsize ? "%dKB ROM aperture detected." 221 : "No ROM present."), romsize/1024); 222 printf("\r\n"); 223 break; 224 case SAVE: 225 if (romsize == 0) 226 continue; /* XXX */ 227 228 snprintf(filename, sizeof(filename), "%08X.rom", 229 ((p->pc_device << 16) | p->pc_vendor)); 230 231 fprintf(stderr, "Saving %dKB ROM image to %s...\r\n", 232 romsize, filename); 233 234 if (pci_enable_bars(pci_fd, p, &oldcmd) == 0) 235 result = pci_save_rom(filename, romsize); 236 237 pci_disable_bars(pci_fd, p, &oldcmd); 238 239 if (result == 0) { 240 fprintf(stderr, "Done.\r\n"); 241 } else { 242 fprintf(stderr, 243 "An error occurred whilst saving the ROM.\r\n"); 244 } 245 break; 246 } /* switch */ 247 } /* for */ 248 } 249 250 /* 251 * Return: size of ROM aperture off dev, 0 if no ROM exists. 252 */ 253 static uint32_t 254 pci_testrombar(int pci_fd, struct pci_conf *dev) 255 { 256 struct pci_io io; 257 uint32_t romsize; 258 259 romsize = 0; 260 261 /* 262 * Only attempt to discover ROMs on Header Type 0x00 devices. 263 */ 264 if (dev->pc_hdr != 0x00) 265 return romsize; 266 267 /* 268 * Activate ROM BAR 269 */ 270 io.pi_sel = dev->pc_sel; 271 io.pi_reg = PCI_CFG_ROM_BAR; 272 io.pi_width = 4; 273 io.pi_data = 0xFFFFFFFF; 274 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 275 return romsize; 276 277 /* 278 * Read back ROM BAR and compare with mask 279 */ 280 if (ioctl(pci_fd, PCIOCREAD, &io) == -1) 281 return 0; 282 283 /* 284 * Calculate ROM aperture if one was set. 285 */ 286 if (io.pi_data & PCI_ROM_ADDR_MASK) 287 romsize = -(io.pi_data & PCI_ROM_ADDR_MASK); 288 289 /* 290 * Disable the ROM BAR when done. 291 */ 292 io.pi_data = 0; 293 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 294 return 0; 295 296 return romsize; 297 } 298 299 static int 300 pci_save_rom(char *filename, int romsize) 301 { 302 int fd, mem_fd, err; 303 void *map_addr; 304 305 fd = err = mem_fd = -1; 306 map_addr = MAP_FAILED; 307 308 if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) { 309 perror("open"); 310 return -1; 311 } 312 313 map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE, 314 mem_fd, base_addr); 315 316 /* Dump ROM aperture to a file. */ 317 if ((fd = open(filename, O_CREAT|O_RDWR|O_TRUNC|O_NOFOLLOW, 318 S_IRUSR|S_IWUSR)) == -1) { 319 perror("open"); 320 goto cleanup; 321 } 322 323 if (write(fd, map_addr, romsize) != romsize) 324 perror("write"); 325 326 err = 0; 327 cleanup: 328 if (fd != -1) 329 close(fd); 330 331 if (map_addr != MAP_FAILED) 332 munmap((void *)base_addr, romsize); 333 334 if (mem_fd != -1) 335 close(mem_fd); 336 337 return err; 338 } 339 340 static int 341 pci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd) 342 { 343 struct pci_io io; 344 345 /* Don't grok bridges. */ 346 if (dev->pc_hdr != 0x00) 347 return -1; 348 349 /* Save command register. */ 350 io.pi_sel = dev->pc_sel; 351 io.pi_reg = PCI_CFG_CMD; 352 io.pi_width = 2; 353 if (ioctl(pci_fd, PCIOCREAD, &io) == -1) 354 return -1; 355 *oldcmd = (uint16_t)io.pi_data; 356 357 io.pi_data |= PCI_CMD_MEM_SPACE; 358 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 359 return -1; 360 361 /* 362 * Activate ROM BAR and map at the specified base address. 363 */ 364 io.pi_sel = dev->pc_sel; 365 io.pi_reg = PCI_CFG_ROM_BAR; 366 io.pi_width = 4; 367 io.pi_data = (base_addr | PCI_ROM_ACTIVATE); 368 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 369 return -1; 370 371 return 0; 372 } 373 374 static int 375 pci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd) 376 { 377 struct pci_io io; 378 379 /* 380 * Clear ROM BAR to deactivate the mapping. 381 */ 382 io.pi_sel = dev->pc_sel; 383 io.pi_reg = PCI_CFG_ROM_BAR; 384 io.pi_width = 4; 385 io.pi_data = 0; 386 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) 387 return 0; 388 389 /* 390 * Restore state of the command register. 391 */ 392 io.pi_sel = dev->pc_sel; 393 io.pi_reg = PCI_CFG_CMD; 394 io.pi_width = 2; 395 io.pi_data = *oldcmd; 396 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) { 397 perror("ioctl"); 398 return 0; 399 } 400 401 return 0; 402 } 403