pciroms.c revision 167167
1167167Sbms/*
2167167Sbms * Copyright (c) 2007 Bruce M. Simpson.
3167167Sbms * All rights reserved
4167167Sbms *
5167167Sbms * Redistribution and use in source and binary forms, with or without
6167167Sbms * modification, are permitted provided that the following conditions
7167167Sbms * are met:
8167167Sbms * 1. Redistributions of source code must retain the above copyright
9167167Sbms *    notice, this list of conditions and the following disclaimer.
10167167Sbms * 2. Redistributions in binary form must reproduce the above copyright
11167167Sbms *    notice, this list of conditions and the following disclaimer in the
12167167Sbms *    documentation and/or other materials provided with the distribution.
13167167Sbms * 3. All advertising materials mentioning features or use of this software
14167167Sbms *    must display the following acknowledgement:
15167167Sbms *        This product includes software developed by Bruce M. Simpson.
16167167Sbms * 4. Neither the name of Bruce M. Simpson nor the names of
17167167Sbms *    contributors may be used to endorse or promote products derived
18167167Sbms *    from this software without specific prior written permission.
19167167Sbms *
20167167Sbms * THIS SOFTWARE IS PROVIDED BY BRUCE M. SIMPSON AND AFFILIATES
21167167Sbms * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22167167Sbms * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23167167Sbms * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24167167Sbms * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25167167Sbms * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26167167Sbms * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27167167Sbms * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28167167Sbms * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29167167Sbms * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30167167Sbms * POSSIBILITY OF SUCH DAMAGE.
31167167Sbms *
32167167Sbms */
33167167Sbms
34167167Sbms#include <sys/cdefs.h>
35167167Sbms__FBSDID("$FreeBSD: head/tools/tools/pciroms/pciroms.c 167167 2007-03-02 13:53:23Z bms $");
36167167Sbms
37167167Sbms#include <sys/types.h>
38167167Sbms#include <sys/ioctl.h>
39167167Sbms#include <sys/pciio.h>
40167167Sbms#include <sys/mman.h>
41167167Sbms#include <sys/memrange.h>
42167167Sbms#include <sys/stat.h>
43167167Sbms#include <machine/endian.h>
44167167Sbms
45167167Sbms#include <stddef.h>
46167167Sbms#include <inttypes.h>
47167167Sbms#include <stdio.h>
48167167Sbms#include <stdlib.h>
49167167Sbms#include <libgen.h>
50167167Sbms#include <fcntl.h>
51167167Sbms#include <string.h>
52167167Sbms#include <unistd.h>
53167167Sbms
54167167Sbms#define	_PATH_DEVPCI	"/dev/pci"
55167167Sbms#define	_PATH_DEVMEM	"/dev/mem"
56167167Sbms
57167167Sbms#define	PCI_CFG_CMD		0x04		/* command register */
58167167Sbms#define	PCI_CFG_ROM_BAR		0x30		/* rom base register */
59167167Sbms
60167167Sbms#define	PCI_ROM_ADDR_MASK	0xFFFFFC00	/* the 21 MSBs form the BAR */
61167167Sbms#define	PCI_ROM_RESERVED_MASK	0x03FE		/* mask for reserved bits */
62167167Sbms#define	PCI_ROM_ACTIVATE	0x01		/* mask for activation bit */
63167167Sbms
64167167Sbms#define	PCI_CMD_MEM_SPACE	0x02		/* memory space bit */
65167167Sbms#define	PCI_HDRTYPE_MFD		0x80		/* MFD bit in HDRTYPE reg. */
66167167Sbms
67167167Sbms#define	MAX_PCI_DEVS		64		/* # of devices in system */
68167167Sbms
69167167Sbmstypedef enum {
70167167Sbms	PRINT = 0,
71167167Sbms	SAVE = 1
72167167Sbms} action_t;
73167167Sbms
74167167Sbms/*
75167167Sbms * This is set to a safe physical base address in PCI range for my Vaio.
76167167Sbms * YOUR MACHINE *WILL* VARY, I SUGGEST YOU LOOK UP YOUR MACHINE'S MEMORY
77167167Sbms * MAP IN DETAIL IF YOU PLAN ON SAVING ROMS.
78167167Sbms *
79167167Sbms * This is the hole between the APIC and the BIOS (FED00000-FEDFFFFF);
80167167Sbms * should be a safe range on the i815 Solano chipset.
81167167Sbms */
82167167Sbms#define PCI_DEFAULT_ROM_ADDR	0xFED00000
83167167Sbms
84167167Sbmsstatic char *progname = NULL;
85167167Sbmsstatic uintptr_t base_addr = PCI_DEFAULT_ROM_ADDR;
86167167Sbms
87167167Sbmsstatic void	usage(void);
88167167Sbmsstatic void	banner(void);
89167167Sbmsstatic void	pci_enum_devs(int pci_fd, action_t action);
90167167Sbmsstatic uint32_t	pci_testrombar(int pci_fd, struct pci_conf *dev);
91167167Sbmsstatic int	pci_enable_bars(int pci_fd, struct pci_conf *dev,
92167167Sbms    uint16_t *oldcmd);
93167167Sbmsstatic int	pci_disable_bars(int pci_fd, struct pci_conf *dev,
94167167Sbms    uint16_t *oldcmd);
95167167Sbmsstatic int	pci_save_rom(char *filename, int romsize);
96167167Sbms
97167167Sbmsint
98167167Sbmsmain(int argc, char *argv[])
99167167Sbms{
100167167Sbms	int		 pci_fd;
101167167Sbms	int		 err;
102167167Sbms	int		 ch;
103167167Sbms	action_t	 action;
104167167Sbms	char		*base_addr_string;
105167167Sbms	char		*ep;
106167167Sbms
107167167Sbms	err = -1;
108167167Sbms	pci_fd = -1;
109167167Sbms	action = PRINT;
110167167Sbms	base_addr_string = NULL;
111167167Sbms	ep = NULL;
112167167Sbms	progname = basename(argv[0]);
113167167Sbms
114167167Sbms	while ((ch = getopt(argc, argv, "sb:h")) != -1)
115167167Sbms		switch (ch) {
116167167Sbms		case 's':
117167167Sbms			action = SAVE;
118167167Sbms			break;
119167167Sbms		case 'b':
120167167Sbms			base_addr_string = optarg;
121167167Sbms			break;
122167167Sbms		case 'h':
123167167Sbms		default:
124167167Sbms		     usage();
125167167Sbms	}
126167167Sbms	argc -= optind;
127167167Sbms	argv += optind;
128167167Sbms
129167167Sbms	if (base_addr_string != NULL) {
130167167Sbms		uintmax_t base_addr_max;
131167167Sbms
132167167Sbms		base_addr_max = strtoumax(base_addr_string, &ep, 16);
133167167Sbms		if (*ep != '\0') {
134167167Sbms			fprintf(stderr, "Invalid base address.\r\n");
135167167Sbms			usage();
136167167Sbms		}
137167167Sbms		/* XXX: TODO: deal with 64-bit PCI. */
138167167Sbms		base_addr = (uintptr_t)base_addr_max;
139167167Sbms		base_addr &= ~PCI_ROM_RESERVED_MASK;
140167167Sbms	}
141167167Sbms
142167167Sbms	if (argc > 0)
143167167Sbms		usage();
144167167Sbms
145167167Sbms	if ((pci_fd = open(_PATH_DEVPCI, O_RDWR)) == -1) {
146167167Sbms		perror("open");
147167167Sbms		goto cleanup;
148167167Sbms	}
149167167Sbms
150167167Sbms	banner();
151167167Sbms	pci_enum_devs(pci_fd, action);
152167167Sbms
153167167Sbms	err = 0;
154167167Sbmscleanup:
155167167Sbms	if (pci_fd != -1)
156167167Sbms		close(pci_fd);
157167167Sbms
158167167Sbms	exit ((err == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
159167167Sbms}
160167167Sbms
161167167Sbmsstatic void
162167167Sbmsusage(void)
163167167Sbms{
164167167Sbms
165167167Sbms	fprintf(stderr, "usage: %s [-s] [-b <base-address>]\r\n", progname);
166167167Sbms	exit(EXIT_FAILURE);
167167167Sbms}
168167167Sbms
169167167Sbmsstatic void
170167167Sbmsbanner(void)
171167167Sbms{
172167167Sbms
173167167Sbms	fprintf(stderr,
174167167Sbms		"WARNING: You are advised to run this program in single\r\n"
175167167Sbms		"user mode, with few or no processes running.\r\n\r\n");
176167167Sbms}
177167167Sbms
178167167Sbms/*
179167167Sbms * Enumerate PCI device list to a limit of MAX_PCI_DEVS devices.
180167167Sbms */
181167167Sbmsstatic void
182167167Sbmspci_enum_devs(int pci_fd, action_t action)
183167167Sbms{
184167167Sbms	struct pci_conf		 devs[MAX_PCI_DEVS];
185167167Sbms	char			 filename[16];
186167167Sbms	struct pci_conf_io	 pc;
187167167Sbms	struct pci_conf		*p;
188167167Sbms	int			 result;
189167167Sbms	int			 romsize;
190167167Sbms	uint16_t		 oldcmd;
191167167Sbms
192167167Sbms	result = -1;
193167167Sbms	romsize = 0;
194167167Sbms
195167167Sbms	bzero(&pc, sizeof(pc));
196167167Sbms	pc.match_buf_len = sizeof(devs);
197167167Sbms	pc.matches = devs;
198167167Sbms
199167167Sbms	if (ioctl(pci_fd, PCIOCGETCONF, &pc) == -1) {
200167167Sbms		perror("ioctl PCIOCGETCONF");
201167167Sbms		return;
202167167Sbms	}
203167167Sbms
204167167Sbms	if (pc.status == PCI_GETCONF_ERROR) {
205167167Sbms		fprintf(stderr,
206167167Sbms		    "Error fetching PCI device list from kernel.\r\n");
207167167Sbms		return;
208167167Sbms	}
209167167Sbms
210167167Sbms	if (pc.status == PCI_GETCONF_MORE_DEVS) {
211167167Sbms		fprintf(stderr,
212167167Sbms"More than %d devices exist. Only the first %d will be inspected.\r\n",
213167167Sbms		    MAX_PCI_DEVS, MAX_PCI_DEVS);
214167167Sbms	}
215167167Sbms
216167167Sbms	for (p = devs ; p < &devs[pc.num_matches]; p++) {
217167167Sbms
218167167Sbms		/* No PCI bridges; only PCI devices. */
219167167Sbms		if (p->pc_hdr != 0x00)
220167167Sbms			continue;
221167167Sbms
222167167Sbms		romsize = pci_testrombar(pci_fd, p);
223167167Sbms
224167167Sbms		switch (action) {
225167167Sbms		case PRINT:
226167167Sbms			printf("Bus %02Xh Device %02Xh Function %02Xh: ",
227167167Sbms				p->pc_sel.pc_bus, p->pc_sel.pc_dev,
228167167Sbms				p->pc_sel.pc_func);
229167167Sbms			printf((romsize ? "%dKB ROM aperture detected."
230167167Sbms					: "No ROM present."), romsize/1024);
231167167Sbms			printf("\r\n");
232167167Sbms			break;
233167167Sbms		case SAVE:
234167167Sbms			if (romsize == 0)
235167167Sbms				continue;	/* XXX */
236167167Sbms
237167167Sbms			snprintf(filename, sizeof(filename), "%08X.rom",
238167167Sbms			    ((p->pc_device << 16) | p->pc_vendor));
239167167Sbms
240167167Sbms			fprintf(stderr, "Saving %dKB ROM image to %s...\r\n",
241167167Sbms			    romsize, filename);
242167167Sbms
243167167Sbms			if (pci_enable_bars(pci_fd, p, &oldcmd) == 0)
244167167Sbms				result = pci_save_rom(filename, romsize);
245167167Sbms
246167167Sbms			pci_disable_bars(pci_fd, p, &oldcmd);
247167167Sbms
248167167Sbms			if (result == 0)  {
249167167Sbms				fprintf(stderr, "Done.\r\n");
250167167Sbms			} else  {
251167167Sbms				fprintf(stderr,
252167167Sbms"An error occurred whilst saving the ROM.\r\n");
253167167Sbms			}
254167167Sbms			break;
255167167Sbms		} /* switch */
256167167Sbms	} /* for */
257167167Sbms}
258167167Sbms
259167167Sbms/*
260167167Sbms * Return: size of ROM aperture off dev, 0 if no ROM exists.
261167167Sbms */
262167167Sbmsstatic uint32_t
263167167Sbmspci_testrombar(int pci_fd, struct pci_conf *dev)
264167167Sbms{
265167167Sbms	struct pci_io	 io;
266167167Sbms	uint32_t	 romsize;
267167167Sbms
268167167Sbms	romsize = 0;
269167167Sbms
270167167Sbms	/*
271167167Sbms	 * Only attempt to discover ROMs on Header Type 0x00 devices.
272167167Sbms	 */
273167167Sbms	if (dev->pc_hdr != 0x00)
274167167Sbms		return romsize;
275167167Sbms
276167167Sbms	/*
277167167Sbms	 * Activate ROM BAR
278167167Sbms	 */
279167167Sbms	io.pi_sel = dev->pc_sel;
280167167Sbms	io.pi_reg = PCI_CFG_ROM_BAR;
281167167Sbms	io.pi_width = 4;
282167167Sbms	io.pi_data = 0xFFFFFFFF;
283167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
284167167Sbms		return romsize;
285167167Sbms
286167167Sbms	/*
287167167Sbms	 * Read back ROM BAR and compare with mask
288167167Sbms	 */
289167167Sbms	if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
290167167Sbms		return 0;
291167167Sbms
292167167Sbms	/*
293167167Sbms	 * Calculate ROM aperture if one was set.
294167167Sbms	 */
295167167Sbms	if (io.pi_data & PCI_ROM_ADDR_MASK)
296167167Sbms		romsize = -(io.pi_data & PCI_ROM_ADDR_MASK);
297167167Sbms
298167167Sbms	/*
299167167Sbms	 * Disable the ROM BAR when done.
300167167Sbms	 */
301167167Sbms	io.pi_data = 0;
302167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
303167167Sbms		return 0;
304167167Sbms
305167167Sbms	return romsize;
306167167Sbms}
307167167Sbms
308167167Sbmsstatic int
309167167Sbmspci_save_rom(char *filename, int romsize)
310167167Sbms{
311167167Sbms	int	 fd, mem_fd, err;
312167167Sbms	void	*map_addr;
313167167Sbms
314167167Sbms	fd = err = mem_fd = -1;
315167167Sbms	map_addr = MAP_FAILED;
316167167Sbms
317167167Sbms	if ((mem_fd = open(_PATH_DEVMEM, O_RDONLY)) == -1) {
318167167Sbms		perror("open");
319167167Sbms		return -1;
320167167Sbms	}
321167167Sbms
322167167Sbms	map_addr = mmap(NULL, romsize, PROT_READ, MAP_SHARED|MAP_NOCORE,
323167167Sbms	    mem_fd, base_addr);
324167167Sbms
325167167Sbms	/* Dump ROM aperture to a file. */
326167167Sbms	if ((fd = open(filename, O_CREAT|O_RDWR|O_TRUNC|O_NOFOLLOW,
327167167Sbms	    S_IRUSR|S_IWUSR)) == -1) {
328167167Sbms		perror("open");
329167167Sbms		goto cleanup;
330167167Sbms	}
331167167Sbms
332167167Sbms	if (write(fd, map_addr, romsize) != romsize)
333167167Sbms		perror("write");
334167167Sbms
335167167Sbms	err = 0;
336167167Sbmscleanup:
337167167Sbms	if (fd != -1)
338167167Sbms		close(fd);
339167167Sbms
340167167Sbms	if (map_addr != MAP_FAILED)
341167167Sbms		munmap((void *)base_addr, romsize);
342167167Sbms
343167167Sbms	if (mem_fd != -1)
344167167Sbms		close(mem_fd);
345167167Sbms
346167167Sbms	return err;
347167167Sbms}
348167167Sbms
349167167Sbmsstatic int
350167167Sbmspci_enable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
351167167Sbms{
352167167Sbms	struct pci_io io;
353167167Sbms
354167167Sbms	/* Don't grok bridges. */
355167167Sbms	if (dev->pc_hdr != 0x00)
356167167Sbms		return -1;
357167167Sbms
358167167Sbms	/* Save command register. */
359167167Sbms	io.pi_sel = dev->pc_sel;
360167167Sbms	io.pi_reg = PCI_CFG_CMD;
361167167Sbms	io.pi_width = 2;
362167167Sbms	if (ioctl(pci_fd, PCIOCREAD, &io) == -1)
363167167Sbms		return -1;
364167167Sbms	*oldcmd = (uint16_t)io.pi_data;
365167167Sbms
366167167Sbms	io.pi_data |= PCI_CMD_MEM_SPACE;
367167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
368167167Sbms		return -1;
369167167Sbms
370167167Sbms	/*
371167167Sbms	 * Activate ROM BAR and map at the specified base address.
372167167Sbms	 */
373167167Sbms	io.pi_sel = dev->pc_sel;
374167167Sbms	io.pi_reg = PCI_CFG_ROM_BAR;
375167167Sbms	io.pi_width = 4;
376167167Sbms	io.pi_data = (base_addr | PCI_ROM_ACTIVATE);
377167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
378167167Sbms		return -1;
379167167Sbms
380167167Sbms	return 0;
381167167Sbms}
382167167Sbms
383167167Sbmsstatic int
384167167Sbmspci_disable_bars(int pci_fd, struct pci_conf *dev, uint16_t *oldcmd)
385167167Sbms{
386167167Sbms	struct pci_io	 io;
387167167Sbms
388167167Sbms	/*
389167167Sbms	 * Clear ROM BAR to deactivate the mapping.
390167167Sbms	 */
391167167Sbms	io.pi_sel = dev->pc_sel;
392167167Sbms	io.pi_reg = PCI_CFG_ROM_BAR;
393167167Sbms	io.pi_width = 4;
394167167Sbms	io.pi_data = 0;
395167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1)
396167167Sbms		return 0;
397167167Sbms
398167167Sbms	/*
399167167Sbms	 * Restore state of the command register.
400167167Sbms	 */
401167167Sbms	io.pi_sel = dev->pc_sel;
402167167Sbms	io.pi_reg = PCI_CFG_CMD;
403167167Sbms	io.pi_width = 2;
404167167Sbms	io.pi_data = *oldcmd;
405167167Sbms	if (ioctl(pci_fd, PCIOCWRITE, &io) == -1) {
406167167Sbms		perror("ioctl");
407167167Sbms		return 0;
408167167Sbms	}
409167167Sbms
410167167Sbms	return 0;
411167167Sbms}
412