]>
Commit | Line | Data |
---|---|---|
39795f9c DW |
1 | /* |
2 | * probe_roms - scan for Adapter ROMS | |
3 | * | |
4 | * (based on linux-2.6:arch/x86/kernel/probe_roms_32.c) | |
5 | * | |
6 | * Copyright (C) 2008 Intel Corporation | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms and conditions of the GNU General Public License, | |
10 | * version 2, as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | */ | |
21 | ||
22 | #include "probe_roms.h" | |
23 | #include <unistd.h> | |
24 | #include <signal.h> | |
25 | #include <fcntl.h> | |
26 | #include <sys/mman.h> | |
27 | #include <sys/stat.h> | |
28 | #include <sys/types.h> | |
29 | #include <asm/types.h> | |
30 | ||
31 | static void *rom_mem = MAP_FAILED; | |
32 | static int rom_fd = -1; | |
33 | const static int rom_len = 0xf0000 - 0xc0000; /* option-rom memory region */ | |
34 | static int _sigbus; | |
35 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | |
36 | ||
37 | static void sigbus(int sig) | |
38 | { | |
39 | _sigbus = 1; | |
40 | } | |
41 | ||
42 | static int probe_address8(const __u8 *ptr, __u8 *val) | |
43 | { | |
44 | int rc = 0; | |
45 | ||
46 | *val = *ptr; | |
47 | if (_sigbus) | |
48 | rc = -1; | |
49 | _sigbus = 0; | |
50 | ||
51 | return rc; | |
52 | } | |
53 | ||
54 | static int probe_address16(const __u16 *ptr, __u16 *val) | |
55 | { | |
56 | int rc = 0; | |
57 | ||
58 | *val = *ptr; | |
59 | if (_sigbus) | |
60 | rc = -1; | |
61 | _sigbus = 0; | |
62 | ||
63 | return rc; | |
64 | } | |
65 | ||
66 | void probe_roms_exit(void) | |
67 | { | |
68 | signal(SIGBUS, SIG_DFL); | |
69 | if (rom_fd >= 0) { | |
70 | close(rom_fd); | |
71 | rom_fd = -1; | |
72 | } | |
73 | if (rom_mem != MAP_FAILED) { | |
74 | munmap(rom_mem, rom_len); | |
75 | rom_mem = MAP_FAILED; | |
76 | } | |
77 | } | |
78 | ||
79 | int probe_roms_init(void) | |
80 | { | |
81 | int fd; | |
82 | int rc = 0; | |
83 | ||
84 | if (signal(SIGBUS, sigbus) == SIG_ERR) | |
85 | rc = -1; | |
86 | if (rc == 0) { | |
87 | fd = open("/dev/mem", O_RDONLY); | |
88 | if (fd < 0) | |
89 | rc = -1; | |
90 | } | |
91 | if (rc == 0) { | |
92 | rom_mem = mmap(NULL, rom_len, PROT_READ, MAP_PRIVATE, fd, 0xc0000); | |
93 | if (rom_mem == MAP_FAILED) | |
94 | rc = -1; | |
95 | } | |
96 | ||
97 | if (rc == 0) | |
98 | rom_fd = fd; | |
99 | else | |
100 | probe_roms_exit(); | |
101 | ||
102 | return rc; | |
103 | } | |
104 | ||
105 | /** | |
106 | * isa_bus_to_virt - convert physical address to mmap'd region | |
107 | * @addr - address to convert | |
108 | * | |
109 | * Only valid between a successful call to probe_roms_init and the | |
110 | * corresponding probe_roms_exit | |
111 | */ | |
112 | static void *isa_bus_to_virt(unsigned long addr) | |
113 | { | |
114 | return rom_mem + (addr - 0xc0000); | |
115 | } | |
116 | ||
117 | struct resource { | |
118 | unsigned long start; | |
119 | unsigned long end; | |
120 | const char *name; | |
121 | }; | |
122 | ||
123 | static struct resource system_rom_resource = { | |
124 | .name = "System ROM", | |
125 | .start = 0xf0000, | |
126 | .end = 0xfffff, | |
127 | }; | |
128 | ||
129 | static struct resource extension_rom_resource = { | |
130 | .name = "Extension ROM", | |
131 | .start = 0xe0000, | |
132 | .end = 0xeffff, | |
133 | }; | |
134 | ||
135 | static struct resource adapter_rom_resources[] = { { | |
136 | .name = "Adapter ROM", | |
137 | .start = 0xc8000, | |
138 | .end = 0, | |
139 | }, { | |
140 | .name = "Adapter ROM", | |
141 | .start = 0, | |
142 | .end = 0, | |
143 | }, { | |
144 | .name = "Adapter ROM", | |
145 | .start = 0, | |
146 | .end = 0, | |
147 | }, { | |
148 | .name = "Adapter ROM", | |
149 | .start = 0, | |
150 | .end = 0, | |
151 | }, { | |
152 | .name = "Adapter ROM", | |
153 | .start = 0, | |
154 | .end = 0, | |
155 | }, { | |
156 | .name = "Adapter ROM", | |
157 | .start = 0, | |
158 | .end = 0, | |
159 | } }; | |
160 | ||
161 | static struct resource video_rom_resource = { | |
162 | .name = "Video ROM", | |
163 | .start = 0xc0000, | |
164 | .end = 0xc7fff, | |
165 | }; | |
166 | ||
167 | #define ROMSIGNATURE 0xaa55 | |
168 | ||
169 | static int romsignature(const unsigned char *rom) | |
170 | { | |
171 | const unsigned short * const ptr = (const unsigned short *)rom; | |
172 | unsigned short sig = 0; | |
173 | ||
174 | return probe_address16(ptr, &sig) == 0 && sig == ROMSIGNATURE; | |
175 | } | |
176 | ||
177 | static int romchecksum(const unsigned char *rom, unsigned long length) | |
178 | { | |
179 | unsigned char sum, c; | |
180 | ||
181 | for (sum = 0; length && probe_address8(rom++, &c) == 0; length--) | |
182 | sum += c; | |
183 | return !length && !sum; | |
184 | } | |
185 | ||
186 | int scan_adapter_roms(scan_fn fn) | |
187 | { | |
188 | /* let scan_fn examing each of the adapter roms found by probe_roms */ | |
189 | int i; | |
190 | int found; | |
191 | ||
192 | if (rom_fd < 0) | |
193 | return 0; | |
194 | ||
195 | found = 0; | |
196 | for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) { | |
197 | struct resource *res = &adapter_rom_resources[i]; | |
198 | ||
199 | if (res->start) { | |
200 | found = fn(isa_bus_to_virt(res->start), | |
201 | isa_bus_to_virt(res->end)); | |
202 | if (found) | |
203 | break; | |
204 | } else | |
205 | break; | |
206 | } | |
207 | ||
208 | return found; | |
209 | } | |
210 | ||
211 | void probe_roms(void) | |
212 | { | |
213 | const void *rom; | |
214 | unsigned long start, length, upper; | |
215 | unsigned char c; | |
216 | int i; | |
217 | ||
218 | if (rom_fd < 0) | |
219 | return; | |
220 | ||
221 | /* video rom */ | |
222 | upper = adapter_rom_resources[0].start; | |
223 | for (start = video_rom_resource.start; start < upper; start += 2048) { | |
224 | rom = isa_bus_to_virt(start); | |
225 | if (!romsignature(rom)) | |
226 | continue; | |
227 | ||
228 | video_rom_resource.start = start; | |
229 | ||
230 | if (probe_address8(rom + 2, &c) != 0) | |
231 | continue; | |
232 | ||
233 | /* 0 < length <= 0x7f * 512, historically */ | |
234 | length = c * 512; | |
235 | ||
236 | /* if checksum okay, trust length byte */ | |
237 | if (length && romchecksum(rom, length)) | |
238 | video_rom_resource.end = start + length - 1; | |
239 | break; | |
240 | } | |
241 | ||
242 | start = (video_rom_resource.end + 1 + 2047) & ~2047UL; | |
243 | if (start < upper) | |
244 | start = upper; | |
245 | ||
246 | /* system rom */ | |
247 | upper = system_rom_resource.start; | |
248 | ||
249 | /* check for extension rom (ignore length byte!) */ | |
250 | rom = isa_bus_to_virt(extension_rom_resource.start); | |
251 | if (romsignature(rom)) { | |
252 | length = extension_rom_resource.end - extension_rom_resource.start + 1; | |
253 | if (romchecksum(rom, length)) | |
254 | upper = extension_rom_resource.start; | |
255 | } | |
256 | ||
257 | /* check for adapter roms on 2k boundaries */ | |
258 | for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) { | |
259 | rom = isa_bus_to_virt(start); | |
260 | if (!romsignature(rom)) | |
261 | continue; | |
262 | ||
263 | if (probe_address8(rom + 2, &c) != 0) | |
264 | continue; | |
265 | ||
266 | /* 0 < length <= 0x7f * 512, historically */ | |
267 | length = c * 512; | |
268 | ||
269 | /* but accept any length that fits if checksum okay */ | |
270 | if (!length || start + length > upper || !romchecksum(rom, length)) | |
271 | continue; | |
272 | ||
273 | adapter_rom_resources[i].start = start; | |
274 | adapter_rom_resources[i].end = start + length - 1; | |
275 | ||
276 | start = adapter_rom_resources[i++].end & ~2047UL; | |
277 | } | |
278 | } | |
279 |