2 * Copyright (c) 2007 Bruce M. Simpson.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/pciio.h>
34 #include <sys/memrange.h>
36 #include <machine/endian.h>
47 #define _PATH_DEVPCI "/dev/pci"
48 #define _PATH_DEVMEM "/dev/mem"
50 #define PCI_CFG_CMD 0x04 /* command register */
51 #define PCI_CFG_ROM_BAR 0x30 /* rom base register */
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 */
57 #define PCI_CMD_MEM_SPACE 0x02 /* memory space bit */
58 #define PCI_HDRTYPE_MFD 0x80 /* MFD bit in HDRTYPE reg. */
60 #define MAX_PCI_DEVS 64 /* # of devices in system */
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.
72 * This is the hole between the APIC and the BIOS (FED00000-FEDFFFFF);
73 * should be a safe range on the i815 Solano chipset.
75 #define PCI_DEFAULT_ROM_ADDR 0xFED00000
77 static char *progname = NULL;
78 static uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR;
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,
86 static int pci_disable_bars(int pci_fd, struct pci_conf *dev,
88 static int pci_save_rom(char *filename, int romsize);
91 main(int argc, char *argv[])
97 char *base_addr_string;
103 base_addr_string = NULL;
105 progname = basename(argv[0]);
107 while ((ch = getopt(argc, argv, "sb:h")) != -1)
113 base_addr_string = optarg;
122 if (base_addr_string != NULL) {
123 uintmax_t base_addr_max;
125 base_addr_max = strtoumax(base_addr_string, &ep, 16);
127 fprintf(stderr, "Invalid base address.\r\n");
130 /* XXX: TODO: deal with 64-bit PCI. */
131 base_addr = (uintptr_t)base_addr_max;
132 base_addr &= ~PCI_ROM_RESERVED_MASK;
138 if ((pci_fd = open(_PATH_DEVPCI, O_RDWR)) == -1) {
144 pci_enum_devs(pci_fd, action);
151 exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
158 fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname);
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");
172 * Enumerate PCI device list to a limit of MAX_PCI_DEVS devices.
175 pci_enum_devs(int pci_fd, action_t action)
177 struct pci_conf devs[MAX_PCI_DEVS];
179 struct pci_conf_io pc;
188 bzero(&pc, sizeof(pc));
189 pc.match_buf_len = sizeof(devs);
192 if (ioctl(pci_fd, PCIOCGETCONF, &pc) == -1) {
193 perror("ioctl PCIOCGETCONF");
197 if (pc.status == PCI_GETCONF_ERROR) {
199 "Error fetching PCI device list from kernel.\r\n");
203 if (pc.status == PCI_GETCONF_MORE_DEVS) {
205 "More than %d devices exist. Only the first %d will be inspected.\r\n",
206 MAX_PCI_DEVS, MAX_PCI_DEVS);
209 for (p = devs ; p < &devs[pc.num_matches]; p++) {
211 /* No PCI bridges; only PCI devices. */
212 if (p->pc_hdr != 0x00)
215 romsize = pci_testrombar(pci_fd, p);
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);
231 snprintf(filename, sizeof(filename), "%08X.rom",
232 ((p->pc_device << 16) | p->pc_vendor));
234 fprintf(stderr, "Saving %dKB ROM image to %s...\r\n",
237 if (pci_enable_bars(pci_fd, p, &oldcmd) == 0)
238 result = pci_save_rom(filename, romsize);
240 pci_disable_bars(pci_fd, p, &oldcmd);
243 fprintf(stderr, "Done.\r\n");
246 "An error occurred whilst saving the ROM.\r\n");
254 * Return: size of ROM aperture off dev, 0 if no ROM exists.
257 pci_testrombar(int pci_fd, struct pci_conf *dev)
265 * Only attempt to discover ROMs on Header Type 0x00 devices.
267 if (dev->pc_hdr != 0x00)
273 io.pi_sel = dev->pc_sel;
274 io.pi_reg = PCI_CFG_ROM_BAR;
276 io.pi_data = 0xFFFFFFFF;
277 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
281 * Read back ROM BAR and compare with mask
283 if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
287 * Calculate ROM aperture if one was set.
289 if (io.pi_data & PCI_ROM_ADDR_MASK)
290 romsize = -(io.pi_data & PCI_ROM_ADDR_MASK);
293 * Disable the ROM BAR when done.
296 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
303 pci_save_rom(char *filename, int romsize)
308 fd = err = mem_fd = -1;
309 map_addr = MAP_FAILED;
311 if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
316 map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE,
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) {
326 if (write(fd, map_addr, romsize) != romsize)
334 if (map_addr != MAP_FAILED)
335 munmap((void *)base_addr, romsize);
344 pci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
348 /* Don't grok bridges. */
349 if (dev->pc_hdr != 0x00)
352 /* Save command register. */
353 io.pi_sel = dev->pc_sel;
354 io.pi_reg = PCI_CFG_CMD;
356 if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
358 *oldcmd = (uint16_t)io.pi_data;
360 io.pi_data |= PCI_CMD_MEM_SPACE;
361 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
365 * Activate ROM BAR and map at the specified base address.
367 io.pi_sel = dev->pc_sel;
368 io.pi_reg = PCI_CFG_ROM_BAR;
370 io.pi_data = (base_addr | PCI_ROM_ACTIVATE);
371 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
378 pci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
383 * Clear ROM BAR to deactivate the mapping.
385 io.pi_sel = dev->pc_sel;
386 io.pi_reg = PCI_CFG_ROM_BAR;
389 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
393 * Restore state of the command register.
395 io.pi_sel = dev->pc_sel;
396 io.pi_reg = PCI_CFG_CMD;
398 io.pi_data = *oldcmd;
399 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) {