]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/pciroms/pciroms.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  *
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 }