Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | #include <linux/types.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/string.h> | |
4 | #include <linux/init.h> | |
5 | #include <linux/module.h> | |
6 | #include <linux/slab.h> | |
7 | #include <linux/acpi.h> | |
8 | #include <asm/io.h> | |
9 | #include <linux/pm.h> | |
10 | #include <asm/system.h> | |
11 | #include <linux/dmi.h> | |
12 | #include <linux/bootmem.h> | |
13 | ||
14 | ||
15 | struct dmi_header | |
16 | { | |
17 | u8 type; | |
18 | u8 length; | |
19 | u16 handle; | |
20 | }; | |
21 | ||
22 | #undef DMI_DEBUG | |
23 | ||
24 | #ifdef DMI_DEBUG | |
25 | #define dmi_printk(x) printk x | |
26 | #else | |
27 | #define dmi_printk(x) | |
28 | #endif | |
29 | ||
30 | static char * __init dmi_string(struct dmi_header *dm, u8 s) | |
31 | { | |
32 | u8 *bp=(u8 *)dm; | |
33 | bp+=dm->length; | |
34 | if(!s) | |
35 | return ""; | |
36 | s--; | |
37 | while(s>0 && *bp) | |
38 | { | |
39 | bp+=strlen(bp); | |
40 | bp++; | |
41 | s--; | |
42 | } | |
43 | return bp; | |
44 | } | |
45 | ||
46 | /* | |
47 | * We have to be cautious here. We have seen BIOSes with DMI pointers | |
48 | * pointing to completely the wrong place for example | |
49 | */ | |
50 | ||
51 | static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *)) | |
52 | { | |
53 | u8 *buf; | |
54 | struct dmi_header *dm; | |
55 | u8 *data; | |
56 | int i=0; | |
57 | ||
58 | buf = bt_ioremap(base, len); | |
59 | if(buf==NULL) | |
60 | return -1; | |
61 | ||
62 | data = buf; | |
63 | ||
64 | /* | |
65 | * Stop when we see all the items the table claimed to have | |
66 | * OR we run off the end of the table (also happens) | |
67 | */ | |
68 | ||
69 | while(i<num && data-buf+sizeof(struct dmi_header)<=len) | |
70 | { | |
71 | dm=(struct dmi_header *)data; | |
72 | /* | |
73 | * We want to know the total length (formated area and strings) | |
74 | * before decoding to make sure we won't run off the table in | |
75 | * dmi_decode or dmi_string | |
76 | */ | |
77 | data+=dm->length; | |
78 | while(data-buf<len-1 && (data[0] || data[1])) | |
79 | data++; | |
80 | if(data-buf<len-1) | |
81 | decode(dm); | |
82 | data+=2; | |
83 | i++; | |
84 | } | |
85 | bt_iounmap(buf, len); | |
86 | return 0; | |
87 | } | |
88 | ||
89 | ||
90 | inline static int __init dmi_checksum(u8 *buf) | |
91 | { | |
92 | u8 sum=0; | |
93 | int a; | |
94 | ||
95 | for(a=0; a<15; a++) | |
96 | sum+=buf[a]; | |
97 | return (sum==0); | |
98 | } | |
99 | ||
100 | static int __init dmi_iterate(void (*decode)(struct dmi_header *)) | |
101 | { | |
102 | u8 buf[15]; | |
103 | char __iomem *p, *q; | |
104 | ||
105 | /* | |
106 | * no iounmap() for that ioremap(); it would be a no-op, but it's | |
107 | * so early in setup that sucker gets confused into doing what | |
108 | * it shouldn't if we actually call it. | |
109 | */ | |
110 | p = ioremap(0xF0000, 0x10000); | |
111 | if (p == NULL) | |
112 | return -1; | |
113 | for (q = p; q < p + 0x10000; q += 16) { | |
114 | memcpy_fromio(buf, q, 15); | |
115 | if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf)) | |
116 | { | |
117 | u16 num=buf[13]<<8|buf[12]; | |
118 | u16 len=buf[7]<<8|buf[6]; | |
119 | u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; | |
120 | ||
121 | /* | |
122 | * DMI version 0.0 means that the real version is taken from | |
123 | * the SMBIOS version, which we don't know at this point. | |
124 | */ | |
125 | if(buf[14]!=0) | |
126 | printk(KERN_INFO "DMI %d.%d present.\n", | |
127 | buf[14]>>4, buf[14]&0x0F); | |
128 | else | |
129 | printk(KERN_INFO "DMI present.\n"); | |
130 | dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n", | |
131 | num, len)); | |
132 | dmi_printk((KERN_INFO "DMI table at 0x%08X.\n", | |
133 | base)); | |
134 | if(dmi_table(base,len, num, decode)==0) | |
135 | return 0; | |
136 | } | |
137 | } | |
138 | return -1; | |
139 | } | |
140 | ||
141 | static char *dmi_ident[DMI_STRING_MAX]; | |
142 | ||
143 | /* | |
144 | * Save a DMI string | |
145 | */ | |
146 | ||
147 | static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) | |
148 | { | |
149 | char *d = (char*)dm; | |
150 | char *p = dmi_string(dm, d[string]); | |
151 | if(p==NULL || *p == 0) | |
152 | return; | |
153 | if (dmi_ident[slot]) | |
154 | return; | |
155 | dmi_ident[slot] = alloc_bootmem(strlen(p)+1); | |
156 | if(dmi_ident[slot]) | |
157 | strcpy(dmi_ident[slot], p); | |
158 | else | |
159 | printk(KERN_ERR "dmi_save_ident: out of memory.\n"); | |
160 | } | |
161 | ||
162 | /* | |
163 | * Ugly compatibility crap. | |
164 | */ | |
165 | #define dmi_blacklist dmi_system_id | |
166 | #define NO_MATCH { DMI_NONE, NULL} | |
167 | #define MATCH DMI_MATCH | |
168 | ||
169 | /* | |
170 | * Toshiba keyboard likes to repeat keys when they are not repeated. | |
171 | */ | |
172 | ||
173 | static __init int broken_toshiba_keyboard(struct dmi_blacklist *d) | |
174 | { | |
175 | printk(KERN_WARNING "Toshiba with broken keyboard detected. If your keyboard sometimes generates 3 keypresses instead of one, see http://davyd.ucc.asn.au/projects/toshiba/README\n"); | |
176 | return 0; | |
177 | } | |
178 | ||
179 | ||
180 | #ifdef CONFIG_ACPI_SLEEP | |
181 | static __init int reset_videomode_after_s3(struct dmi_blacklist *d) | |
182 | { | |
183 | /* See acpi_wakeup.S */ | |
184 | extern long acpi_video_flags; | |
185 | acpi_video_flags |= 2; | |
186 | return 0; | |
187 | } | |
188 | #endif | |
189 | ||
190 | ||
191 | #ifdef CONFIG_ACPI_BOOT | |
192 | extern int acpi_force; | |
193 | ||
194 | static __init __attribute__((unused)) int dmi_disable_acpi(struct dmi_blacklist *d) | |
195 | { | |
196 | if (!acpi_force) { | |
197 | printk(KERN_NOTICE "%s detected: acpi off\n",d->ident); | |
198 | disable_acpi(); | |
199 | } else { | |
200 | printk(KERN_NOTICE | |
201 | "Warning: DMI blacklist says broken, but acpi forced\n"); | |
202 | } | |
203 | return 0; | |
204 | } | |
205 | ||
206 | /* | |
207 | * Limit ACPI to CPU enumeration for HT | |
208 | */ | |
209 | static __init __attribute__((unused)) int force_acpi_ht(struct dmi_blacklist *d) | |
210 | { | |
211 | if (!acpi_force) { | |
212 | printk(KERN_NOTICE "%s detected: force use of acpi=ht\n", d->ident); | |
213 | disable_acpi(); | |
214 | acpi_ht = 1; | |
215 | } else { | |
216 | printk(KERN_NOTICE | |
217 | "Warning: acpi=force overrules DMI blacklist: acpi=ht\n"); | |
218 | } | |
219 | return 0; | |
220 | } | |
221 | #endif | |
222 | ||
223 | #ifdef CONFIG_ACPI_PCI | |
224 | static __init int disable_acpi_irq(struct dmi_blacklist *d) | |
225 | { | |
226 | if (!acpi_force) { | |
227 | printk(KERN_NOTICE "%s detected: force use of acpi=noirq\n", | |
228 | d->ident); | |
229 | acpi_noirq_set(); | |
230 | } | |
231 | return 0; | |
232 | } | |
233 | static __init int disable_acpi_pci(struct dmi_blacklist *d) | |
234 | { | |
235 | if (!acpi_force) { | |
236 | printk(KERN_NOTICE "%s detected: force use of pci=noacpi\n", | |
237 | d->ident); | |
238 | acpi_disable_pci(); | |
239 | } | |
240 | return 0; | |
241 | } | |
242 | #endif | |
243 | ||
244 | /* | |
245 | * Process the DMI blacklists | |
246 | */ | |
247 | ||
248 | ||
249 | /* | |
250 | * This will be expanded over time to force things like the APM | |
251 | * interrupt mask settings according to the laptop | |
252 | */ | |
253 | ||
254 | static __initdata struct dmi_blacklist dmi_blacklist[]={ | |
255 | ||
256 | { broken_toshiba_keyboard, "Toshiba Satellite 4030cdt", { /* Keyboard generates spurious repeats */ | |
257 | MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), | |
258 | NO_MATCH, NO_MATCH, NO_MATCH | |
259 | } }, | |
260 | #ifdef CONFIG_ACPI_SLEEP | |
261 | { reset_videomode_after_s3, "Toshiba Satellite 4030cdt", { /* Reset video mode after returning from ACPI S3 sleep */ | |
262 | MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), | |
263 | NO_MATCH, NO_MATCH, NO_MATCH | |
264 | } }, | |
265 | #endif | |
266 | ||
267 | #ifdef CONFIG_ACPI_BOOT | |
268 | /* | |
269 | * If your system is blacklisted here, but you find that acpi=force | |
270 | * works for you, please contact acpi-devel@sourceforge.net | |
271 | */ | |
272 | ||
273 | /* | |
274 | * Boxes that need ACPI disabled | |
275 | */ | |
276 | ||
277 | { dmi_disable_acpi, "IBM Thinkpad", { | |
278 | MATCH(DMI_BOARD_VENDOR, "IBM"), | |
279 | MATCH(DMI_BOARD_NAME, "2629H1G"), | |
280 | NO_MATCH, NO_MATCH }}, | |
281 | ||
282 | /* | |
283 | * Boxes that need acpi=ht | |
284 | */ | |
285 | ||
286 | { force_acpi_ht, "FSC Primergy T850", { | |
287 | MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | |
288 | MATCH(DMI_PRODUCT_NAME, "PRIMERGY T850"), | |
289 | NO_MATCH, NO_MATCH }}, | |
290 | ||
291 | { force_acpi_ht, "DELL GX240", { | |
292 | MATCH(DMI_BOARD_VENDOR, "Dell Computer Corporation"), | |
293 | MATCH(DMI_BOARD_NAME, "OptiPlex GX240"), | |
294 | NO_MATCH, NO_MATCH }}, | |
295 | ||
296 | { force_acpi_ht, "HP VISUALIZE NT Workstation", { | |
297 | MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), | |
298 | MATCH(DMI_PRODUCT_NAME, "HP VISUALIZE NT Workstation"), | |
299 | NO_MATCH, NO_MATCH }}, | |
300 | ||
301 | { force_acpi_ht, "Compaq Workstation W8000", { | |
302 | MATCH(DMI_SYS_VENDOR, "Compaq"), | |
303 | MATCH(DMI_PRODUCT_NAME, "Workstation W8000"), | |
304 | NO_MATCH, NO_MATCH }}, | |
305 | ||
306 | { force_acpi_ht, "ASUS P4B266", { | |
307 | MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | |
308 | MATCH(DMI_BOARD_NAME, "P4B266"), | |
309 | NO_MATCH, NO_MATCH }}, | |
310 | ||
311 | { force_acpi_ht, "ASUS P2B-DS", { | |
312 | MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | |
313 | MATCH(DMI_BOARD_NAME, "P2B-DS"), | |
314 | NO_MATCH, NO_MATCH }}, | |
315 | ||
316 | { force_acpi_ht, "ASUS CUR-DLS", { | |
317 | MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | |
318 | MATCH(DMI_BOARD_NAME, "CUR-DLS"), | |
319 | NO_MATCH, NO_MATCH }}, | |
320 | ||
321 | { force_acpi_ht, "ABIT i440BX-W83977", { | |
322 | MATCH(DMI_BOARD_VENDOR, "ABIT <http://www.abit.com>"), | |
323 | MATCH(DMI_BOARD_NAME, "i440BX-W83977 (BP6)"), | |
324 | NO_MATCH, NO_MATCH }}, | |
325 | ||
326 | { force_acpi_ht, "IBM Bladecenter", { | |
327 | MATCH(DMI_BOARD_VENDOR, "IBM"), | |
328 | MATCH(DMI_BOARD_NAME, "IBM eServer BladeCenter HS20"), | |
329 | NO_MATCH, NO_MATCH }}, | |
330 | ||
331 | { force_acpi_ht, "IBM eServer xSeries 360", { | |
332 | MATCH(DMI_BOARD_VENDOR, "IBM"), | |
333 | MATCH(DMI_BOARD_NAME, "eServer xSeries 360"), | |
334 | NO_MATCH, NO_MATCH }}, | |
335 | ||
336 | { force_acpi_ht, "IBM eserver xSeries 330", { | |
337 | MATCH(DMI_BOARD_VENDOR, "IBM"), | |
338 | MATCH(DMI_BOARD_NAME, "eserver xSeries 330"), | |
339 | NO_MATCH, NO_MATCH }}, | |
340 | ||
341 | { force_acpi_ht, "IBM eserver xSeries 440", { | |
342 | MATCH(DMI_BOARD_VENDOR, "IBM"), | |
343 | MATCH(DMI_PRODUCT_NAME, "eserver xSeries 440"), | |
344 | NO_MATCH, NO_MATCH }}, | |
345 | ||
346 | #endif // CONFIG_ACPI_BOOT | |
347 | ||
348 | #ifdef CONFIG_ACPI_PCI | |
349 | /* | |
350 | * Boxes that need ACPI PCI IRQ routing disabled | |
351 | */ | |
352 | ||
353 | { disable_acpi_irq, "ASUS A7V", { | |
354 | MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC"), | |
355 | MATCH(DMI_BOARD_NAME, "<A7V>"), | |
356 | /* newer BIOS, Revision 1011, does work */ | |
357 | MATCH(DMI_BIOS_VERSION, "ASUS A7V ACPI BIOS Revision 1007"), | |
358 | NO_MATCH }}, | |
359 | ||
360 | /* | |
361 | * Boxes that need ACPI PCI IRQ routing and PCI scan disabled | |
362 | */ | |
363 | { disable_acpi_pci, "ASUS PR-DLS", { /* _BBN 0 bug */ | |
364 | MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | |
365 | MATCH(DMI_BOARD_NAME, "PR-DLS"), | |
366 | MATCH(DMI_BIOS_VERSION, "ASUS PR-DLS ACPI BIOS Revision 1010"), | |
367 | MATCH(DMI_BIOS_DATE, "03/21/2003") }}, | |
368 | ||
369 | { disable_acpi_pci, "Acer TravelMate 36x Laptop", { | |
370 | MATCH(DMI_SYS_VENDOR, "Acer"), | |
371 | MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), | |
372 | NO_MATCH, NO_MATCH | |
373 | } }, | |
374 | ||
375 | #endif | |
376 | ||
377 | { NULL, } | |
378 | }; | |
379 | ||
380 | /* | |
381 | * Process a DMI table entry. Right now all we care about are the BIOS | |
382 | * and machine entries. For 2.5 we should pull the smbus controller info | |
383 | * out of here. | |
384 | */ | |
385 | ||
386 | static void __init dmi_decode(struct dmi_header *dm) | |
387 | { | |
388 | #ifdef DMI_DEBUG | |
389 | u8 *data = (u8 *)dm; | |
390 | #endif | |
391 | ||
392 | switch(dm->type) | |
393 | { | |
394 | case 0: | |
395 | dmi_printk(("BIOS Vendor: %s\n", | |
396 | dmi_string(dm, data[4]))); | |
397 | dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); | |
398 | dmi_printk(("BIOS Version: %s\n", | |
399 | dmi_string(dm, data[5]))); | |
400 | dmi_save_ident(dm, DMI_BIOS_VERSION, 5); | |
401 | dmi_printk(("BIOS Release: %s\n", | |
402 | dmi_string(dm, data[8]))); | |
403 | dmi_save_ident(dm, DMI_BIOS_DATE, 8); | |
404 | break; | |
405 | case 1: | |
406 | dmi_printk(("System Vendor: %s\n", | |
407 | dmi_string(dm, data[4]))); | |
408 | dmi_save_ident(dm, DMI_SYS_VENDOR, 4); | |
409 | dmi_printk(("Product Name: %s\n", | |
410 | dmi_string(dm, data[5]))); | |
411 | dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); | |
412 | dmi_printk(("Version: %s\n", | |
413 | dmi_string(dm, data[6]))); | |
414 | dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); | |
415 | dmi_printk(("Serial Number: %s\n", | |
416 | dmi_string(dm, data[7]))); | |
417 | break; | |
418 | case 2: | |
419 | dmi_printk(("Board Vendor: %s\n", | |
420 | dmi_string(dm, data[4]))); | |
421 | dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); | |
422 | dmi_printk(("Board Name: %s\n", | |
423 | dmi_string(dm, data[5]))); | |
424 | dmi_save_ident(dm, DMI_BOARD_NAME, 5); | |
425 | dmi_printk(("Board Version: %s\n", | |
426 | dmi_string(dm, data[6]))); | |
427 | dmi_save_ident(dm, DMI_BOARD_VERSION, 6); | |
428 | break; | |
429 | } | |
430 | } | |
431 | ||
432 | void __init dmi_scan_machine(void) | |
433 | { | |
434 | int err = dmi_iterate(dmi_decode); | |
435 | if(err == 0) | |
436 | dmi_check_system(dmi_blacklist); | |
437 | else | |
438 | printk(KERN_INFO "DMI not present.\n"); | |
439 | } | |
440 | ||
441 | ||
442 | /** | |
443 | * dmi_check_system - check system DMI data | |
444 | * @list: array of dmi_system_id structures to match against | |
445 | * | |
446 | * Walk the blacklist table running matching functions until someone | |
447 | * returns non zero or we hit the end. Callback function is called for | |
448 | * each successfull match. Returns the number of matches. | |
449 | */ | |
450 | int dmi_check_system(struct dmi_system_id *list) | |
451 | { | |
452 | int i, count = 0; | |
453 | struct dmi_system_id *d = list; | |
454 | ||
455 | while (d->ident) { | |
456 | for (i = 0; i < ARRAY_SIZE(d->matches); i++) { | |
457 | int s = d->matches[i].slot; | |
458 | if (s == DMI_NONE) | |
459 | continue; | |
460 | if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) | |
461 | continue; | |
462 | /* No match */ | |
463 | goto fail; | |
464 | } | |
465 | if (d->callback && d->callback(d)) | |
466 | break; | |
467 | count++; | |
468 | fail: d++; | |
469 | } | |
470 | ||
471 | return count; | |
472 | } | |
473 | ||
474 | EXPORT_SYMBOL(dmi_check_system); | |
475 | ||
476 | /** | |
477 | * dmi_get_system_info - return DMI data value | |
478 | * @field: data index (see enum dmi_filed) | |
479 | * | |
480 | * Returns one DMI data value, can be used to perform | |
481 | * complex DMI data checks. | |
482 | */ | |
483 | char * dmi_get_system_info(int field) | |
484 | { | |
485 | return dmi_ident[field]; | |
486 | } | |
487 |