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.
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.
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.
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/pciio.h>
41 #include <sys/memrange.h>
43 #include <machine/endian.h>
54 #define _PATH_DEVPCI "/dev/pci"
55 #define _PATH_DEVMEM "/dev/mem"
57 #define PCI_CFG_CMD 0x04 /* command register */
58 #define PCI_CFG_ROM_BAR 0x30 /* rom base register */
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 */
64 #define PCI_CMD_MEM_SPACE 0x02 /* memory space bit */
65 #define PCI_HDRTYPE_MFD 0x80 /* MFD bit in HDRTYPE reg. */
67 #define MAX_PCI_DEVS 64 /* # of devices in system */
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.
79 * This is the hole between the APIC and the BIOS (FED00000-FEDFFFFF);
80 * should be a safe range on the i815 Solano chipset.
82 #define PCI_DEFAULT_ROM_ADDR 0xFED00000
84 static char *progname = NULL;
85 static uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR;
87 static void usage(void);
88 static void banner(void);
89 static void pci_enum_devs(int pci_fd, action_t action);
90 static uint32_t pci_testrombar(int pci_fd, struct pci_conf *dev);
91 static int pci_enable_bars(int pci_fd, struct pci_conf *dev,
93 static int pci_disable_bars(int pci_fd, struct pci_conf *dev,
95 static int pci_save_rom(char *filename, int romsize);
98 main(int argc, char *argv[])
104 char *base_addr_string;
110 base_addr_string = NULL;
112 progname = basename(argv[0]);
114 while ((ch = getopt(argc, argv, "sb:h")) != -1)
120 base_addr_string = optarg;
129 if (base_addr_string != NULL) {
130 uintmax_t base_addr_max;
132 base_addr_max = strtoumax(base_addr_string, &ep, 16);
134 fprintf(stderr, "Invalid base address.\r\n");
137 /* XXX: TODO: deal with 64-bit PCI. */
138 base_addr = (uintptr_t)base_addr_max;
139 base_addr &= ~PCI_ROM_RESERVED_MASK;
145 if ((pci_fd = open(_PATH_DEVPCI, O_RDWR)) == -1) {
151 pci_enum_devs(pci_fd, action);
158 exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
165 fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname);
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");
179 * Enumerate PCI device list to a limit of MAX_PCI_DEVS devices.
182 pci_enum_devs(int pci_fd, action_t action)
184 struct pci_conf devs[MAX_PCI_DEVS];
186 struct pci_conf_io pc;
195 bzero(&pc, sizeof(pc));
196 pc.match_buf_len = sizeof(devs);
199 if (ioctl(pci_fd, PCIOCGETCONF, &pc) == -1) {
200 perror("ioctl PCIOCGETCONF");
204 if (pc.status == PCI_GETCONF_ERROR) {
206 "Error fetching PCI device list from kernel.\r\n");
210 if (pc.status == PCI_GETCONF_MORE_DEVS) {
212 "More than %d devices exist. Only the first %d will be inspected.\r\n",
213 MAX_PCI_DEVS, MAX_PCI_DEVS);
216 for (p = devs ; p < &devs[pc.num_matches]; p++) {
218 /* No PCI bridges; only PCI devices. */
219 if (p->pc_hdr != 0x00)
222 romsize = pci_testrombar(pci_fd, p);
227 "Domain %04Xh Bus %02Xh Device %02Xh Function %02Xh: ",
228 p->pc_sel.pc_domain, p->pc_sel.pc_bus,
229 p->pc_sel.pc_dev, p->pc_sel.pc_func);
230 printf((romsize ? "%dKB ROM aperture detected."
231 : "No ROM present."), romsize/1024);
238 snprintf(filename, sizeof(filename), "%08X.rom",
239 ((p->pc_device << 16) | p->pc_vendor));
241 fprintf(stderr, "Saving %dKB ROM image to %s...\r\n",
244 if (pci_enable_bars(pci_fd, p, &oldcmd) == 0)
245 result = pci_save_rom(filename, romsize);
247 pci_disable_bars(pci_fd, p, &oldcmd);
250 fprintf(stderr, "Done.\r\n");
253 "An error occurred whilst saving the ROM.\r\n");
261 * Return: size of ROM aperture off dev, 0 if no ROM exists.
264 pci_testrombar(int pci_fd, struct pci_conf *dev)
272 * Only attempt to discover ROMs on Header Type 0x00 devices.
274 if (dev->pc_hdr != 0x00)
280 io.pi_sel = dev->pc_sel;
281 io.pi_reg = PCI_CFG_ROM_BAR;
283 io.pi_data = 0xFFFFFFFF;
284 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
288 * Read back ROM BAR and compare with mask
290 if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
294 * Calculate ROM aperture if one was set.
296 if (io.pi_data & PCI_ROM_ADDR_MASK)
297 romsize = -(io.pi_data & PCI_ROM_ADDR_MASK);
300 * Disable the ROM BAR when done.
303 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
310 pci_save_rom(char *filename, int romsize)
315 fd = err = mem_fd = -1;
316 map_addr = MAP_FAILED;
318 if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
323 map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE,
326 /* Dump ROM aperture to a file. */
327 if ((fd = open(filename, O_CREAT|O_RDWR|O_TRUNC|O_NOFOLLOW,
328 S_IRUSR|S_IWUSR)) == -1) {
333 if (write(fd, map_addr, romsize) != romsize)
341 if (map_addr != MAP_FAILED)
342 munmap((void *)base_addr, romsize);
351 pci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
355 /* Don't grok bridges. */
356 if (dev->pc_hdr != 0x00)
359 /* Save command register. */
360 io.pi_sel = dev->pc_sel;
361 io.pi_reg = PCI_CFG_CMD;
363 if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
365 *oldcmd = (uint16_t)io.pi_data;
367 io.pi_data |= PCI_CMD_MEM_SPACE;
368 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
372 * Activate ROM BAR and map at the specified base address.
374 io.pi_sel = dev->pc_sel;
375 io.pi_reg = PCI_CFG_ROM_BAR;
377 io.pi_data = (base_addr | PCI_ROM_ACTIVATE);
378 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
385 pci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
390 * Clear ROM BAR to deactivate the mapping.
392 io.pi_sel = dev->pc_sel;
393 io.pi_reg = PCI_CFG_ROM_BAR;
396 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
400 * Restore state of the command register.
402 io.pi_sel = dev->pc_sel;
403 io.pi_reg = PCI_CFG_CMD;
405 io.pi_data = *oldcmd;
406 if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) {