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