aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/probe_roms.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-03-08 13:36:19 -0500
committerH. Peter Anvin <hpa@linux.intel.com>2011-03-15 18:34:15 -0400
commit5d94e81f69d4b1d1102d3ab557ce0a817c11fbbb (patch)
tree9cd387df3bd7660708b153a745c9b45a7bc16139 /arch/x86/kernel/probe_roms.c
parent45bb1674b976ef81429c1e42de05844b49d45dea (diff)
x86: Introduce pci_map_biosrom()
The isci driver needs to retrieve its preboot OROM image which contains necessary runtime parameters like platform specific sas addresses and phy configuration. There is no ROM BAR associated with this area, instead we will need to scan legacy expansion ROM space. 1/ Promote the probe_roms_32 implementation to x86-64 2/ Add a facility to find and map an adapter rom by pci device (according to PCI Firmware Specification Revision 3.0) Signed-off-by: Dave Jiang <dave.jiang@intel.com> LKML-Reference: <20110308183226.6246.90354.stgit@localhost6.localdomain6> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/probe_roms.c')
-rw-r--r--arch/x86/kernel/probe_roms.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/arch/x86/kernel/probe_roms.c b/arch/x86/kernel/probe_roms.c
new file mode 100644
index 000000000000..ba0a4cce53be
--- /dev/null
+++ b/arch/x86/kernel/probe_roms.c
@@ -0,0 +1,267 @@
1#include <linux/sched.h>
2#include <linux/mm.h>
3#include <linux/uaccess.h>
4#include <linux/mmzone.h>
5#include <linux/ioport.h>
6#include <linux/seq_file.h>
7#include <linux/console.h>
8#include <linux/init.h>
9#include <linux/edd.h>
10#include <linux/dmi.h>
11#include <linux/pfn.h>
12#include <linux/pci.h>
13#include <asm/pci-direct.h>
14
15
16#include <asm/e820.h>
17#include <asm/mmzone.h>
18#include <asm/setup.h>
19#include <asm/sections.h>
20#include <asm/io.h>
21#include <asm/setup_arch.h>
22
23static struct resource system_rom_resource = {
24 .name = "System ROM",
25 .start = 0xf0000,
26 .end = 0xfffff,
27 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
28};
29
30static struct resource extension_rom_resource = {
31 .name = "Extension ROM",
32 .start = 0xe0000,
33 .end = 0xeffff,
34 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
35};
36
37static struct resource adapter_rom_resources[] = { {
38 .name = "Adapter ROM",
39 .start = 0xc8000,
40 .end = 0,
41 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
42}, {
43 .name = "Adapter ROM",
44 .start = 0,
45 .end = 0,
46 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
47}, {
48 .name = "Adapter ROM",
49 .start = 0,
50 .end = 0,
51 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
52}, {
53 .name = "Adapter ROM",
54 .start = 0,
55 .end = 0,
56 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
57}, {
58 .name = "Adapter ROM",
59 .start = 0,
60 .end = 0,
61 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
62}, {
63 .name = "Adapter ROM",
64 .start = 0,
65 .end = 0,
66 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
67} };
68
69static struct resource video_rom_resource = {
70 .name = "Video ROM",
71 .start = 0xc0000,
72 .end = 0xc7fff,
73 .flags = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
74};
75
76/* does this oprom support the given pci device, or any of the devices
77 * that the driver supports?
78 */
79static bool match_id(struct pci_dev *pdev, unsigned short vendor, unsigned short device)
80{
81 struct pci_driver *drv = pdev->driver;
82 const struct pci_device_id *id;
83
84 if (pdev->vendor == vendor && pdev->device == device)
85 return true;
86
87 for (id = drv ? drv->id_table : NULL; id && id->vendor; id++)
88 if (id->vendor == vendor && id->device == device)
89 break;
90
91 return id && id->vendor;
92}
93
94static bool probe_list(struct pci_dev *pdev, unsigned short vendor,
95 const unsigned char *rom_list)
96{
97 unsigned short device;
98
99 do {
100 if (probe_kernel_address(rom_list, device) != 0)
101 device = 0;
102
103 if (device && match_id(pdev, vendor, device))
104 break;
105
106 rom_list += 2;
107 } while (device);
108
109 return !!device;
110}
111
112static struct resource *find_oprom(struct pci_dev *pdev)
113{
114 struct resource *oprom = NULL;
115 int i;
116
117 for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) {
118 struct resource *res = &adapter_rom_resources[i];
119 unsigned short offset, vendor, device, list, rev;
120 const unsigned char *rom;
121
122 if (res->end == 0)
123 break;
124
125 rom = isa_bus_to_virt(res->start);
126 if (probe_kernel_address(rom + 0x18, offset) != 0)
127 continue;
128
129 if (probe_kernel_address(rom + offset + 0x4, vendor) != 0)
130 continue;
131
132 if (probe_kernel_address(rom + offset + 0x6, device) != 0)
133 continue;
134
135 if (match_id(pdev, vendor, device)) {
136 oprom = res;
137 break;
138 }
139
140 if (probe_kernel_address(rom + offset + 0x8, list) == 0 &&
141 probe_kernel_address(rom + offset + 0xc, rev) == 0 &&
142 rev >= 3 && list &&
143 probe_list(pdev, vendor, rom + offset + list)) {
144 oprom = res;
145 break;
146 }
147 }
148
149 return oprom;
150}
151
152void *pci_map_biosrom(struct pci_dev *pdev)
153{
154 struct resource *oprom = find_oprom(pdev);
155
156 if (!oprom)
157 return NULL;
158
159 return ioremap(oprom->start, resource_size(oprom));
160}
161EXPORT_SYMBOL(pci_map_biosrom);
162
163void pci_unmap_biosrom(void __iomem *image)
164{
165 iounmap(image);
166}
167EXPORT_SYMBOL(pci_unmap_biosrom);
168
169size_t pci_biosrom_size(struct pci_dev *pdev)
170{
171 struct resource *oprom = find_oprom(pdev);
172
173 return oprom ? resource_size(oprom) : 0;
174}
175EXPORT_SYMBOL(pci_biosrom_size);
176
177#define ROMSIGNATURE 0xaa55
178
179static int __init romsignature(const unsigned char *rom)
180{
181 const unsigned short * const ptr = (const unsigned short *)rom;
182 unsigned short sig;
183
184 return probe_kernel_address(ptr, sig) == 0 && sig == ROMSIGNATURE;
185}
186
187static int __init romchecksum(const unsigned char *rom, unsigned long length)
188{
189 unsigned char sum, c;
190
191 for (sum = 0; length && probe_kernel_address(rom++, c) == 0; length--)
192 sum += c;
193 return !length && !sum;
194}
195
196void __init probe_roms(void)
197{
198 const unsigned char *rom;
199 unsigned long start, length, upper;
200 unsigned char c;
201 int i;
202
203 /* video rom */
204 upper = adapter_rom_resources[0].start;
205 for (start = video_rom_resource.start; start < upper; start += 2048) {
206 rom = isa_bus_to_virt(start);
207 if (!romsignature(rom))
208 continue;
209
210 video_rom_resource.start = start;
211
212 if (probe_kernel_address(rom + 2, c) != 0)
213 continue;
214
215 /* 0 < length <= 0x7f * 512, historically */
216 length = c * 512;
217
218 /* if checksum okay, trust length byte */
219 if (length && romchecksum(rom, length))
220 video_rom_resource.end = start + length - 1;
221
222 request_resource(&iomem_resource, &video_rom_resource);
223 break;
224 }
225
226 start = (video_rom_resource.end + 1 + 2047) & ~2047UL;
227 if (start < upper)
228 start = upper;
229
230 /* system rom */
231 request_resource(&iomem_resource, &system_rom_resource);
232 upper = system_rom_resource.start;
233
234 /* check for extension rom (ignore length byte!) */
235 rom = isa_bus_to_virt(extension_rom_resource.start);
236 if (romsignature(rom)) {
237 length = extension_rom_resource.end - extension_rom_resource.start + 1;
238 if (romchecksum(rom, length)) {
239 request_resource(&iomem_resource, &extension_rom_resource);
240 upper = extension_rom_resource.start;
241 }
242 }
243
244 /* check for adapter roms on 2k boundaries */
245 for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) {
246 rom = isa_bus_to_virt(start);
247 if (!romsignature(rom))
248 continue;
249
250 if (probe_kernel_address(rom + 2, c) != 0)
251 continue;
252
253 /* 0 < length <= 0x7f * 512, historically */
254 length = c * 512;
255
256 /* but accept any length that fits if checksum okay */
257 if (!length || start + length > upper || !romchecksum(rom, length))
258 continue;
259
260 adapter_rom_resources[i].start = start;
261 adapter_rom_resources[i].end = start + length - 1;
262 request_resource(&iomem_resource, &adapter_rom_resources[i]);
263
264 start = adapter_rom_resources[i++].end & ~2047UL;
265 }
266}
267