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