blob: ae1a1aed2fc0d1e32bb26e9c1575b827e45c0465 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07002#include <linux/string.h>
3#include <linux/init.h>
4#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005#include <linux/dmi.h>
6#include <linux/bootmem.h>
7
8
Andrey Panin1249c512005-06-25 14:54:47 -07009struct dmi_header {
10 u8 type;
11 u8 length;
12 u16 handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -070013};
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
16static char * __init dmi_string(struct dmi_header *dm, u8 s)
17{
Andrey Panin1249c512005-06-25 14:54:47 -070018 u8 *bp = ((u8 *) dm) + dm->length;
Andrey Paninc3c71202005-09-06 15:18:28 -070019 char *str = "";
Andrey Panin1249c512005-06-25 14:54:47 -070020
Andrey Paninc3c71202005-09-06 15:18:28 -070021 if (s) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 s--;
Andrey Paninc3c71202005-09-06 15:18:28 -070023 while (s > 0 && *bp) {
24 bp += strlen(bp) + 1;
25 s--;
26 }
27
28 if (*bp != 0) {
29 str = alloc_bootmem(strlen(bp) + 1);
30 if (str != NULL)
31 strcpy(str, bp);
32 else
33 printk(KERN_ERR "dmi_string: out of memory.\n");
34 }
35 }
36
37 return str;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038}
39
40/*
41 * We have to be cautious here. We have seen BIOSes with DMI pointers
42 * pointing to completely the wrong place for example
43 */
Andrey Panin1249c512005-06-25 14:54:47 -070044static int __init dmi_table(u32 base, int len, int num,
45 void (*decode)(struct dmi_header *))
Linus Torvalds1da177e2005-04-16 15:20:36 -070046{
Andrey Panin1249c512005-06-25 14:54:47 -070047 u8 *buf, *data;
48 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50 buf = bt_ioremap(base, len);
Andrey Panin1249c512005-06-25 14:54:47 -070051 if (buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 return -1;
53
54 data = buf;
55
56 /*
57 * Stop when we see all the items the table claimed to have
58 * OR we run off the end of the table (also happens)
59 */
Andrey Panin1249c512005-06-25 14:54:47 -070060 while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
61 struct dmi_header *dm = (struct dmi_header *)data;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 /*
63 * We want to know the total length (formated area and strings)
64 * before decoding to make sure we won't run off the table in
65 * dmi_decode or dmi_string
66 */
Andrey Panin1249c512005-06-25 14:54:47 -070067 data += dm->length;
68 while ((data - buf < len - 1) && (data[0] || data[1]))
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 data++;
Andrey Panin1249c512005-06-25 14:54:47 -070070 if (data - buf < len - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 decode(dm);
Andrey Panin1249c512005-06-25 14:54:47 -070072 data += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 i++;
74 }
75 bt_iounmap(buf, len);
76 return 0;
77}
78
Andrey Panin1249c512005-06-25 14:54:47 -070079static int __init dmi_checksum(u8 *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080{
Andrey Panin1249c512005-06-25 14:54:47 -070081 u8 sum = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 int a;
83
Andrey Panin1249c512005-06-25 14:54:47 -070084 for (a = 0; a < 15; a++)
85 sum += buf[a];
86
87 return sum == 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static char *dmi_ident[DMI_STRING_MAX];
91
92/*
93 * Save a DMI string
94 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070095static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
96{
Andrey Paninc3c71202005-09-06 15:18:28 -070097 char *p, *d = (char*) dm;
Andrey Panin1249c512005-06-25 14:54:47 -070098
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 if (dmi_ident[slot])
100 return;
Andrey Panin1249c512005-06-25 14:54:47 -0700101
Andrey Paninc3c71202005-09-06 15:18:28 -0700102 p = dmi_string(dm, d[string]);
103 if (p == NULL)
104 return;
105
106 dmi_ident[slot] = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
109/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 * Process a DMI table entry. Right now all we care about are the BIOS
111 * and machine entries. For 2.5 we should pull the smbus controller info
112 * out of here.
113 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114static void __init dmi_decode(struct dmi_header *dm)
115{
Andrey Panin1249c512005-06-25 14:54:47 -0700116 u8 *data __attribute__((__unused__)) = (u8 *)dm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Andrey Panin1249c512005-06-25 14:54:47 -0700118 switch(dm->type) {
119 case 0:
Andrey Panin1249c512005-06-25 14:54:47 -0700120 dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
Andrey Panin1249c512005-06-25 14:54:47 -0700121 dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
Andrey Panin1249c512005-06-25 14:54:47 -0700122 dmi_save_ident(dm, DMI_BIOS_DATE, 8);
123 break;
124 case 1:
Andrey Panin1249c512005-06-25 14:54:47 -0700125 dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
Andrey Panin1249c512005-06-25 14:54:47 -0700126 dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
Andrey Panin1249c512005-06-25 14:54:47 -0700127 dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
Andrey Panin1249c512005-06-25 14:54:47 -0700128 dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
129 break;
130 case 2:
Andrey Panin1249c512005-06-25 14:54:47 -0700131 dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
Andrey Panin1249c512005-06-25 14:54:47 -0700132 dmi_save_ident(dm, DMI_BOARD_NAME, 5);
Andrey Panin1249c512005-06-25 14:54:47 -0700133 dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
134 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 }
136}
137
138void __init dmi_scan_machine(void)
139{
Andrey Panin61e032fa2005-09-06 15:18:26 -0700140 u8 buf[15];
141 char __iomem *p, *q;
142
143 /*
144 * no iounmap() for that ioremap(); it would be a no-op, but it's
145 * so early in setup that sucker gets confused into doing what
146 * it shouldn't if we actually call it.
147 */
148 p = ioremap(0xF0000, 0x10000);
149 if (p == NULL)
150 goto out;
151
152 for (q = p; q < p + 0x10000; q += 16) {
153 memcpy_fromio(buf, q, 15);
154 if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) {
155 u16 num = (buf[13] << 8) | buf[12];
156 u16 len = (buf[7] << 8) | buf[6];
157 u32 base = (buf[11] << 24) | (buf[10] << 16) |
158 (buf[9] << 8) | buf[8];
159
160 /*
161 * DMI version 0.0 means that the real version is taken from
162 * the SMBIOS version, which we don't know at this point.
163 */
164 if (buf[14] != 0)
165 printk(KERN_INFO "DMI %d.%d present.\n",
166 buf[14] >> 4, buf[14] & 0xF);
167 else
168 printk(KERN_INFO "DMI present.\n");
169
Andrey Panin61e032fa2005-09-06 15:18:26 -0700170 if (dmi_table(base,len, num, dmi_decode) == 0)
171 return;
172 }
173 }
174
175out: printk(KERN_INFO "DMI not present.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
178
179/**
180 * dmi_check_system - check system DMI data
181 * @list: array of dmi_system_id structures to match against
182 *
183 * Walk the blacklist table running matching functions until someone
184 * returns non zero or we hit the end. Callback function is called for
185 * each successfull match. Returns the number of matches.
186 */
187int dmi_check_system(struct dmi_system_id *list)
188{
189 int i, count = 0;
190 struct dmi_system_id *d = list;
191
192 while (d->ident) {
193 for (i = 0; i < ARRAY_SIZE(d->matches); i++) {
194 int s = d->matches[i].slot;
195 if (s == DMI_NONE)
196 continue;
197 if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr))
198 continue;
199 /* No match */
200 goto fail;
201 }
202 if (d->callback && d->callback(d))
203 break;
204 count++;
205fail: d++;
206 }
207
208 return count;
209}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210EXPORT_SYMBOL(dmi_check_system);
211
212/**
213 * dmi_get_system_info - return DMI data value
214 * @field: data index (see enum dmi_filed)
215 *
216 * Returns one DMI data value, can be used to perform
217 * complex DMI data checks.
218 */
Dmitry Torokhove70c9d52005-06-25 14:54:25 -0700219char *dmi_get_system_info(int field)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220{
221 return dmi_ident[field];
222}
Dmitry Torokhove70c9d52005-06-25 14:54:25 -0700223EXPORT_SYMBOL(dmi_get_system_info);