#include <linux/slab.h>
#include <asm/dmi.h>
+/*
+ * DMI stands for "Desktop Management Interface". It is part
+ * of and an antecedent to, SMBIOS, which stands for System
+ * Management BIOS. See further: http://www.dmtf.org/standards
+ */
static char dmi_empty_string[] = " ";
-static char * __init dmi_string(const struct dmi_header *dm, u8 s)
+/*
+ * Catch too early calls to dmi_check_system():
+ */
+static int dmi_initialized;
+
+static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
{
const u8 *bp = ((u8 *) dm) + dm->length;
- char *str = "";
if (s) {
s--;
if (!memcmp(bp, dmi_empty_string, cmp_len))
return dmi_empty_string;
- str = dmi_alloc(len);
- if (str != NULL)
- strcpy(str, bp);
- else
- printk(KERN_ERR "dmi_string: cannot allocate %Zu bytes.\n", len);
+ return bp;
}
}
+ return "";
+}
+
+static char * __init dmi_string(const struct dmi_header *dm, u8 s)
+{
+ const char *bp = dmi_string_nosave(dm, s);
+ char *str;
+ size_t len;
+
+ if (bp == dmi_empty_string)
+ return dmi_empty_string;
+
+ len = strlen(bp) + 1;
+ str = dmi_alloc(len);
+ if (str != NULL)
+ strcpy(str, bp);
+ else
+ printk(KERN_ERR "dmi_string: cannot allocate %Zu bytes.\n", len);
+
return str;
}
* We have to be cautious here. We have seen BIOSes with DMI pointers
* pointing to completely the wrong place for example
*/
-static int __init dmi_table(u32 base, int len, int num,
- void (*decode)(const struct dmi_header *))
+static void dmi_table(u8 *buf, int len, int num,
+ void (*decode)(const struct dmi_header *))
{
- u8 *buf, *data;
+ u8 *data = buf;
int i = 0;
- buf = dmi_ioremap(base, len);
- if (buf == NULL)
- return -1;
-
- data = buf;
-
/*
* Stop when we see all the items the table claimed to have
* OR we run off the end of the table (also happens)
const struct dmi_header *dm = (const struct dmi_header *)data;
/*
- * We want to know the total length (formated area and strings)
- * before decoding to make sure we won't run off the table in
- * dmi_decode or dmi_string
+ * We want to know the total length (formatted area and
+ * strings) before decoding to make sure we won't run off the
+ * table in dmi_decode or dmi_string
*/
data += dm->length;
while ((data - buf < len - 1) && (data[0] || data[1]))
data += 2;
i++;
}
- dmi_iounmap(buf, len);
+}
+
+static u32 dmi_base;
+static u16 dmi_len;
+static u16 dmi_num;
+
+static int __init dmi_walk_early(void (*decode)(const struct dmi_header *))
+{
+ u8 *buf;
+
+ buf = dmi_ioremap(dmi_base, dmi_len);
+ if (buf == NULL)
+ return -1;
+
+ dmi_table(buf, dmi_len, dmi_num, decode);
+
+ dmi_iounmap(buf, dmi_len);
return 0;
}
dmi_ident[slot] = s;
}
+static void __init dmi_save_one_device(int type, const char *name)
+{
+ struct dmi_device *dev;
+
+ /* No duplicate device */
+ if (dmi_find_device(type, name, NULL))
+ return;
+
+ dev = dmi_alloc(sizeof(*dev) + strlen(name) + 1);
+ if (!dev) {
+ printk(KERN_ERR "dmi_save_one_device: out of memory.\n");
+ return;
+ }
+
+ dev->type = type;
+ strcpy((char *)(dev + 1), name);
+ dev->name = (char *)(dev + 1);
+ dev->device_data = NULL;
+ list_add(&dev->list, &dmi_devices);
+}
+
static void __init dmi_save_devices(const struct dmi_header *dm)
{
int i, count = (dm->length - sizeof(struct dmi_header)) / 2;
- struct dmi_device *dev;
for (i = 0; i < count; i++) {
const char *d = (char *)(dm + 1) + (i * 2);
if ((*d & 0x80) == 0)
continue;
- dev = dmi_alloc(sizeof(*dev));
- if (!dev) {
- printk(KERN_ERR "dmi_save_devices: out of memory.\n");
- break;
- }
-
- dev->type = *d++ & 0x7f;
- dev->name = dmi_string(dm, *d);
- dev->device_data = NULL;
- list_add(&dev->list, &dmi_devices);
+ dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d + 1)));
}
}
-static struct dmi_device empty_oem_string_dev = {
- .name = dmi_empty_string,
-};
-
static void __init dmi_save_oem_strings_devices(const struct dmi_header *dm)
{
int i, count = *(u8 *)(dm + 1);
for (i = 1; i <= count; i++) {
char *devname = dmi_string(dm, i);
- if (!strcmp(devname, dmi_empty_string)) {
- list_add(&empty_oem_string_dev.list, &dmi_devices);
+ if (devname == dmi_empty_string)
continue;
- }
dev = dmi_alloc(sizeof(*dev));
if (!dev) {
dev->name = "IPMI controller";
dev->device_data = data;
- list_add(&dev->list, &dmi_devices);
+ list_add_tail(&dev->list, &dmi_devices);
+}
+
+static void __init dmi_save_extended_devices(const struct dmi_header *dm)
+{
+ const u8 *d = (u8*) dm + 5;
+
+ /* Skip disabled device */
+ if ((*d & 0x80) == 0)
+ return;
+
+ dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1)));
}
/*
break;
case 38: /* IPMI Device Information */
dmi_save_ipmi_device(dm);
+ break;
+ case 41: /* Onboard Devices Extended Information */
+ dmi_save_extended_devices(dm);
}
}
memcpy_fromio(buf, p, 15);
if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) {
- u16 num = (buf[13] << 8) | buf[12];
- u16 len = (buf[7] << 8) | buf[6];
- u32 base = (buf[11] << 24) | (buf[10] << 16) |
+ dmi_num = (buf[13] << 8) | buf[12];
+ dmi_len = (buf[7] << 8) | buf[6];
+ dmi_base = (buf[11] << 24) | (buf[10] << 16) |
(buf[9] << 8) | buf[8];
/*
buf[14] >> 4, buf[14] & 0xF);
else
printk(KERN_INFO "DMI present.\n");
- if (dmi_table(base,len, num, dmi_decode) == 0)
+ if (dmi_walk_early(dmi_decode) == 0)
return 0;
}
return 1;
if (efi_enabled) {
if (efi.smbios == EFI_INVALID_TABLE_ADDR)
- goto out;
+ goto error;
/* This is called as a core_initcall() because it isn't
* needed during early boot. This also means we can
*/
p = dmi_ioremap(efi.smbios, 32);
if (p == NULL)
- goto out;
+ goto error;
rc = dmi_present(p + 0x10); /* offset of _DMI_ string */
dmi_iounmap(p, 32);
if (!rc) {
dmi_available = 1;
- return;
+ goto out;
}
}
else {
*/
p = dmi_ioremap(0xF0000, 0x10000);
if (p == NULL)
- goto out;
+ goto error;
for (q = p; q < p + 0x10000; q += 16) {
rc = dmi_present(q);
if (!rc) {
dmi_available = 1;
dmi_iounmap(p, 0x10000);
- return;
+ goto out;
}
}
dmi_iounmap(p, 0x10000);
}
- out: printk(KERN_INFO "DMI not present or invalid.\n");
+ error:
+ printk(KERN_INFO "DMI not present or invalid.\n");
+ out:
+ dmi_initialized = 1;
}
/**
int i, count = 0;
const struct dmi_system_id *d = list;
+ WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n");
+
while (d->ident) {
for (i = 0; i < ARRAY_SIZE(d->matches); i++) {
int s = d->matches[i].slot;
}
/**
- * dmi_get_slot - return dmi_ident[slot]
- * @slot: index into dmi_ident[]
+ * dmi_walk - Walk the DMI table and get called back for every record
+ * @decode: Callback function
+ *
+ * Returns -1 when the DMI table can't be reached, 0 on success.
*/
-char *dmi_get_slot(int slot)
+int dmi_walk(void (*decode)(const struct dmi_header *))
{
- return(dmi_ident[slot]);
+ u8 *buf;
+
+ if (!dmi_available)
+ return -1;
+
+ buf = ioremap(dmi_base, dmi_len);
+ if (buf == NULL)
+ return -1;
+
+ dmi_table(buf, dmi_len, dmi_num, decode);
+
+ iounmap(buf);
+ return 0;
}
+EXPORT_SYMBOL_GPL(dmi_walk);