]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/tools/pciroms/pciroms.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / tools / pciroms / pciroms.c
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$");
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
69 typedef 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
84 static char *progname = NULL;
85 static uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR;
86
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,
92     uint16_t *oldcmd);
93 static int      pci_disable_bars(int pci_fd, struct pci_conf *dev,
94     uint16_t *oldcmd);
95 static int      pci_save_rom(char *filename, int romsize);
96
97 int
98 main(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;
154 cleanup:
155         if (pci_fd != -1)
156                 close(pci_fd);
157
158         exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
159 }
160
161 static void
162 usage(void)
163 {
164
165         fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname);
166         exit(EXIT_FAILURE);
167 }
168
169 static void
170 banner(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  */
181 static void
182 pci_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(
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);
232                         printf("\r\n");
233                         break;
234                 case SAVE:
235                         if (romsize == 0)
236                                 continue;       /* XXX */
237
238                         snprintf(filename, sizeof(filename), "%08X.rom",
239                             ((p->pc_device << 16) | p->pc_vendor));
240
241                         fprintf(stderr, "Saving %dKB ROM image to %s...\r\n",
242                             romsize, filename);
243
244                         if (pci_enable_bars(pci_fd, p, &oldcmd) == 0)
245                                 result = pci_save_rom(filename, romsize);
246
247                         pci_disable_bars(pci_fd, p, &oldcmd);
248
249                         if (result == 0)  {
250                                 fprintf(stderr, "Done.\r\n");
251                         } else  {
252                                 fprintf(stderr,
253 "An error occurred whilst saving the ROM.\r\n");
254                         }
255                         break;
256                 } /* switch */
257         } /* for */
258 }
259
260 /*
261  * Return: size of ROM aperture off dev, 0 if no ROM exists.
262  */
263 static uint32_t
264 pci_testrombar(int pci_fd, struct pci_conf *dev)
265 {
266         struct pci_io    io;
267         uint32_t         romsize;
268
269         romsize = 0;
270
271         /*
272          * Only attempt to discover ROMs on Header Type 0x00 devices.
273          */
274         if (dev->pc_hdr != 0x00)
275                 return romsize;
276
277         /*
278          * Activate ROM BAR
279          */
280         io.pi_sel = dev->pc_sel;
281         io.pi_reg = PCI_CFG_ROM_BAR;
282         io.pi_width = 4;
283         io.pi_data = 0xFFFFFFFF;
284         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
285                 return romsize;
286
287         /*
288          * Read back ROM BAR and compare with mask
289          */
290         if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
291                 return 0;
292
293         /*
294          * Calculate ROM aperture if one was set.
295          */
296         if (io.pi_data & PCI_ROM_ADDR_MASK)
297                 romsize = -(io.pi_data & PCI_ROM_ADDR_MASK);
298
299         /*
300          * Disable the ROM BAR when done.
301          */
302         io.pi_data = 0;
303         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
304                 return 0;
305
306         return romsize;
307 }
308
309 static int
310 pci_save_rom(char *filename, int romsize)
311 {
312         int      fd, mem_fd, err;
313         void    *map_addr;
314
315         fd = err = mem_fd = -1;
316         map_addr = MAP_FAILED;
317
318         if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
319                 perror("open");
320                 return -1;
321         }
322
323         map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE,
324             mem_fd, base_addr);
325
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) {
329                 perror("open");
330                 goto cleanup;
331         }
332
333         if (write(fd, map_addr, romsize) != romsize)
334                 perror("write");
335
336         err = 0;
337 cleanup:
338         if (fd != -1)
339                 close(fd);
340
341         if (map_addr != MAP_FAILED)
342                 munmap((void *)base_addr, romsize);
343
344         if (mem_fd != -1)
345                 close(mem_fd);
346
347         return err;
348 }
349
350 static int
351 pci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
352 {
353         struct pci_io io;
354
355         /* Don't grok bridges. */
356         if (dev->pc_hdr != 0x00)
357                 return -1;
358
359         /* Save command register. */
360         io.pi_sel = dev->pc_sel;
361         io.pi_reg = PCI_CFG_CMD;
362         io.pi_width = 2;
363         if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
364                 return -1;
365         *oldcmd = (uint16_t)io.pi_data;
366
367         io.pi_data |= PCI_CMD_MEM_SPACE;
368         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
369                 return -1;
370
371         /*
372          * Activate ROM BAR and map at the specified base address.
373          */
374         io.pi_sel = dev->pc_sel;
375         io.pi_reg = PCI_CFG_ROM_BAR;
376         io.pi_width = 4;
377         io.pi_data = (base_addr | PCI_ROM_ACTIVATE);
378         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
379                 return -1;
380
381         return 0;
382 }
383
384 static int
385 pci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
386 {
387         struct pci_io    io;
388
389         /*
390          * Clear ROM BAR to deactivate the mapping.
391          */
392         io.pi_sel = dev->pc_sel;
393         io.pi_reg = PCI_CFG_ROM_BAR;
394         io.pi_width = 4;
395         io.pi_data = 0;
396         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
397                 return 0;
398
399         /*
400          * Restore state of the command register.
401          */
402         io.pi_sel = dev->pc_sel;
403         io.pi_reg = PCI_CFG_CMD;
404         io.pi_width = 2;
405         io.pi_data = *oldcmd;
406         if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) {
407                 perror("ioctl");
408                 return 0;
409         }
410
411         return 0;
412 }