xref: /linux/drivers/firmware/dmi_scan.c (revision 79da4721117fcf188b4b007b775738a530f574da)
14f705ae3SBjorn Helgaas #include <linux/types.h>
24f705ae3SBjorn Helgaas #include <linux/string.h>
34f705ae3SBjorn Helgaas #include <linux/init.h>
44f705ae3SBjorn Helgaas #include <linux/module.h>
54f705ae3SBjorn Helgaas #include <linux/dmi.h>
64f705ae3SBjorn Helgaas #include <linux/efi.h>
74f705ae3SBjorn Helgaas #include <linux/bootmem.h>
84f705ae3SBjorn Helgaas #include <linux/slab.h>
94f705ae3SBjorn Helgaas #include <asm/dmi.h>
104f705ae3SBjorn Helgaas 
11*79da4721SParag Warudkar static char dmi_empty_string[] = "        ";
12*79da4721SParag Warudkar 
131855256cSJeff Garzik static char * __init dmi_string(const struct dmi_header *dm, u8 s)
144f705ae3SBjorn Helgaas {
151855256cSJeff Garzik 	const u8 *bp = ((u8 *) dm) + dm->length;
164f705ae3SBjorn Helgaas 	char *str = "";
174f705ae3SBjorn Helgaas 
184f705ae3SBjorn Helgaas 	if (s) {
194f705ae3SBjorn Helgaas 		s--;
204f705ae3SBjorn Helgaas 		while (s > 0 && *bp) {
214f705ae3SBjorn Helgaas 			bp += strlen(bp) + 1;
224f705ae3SBjorn Helgaas 			s--;
234f705ae3SBjorn Helgaas 		}
244f705ae3SBjorn Helgaas 
254f705ae3SBjorn Helgaas 		if (*bp != 0) {
26*79da4721SParag Warudkar 			size_t len = strlen(bp)+1;
27*79da4721SParag Warudkar 			size_t cmp_len = len > 8 ? 8 : len;
28*79da4721SParag Warudkar 
29*79da4721SParag Warudkar 			if (!memcmp(bp, dmi_empty_string, cmp_len))
30*79da4721SParag Warudkar 				return dmi_empty_string;
31*79da4721SParag Warudkar 			str = dmi_alloc(len);
324f705ae3SBjorn Helgaas 			if (str != NULL)
334f705ae3SBjorn Helgaas 				strcpy(str, bp);
344f705ae3SBjorn Helgaas 			else
35*79da4721SParag Warudkar 				printk(KERN_ERR "dmi_string: cannot allocate %Zu bytes.\n", len);
364f705ae3SBjorn Helgaas 		}
374f705ae3SBjorn Helgaas 	}
384f705ae3SBjorn Helgaas 
394f705ae3SBjorn Helgaas 	return str;
404f705ae3SBjorn Helgaas }
414f705ae3SBjorn Helgaas 
424f705ae3SBjorn Helgaas /*
434f705ae3SBjorn Helgaas  *	We have to be cautious here. We have seen BIOSes with DMI pointers
444f705ae3SBjorn Helgaas  *	pointing to completely the wrong place for example
454f705ae3SBjorn Helgaas  */
464f705ae3SBjorn Helgaas static int __init dmi_table(u32 base, int len, int num,
471855256cSJeff Garzik 			    void (*decode)(const struct dmi_header *))
484f705ae3SBjorn Helgaas {
494f705ae3SBjorn Helgaas 	u8 *buf, *data;
504f705ae3SBjorn Helgaas 	int i = 0;
514f705ae3SBjorn Helgaas 
524f705ae3SBjorn Helgaas 	buf = dmi_ioremap(base, len);
534f705ae3SBjorn Helgaas 	if (buf == NULL)
544f705ae3SBjorn Helgaas 		return -1;
554f705ae3SBjorn Helgaas 
564f705ae3SBjorn Helgaas 	data = buf;
574f705ae3SBjorn Helgaas 
584f705ae3SBjorn Helgaas 	/*
594f705ae3SBjorn Helgaas 	 *	Stop when we see all the items the table claimed to have
604f705ae3SBjorn Helgaas 	 *	OR we run off the end of the table (also happens)
614f705ae3SBjorn Helgaas 	 */
624f705ae3SBjorn Helgaas 	while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
631855256cSJeff Garzik 		const struct dmi_header *dm = (const struct dmi_header *)data;
641855256cSJeff Garzik 
654f705ae3SBjorn Helgaas 		/*
664f705ae3SBjorn Helgaas 		 *  We want to know the total length (formated area and strings)
674f705ae3SBjorn Helgaas 		 *  before decoding to make sure we won't run off the table in
684f705ae3SBjorn Helgaas 		 *  dmi_decode or dmi_string
694f705ae3SBjorn Helgaas 		 */
704f705ae3SBjorn Helgaas 		data += dm->length;
714f705ae3SBjorn Helgaas 		while ((data - buf < len - 1) && (data[0] || data[1]))
724f705ae3SBjorn Helgaas 			data++;
734f705ae3SBjorn Helgaas 		if (data - buf < len - 1)
744f705ae3SBjorn Helgaas 			decode(dm);
754f705ae3SBjorn Helgaas 		data += 2;
764f705ae3SBjorn Helgaas 		i++;
774f705ae3SBjorn Helgaas 	}
784f705ae3SBjorn Helgaas 	dmi_iounmap(buf, len);
794f705ae3SBjorn Helgaas 	return 0;
804f705ae3SBjorn Helgaas }
814f705ae3SBjorn Helgaas 
821855256cSJeff Garzik static int __init dmi_checksum(const u8 *buf)
834f705ae3SBjorn Helgaas {
844f705ae3SBjorn Helgaas 	u8 sum = 0;
854f705ae3SBjorn Helgaas 	int a;
864f705ae3SBjorn Helgaas 
874f705ae3SBjorn Helgaas 	for (a = 0; a < 15; a++)
884f705ae3SBjorn Helgaas 		sum += buf[a];
894f705ae3SBjorn Helgaas 
904f705ae3SBjorn Helgaas 	return sum == 0;
914f705ae3SBjorn Helgaas }
924f705ae3SBjorn Helgaas 
934f705ae3SBjorn Helgaas static char *dmi_ident[DMI_STRING_MAX];
944f705ae3SBjorn Helgaas static LIST_HEAD(dmi_devices);
954f5c791aSLennart Poettering int dmi_available;
964f705ae3SBjorn Helgaas 
974f705ae3SBjorn Helgaas /*
984f705ae3SBjorn Helgaas  *	Save a DMI string
994f705ae3SBjorn Helgaas  */
1001855256cSJeff Garzik static void __init dmi_save_ident(const struct dmi_header *dm, int slot, int string)
1014f705ae3SBjorn Helgaas {
1021855256cSJeff Garzik 	const char *d = (const char*) dm;
1031855256cSJeff Garzik 	char *p;
1044f705ae3SBjorn Helgaas 
1054f705ae3SBjorn Helgaas 	if (dmi_ident[slot])
1064f705ae3SBjorn Helgaas 		return;
1074f705ae3SBjorn Helgaas 
1084f705ae3SBjorn Helgaas 	p = dmi_string(dm, d[string]);
1094f705ae3SBjorn Helgaas 	if (p == NULL)
1104f705ae3SBjorn Helgaas 		return;
1114f705ae3SBjorn Helgaas 
1124f705ae3SBjorn Helgaas 	dmi_ident[slot] = p;
1134f705ae3SBjorn Helgaas }
1144f705ae3SBjorn Helgaas 
1151855256cSJeff Garzik static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, int index)
1164f5c791aSLennart Poettering {
1171855256cSJeff Garzik 	const u8 *d = (u8*) dm + index;
1184f5c791aSLennart Poettering 	char *s;
1194f5c791aSLennart Poettering 	int is_ff = 1, is_00 = 1, i;
1204f5c791aSLennart Poettering 
1214f5c791aSLennart Poettering 	if (dmi_ident[slot])
1224f5c791aSLennart Poettering 		return;
1234f5c791aSLennart Poettering 
1244f5c791aSLennart Poettering 	for (i = 0; i < 16 && (is_ff || is_00); i++) {
1254f5c791aSLennart Poettering 		if(d[i] != 0x00) is_ff = 0;
1264f5c791aSLennart Poettering 		if(d[i] != 0xFF) is_00 = 0;
1274f5c791aSLennart Poettering 	}
1284f5c791aSLennart Poettering 
1294f5c791aSLennart Poettering 	if (is_ff || is_00)
1304f5c791aSLennart Poettering 		return;
1314f5c791aSLennart Poettering 
1324f5c791aSLennart Poettering 	s = dmi_alloc(16*2+4+1);
1334f5c791aSLennart Poettering 	if (!s)
1344f5c791aSLennart Poettering 		return;
1354f5c791aSLennart Poettering 
1364f5c791aSLennart Poettering 	sprintf(s,
1374f5c791aSLennart Poettering 		"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
1384f5c791aSLennart Poettering 		d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
1394f5c791aSLennart Poettering 		d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
1404f5c791aSLennart Poettering 
1414f5c791aSLennart Poettering         dmi_ident[slot] = s;
1424f5c791aSLennart Poettering }
1434f5c791aSLennart Poettering 
1441855256cSJeff Garzik static void __init dmi_save_type(const struct dmi_header *dm, int slot, int index)
1454f5c791aSLennart Poettering {
1461855256cSJeff Garzik 	const u8 *d = (u8*) dm + index;
1474f5c791aSLennart Poettering 	char *s;
1484f5c791aSLennart Poettering 
1494f5c791aSLennart Poettering 	if (dmi_ident[slot])
1504f5c791aSLennart Poettering 		return;
1514f5c791aSLennart Poettering 
1524f5c791aSLennart Poettering 	s = dmi_alloc(4);
1534f5c791aSLennart Poettering 	if (!s)
1544f5c791aSLennart Poettering 		return;
1554f5c791aSLennart Poettering 
1564f5c791aSLennart Poettering 	sprintf(s, "%u", *d & 0x7F);
1574f5c791aSLennart Poettering 	dmi_ident[slot] = s;
1584f5c791aSLennart Poettering }
1594f5c791aSLennart Poettering 
1601855256cSJeff Garzik static void __init dmi_save_devices(const struct dmi_header *dm)
1614f705ae3SBjorn Helgaas {
1624f705ae3SBjorn Helgaas 	int i, count = (dm->length - sizeof(struct dmi_header)) / 2;
1634f705ae3SBjorn Helgaas 	struct dmi_device *dev;
1644f705ae3SBjorn Helgaas 
1654f705ae3SBjorn Helgaas 	for (i = 0; i < count; i++) {
1661855256cSJeff Garzik 		const char *d = (char *)(dm + 1) + (i * 2);
1674f705ae3SBjorn Helgaas 
1684f705ae3SBjorn Helgaas 		/* Skip disabled device */
1694f705ae3SBjorn Helgaas 		if ((*d & 0x80) == 0)
1704f705ae3SBjorn Helgaas 			continue;
1714f705ae3SBjorn Helgaas 
1724f705ae3SBjorn Helgaas 		dev = dmi_alloc(sizeof(*dev));
1734f705ae3SBjorn Helgaas 		if (!dev) {
1744f705ae3SBjorn Helgaas 			printk(KERN_ERR "dmi_save_devices: out of memory.\n");
1754f705ae3SBjorn Helgaas 			break;
1764f705ae3SBjorn Helgaas 		}
1774f705ae3SBjorn Helgaas 
1784f705ae3SBjorn Helgaas 		dev->type = *d++ & 0x7f;
1794f705ae3SBjorn Helgaas 		dev->name = dmi_string(dm, *d);
1804f705ae3SBjorn Helgaas 		dev->device_data = NULL;
1812e0c1f6cSShem Multinymous 		list_add(&dev->list, &dmi_devices);
1822e0c1f6cSShem Multinymous 	}
1832e0c1f6cSShem Multinymous }
1842e0c1f6cSShem Multinymous 
185*79da4721SParag Warudkar static struct dmi_device empty_oem_string_dev = {
186*79da4721SParag Warudkar 	.name = dmi_empty_string,
187*79da4721SParag Warudkar };
188*79da4721SParag Warudkar 
1891855256cSJeff Garzik static void __init dmi_save_oem_strings_devices(const struct dmi_header *dm)
1902e0c1f6cSShem Multinymous {
1912e0c1f6cSShem Multinymous 	int i, count = *(u8 *)(dm + 1);
1922e0c1f6cSShem Multinymous 	struct dmi_device *dev;
1932e0c1f6cSShem Multinymous 
1942e0c1f6cSShem Multinymous 	for (i = 1; i <= count; i++) {
195*79da4721SParag Warudkar 		char *devname = dmi_string(dm, i);
196*79da4721SParag Warudkar 
197*79da4721SParag Warudkar 		if (!strcmp(devname, dmi_empty_string)) {
198*79da4721SParag Warudkar 			list_add(&empty_oem_string_dev.list, &dmi_devices);
199*79da4721SParag Warudkar 			continue;
200*79da4721SParag Warudkar 		}
201*79da4721SParag Warudkar 
2022e0c1f6cSShem Multinymous 		dev = dmi_alloc(sizeof(*dev));
2032e0c1f6cSShem Multinymous 		if (!dev) {
2042e0c1f6cSShem Multinymous 			printk(KERN_ERR
2052e0c1f6cSShem Multinymous 			   "dmi_save_oem_strings_devices: out of memory.\n");
2062e0c1f6cSShem Multinymous 			break;
2072e0c1f6cSShem Multinymous 		}
2082e0c1f6cSShem Multinymous 
2092e0c1f6cSShem Multinymous 		dev->type = DMI_DEV_TYPE_OEM_STRING;
210*79da4721SParag Warudkar 		dev->name = devname;
2112e0c1f6cSShem Multinymous 		dev->device_data = NULL;
2124f705ae3SBjorn Helgaas 
2134f705ae3SBjorn Helgaas 		list_add(&dev->list, &dmi_devices);
2144f705ae3SBjorn Helgaas 	}
2154f705ae3SBjorn Helgaas }
2164f705ae3SBjorn Helgaas 
2171855256cSJeff Garzik static void __init dmi_save_ipmi_device(const struct dmi_header *dm)
2184f705ae3SBjorn Helgaas {
2194f705ae3SBjorn Helgaas 	struct dmi_device *dev;
2204f705ae3SBjorn Helgaas 	void * data;
2214f705ae3SBjorn Helgaas 
2224f705ae3SBjorn Helgaas 	data = dmi_alloc(dm->length);
2234f705ae3SBjorn Helgaas 	if (data == NULL) {
2244f705ae3SBjorn Helgaas 		printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n");
2254f705ae3SBjorn Helgaas 		return;
2264f705ae3SBjorn Helgaas 	}
2274f705ae3SBjorn Helgaas 
2284f705ae3SBjorn Helgaas 	memcpy(data, dm, dm->length);
2294f705ae3SBjorn Helgaas 
2304f705ae3SBjorn Helgaas 	dev = dmi_alloc(sizeof(*dev));
2314f705ae3SBjorn Helgaas 	if (!dev) {
2324f705ae3SBjorn Helgaas 		printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n");
2334f705ae3SBjorn Helgaas 		return;
2344f705ae3SBjorn Helgaas 	}
2354f705ae3SBjorn Helgaas 
2364f705ae3SBjorn Helgaas 	dev->type = DMI_DEV_TYPE_IPMI;
2374f705ae3SBjorn Helgaas 	dev->name = "IPMI controller";
2384f705ae3SBjorn Helgaas 	dev->device_data = data;
2394f705ae3SBjorn Helgaas 
2404f705ae3SBjorn Helgaas 	list_add(&dev->list, &dmi_devices);
2414f705ae3SBjorn Helgaas }
2424f705ae3SBjorn Helgaas 
2434f705ae3SBjorn Helgaas /*
2444f705ae3SBjorn Helgaas  *	Process a DMI table entry. Right now all we care about are the BIOS
2454f705ae3SBjorn Helgaas  *	and machine entries. For 2.5 we should pull the smbus controller info
2464f705ae3SBjorn Helgaas  *	out of here.
2474f705ae3SBjorn Helgaas  */
2481855256cSJeff Garzik static void __init dmi_decode(const struct dmi_header *dm)
2494f705ae3SBjorn Helgaas {
2504f705ae3SBjorn Helgaas 	switch(dm->type) {
2514f705ae3SBjorn Helgaas 	case 0:		/* BIOS Information */
2524f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
2534f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
2544f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BIOS_DATE, 8);
2554f705ae3SBjorn Helgaas 		break;
2564f705ae3SBjorn Helgaas 	case 1:		/* System Information */
2574f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
2584f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
2594f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
2604f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
2614f5c791aSLennart Poettering 		dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8);
2624f705ae3SBjorn Helgaas 		break;
2634f705ae3SBjorn Helgaas 	case 2:		/* Base Board Information */
2644f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
2654f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BOARD_NAME, 5);
2664f705ae3SBjorn Helgaas 		dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
2674f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_BOARD_SERIAL, 7);
2684f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_BOARD_ASSET_TAG, 8);
2694f5c791aSLennart Poettering 		break;
2704f5c791aSLennart Poettering 	case 3:		/* Chassis Information */
2714f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_CHASSIS_VENDOR, 4);
2724f5c791aSLennart Poettering 		dmi_save_type(dm, DMI_CHASSIS_TYPE, 5);
2734f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_CHASSIS_VERSION, 6);
2744f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_CHASSIS_SERIAL, 7);
2754f5c791aSLennart Poettering 		dmi_save_ident(dm, DMI_CHASSIS_ASSET_TAG, 8);
2764f705ae3SBjorn Helgaas 		break;
2774f705ae3SBjorn Helgaas 	case 10:	/* Onboard Devices Information */
2784f705ae3SBjorn Helgaas 		dmi_save_devices(dm);
2794f705ae3SBjorn Helgaas 		break;
2802e0c1f6cSShem Multinymous 	case 11:	/* OEM Strings */
2812e0c1f6cSShem Multinymous 		dmi_save_oem_strings_devices(dm);
2822e0c1f6cSShem Multinymous 		break;
2834f705ae3SBjorn Helgaas 	case 38:	/* IPMI Device Information */
2844f705ae3SBjorn Helgaas 		dmi_save_ipmi_device(dm);
2854f705ae3SBjorn Helgaas 	}
2864f705ae3SBjorn Helgaas }
2874f705ae3SBjorn Helgaas 
2881855256cSJeff Garzik static int __init dmi_present(const char __iomem *p)
2894f705ae3SBjorn Helgaas {
2904f705ae3SBjorn Helgaas 	u8 buf[15];
2911855256cSJeff Garzik 
2924f705ae3SBjorn Helgaas 	memcpy_fromio(buf, p, 15);
2934f705ae3SBjorn Helgaas 	if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) {
2944f705ae3SBjorn Helgaas 		u16 num = (buf[13] << 8) | buf[12];
2954f705ae3SBjorn Helgaas 		u16 len = (buf[7] << 8) | buf[6];
2964f705ae3SBjorn Helgaas 		u32 base = (buf[11] << 24) | (buf[10] << 16) |
2974f705ae3SBjorn Helgaas 			(buf[9] << 8) | buf[8];
2984f705ae3SBjorn Helgaas 
2994f705ae3SBjorn Helgaas 		/*
3004f705ae3SBjorn Helgaas 		 * DMI version 0.0 means that the real version is taken from
3014f705ae3SBjorn Helgaas 		 * the SMBIOS version, which we don't know at this point.
3024f705ae3SBjorn Helgaas 		 */
3034f705ae3SBjorn Helgaas 		if (buf[14] != 0)
3044f705ae3SBjorn Helgaas 			printk(KERN_INFO "DMI %d.%d present.\n",
3054f705ae3SBjorn Helgaas 			       buf[14] >> 4, buf[14] & 0xF);
3064f705ae3SBjorn Helgaas 		else
3074f705ae3SBjorn Helgaas 			printk(KERN_INFO "DMI present.\n");
3084f705ae3SBjorn Helgaas 		if (dmi_table(base,len, num, dmi_decode) == 0)
3094f705ae3SBjorn Helgaas 			return 0;
3104f705ae3SBjorn Helgaas 	}
3114f705ae3SBjorn Helgaas 	return 1;
3124f705ae3SBjorn Helgaas }
3134f705ae3SBjorn Helgaas 
3144f705ae3SBjorn Helgaas void __init dmi_scan_machine(void)
3154f705ae3SBjorn Helgaas {
3164f705ae3SBjorn Helgaas 	char __iomem *p, *q;
3174f705ae3SBjorn Helgaas 	int rc;
3184f705ae3SBjorn Helgaas 
3194f705ae3SBjorn Helgaas 	if (efi_enabled) {
3204f705ae3SBjorn Helgaas 		if (efi.smbios == EFI_INVALID_TABLE_ADDR)
3214f705ae3SBjorn Helgaas 			goto out;
3224f705ae3SBjorn Helgaas 
3234f705ae3SBjorn Helgaas 		/* This is called as a core_initcall() because it isn't
3244f705ae3SBjorn Helgaas 		 * needed during early boot.  This also means we can
3254f705ae3SBjorn Helgaas 		 * iounmap the space when we're done with it.
3264f705ae3SBjorn Helgaas 		 */
3274f705ae3SBjorn Helgaas 		p = dmi_ioremap(efi.smbios, 32);
3284f705ae3SBjorn Helgaas 		if (p == NULL)
3294f705ae3SBjorn Helgaas 			goto out;
3304f705ae3SBjorn Helgaas 
3314f705ae3SBjorn Helgaas 		rc = dmi_present(p + 0x10); /* offset of _DMI_ string */
3324f705ae3SBjorn Helgaas 		dmi_iounmap(p, 32);
3334f5c791aSLennart Poettering 		if (!rc) {
3344f5c791aSLennart Poettering 			dmi_available = 1;
3354f705ae3SBjorn Helgaas 			return;
3364f705ae3SBjorn Helgaas 		}
3374f5c791aSLennart Poettering 	}
3384f705ae3SBjorn Helgaas 	else {
3394f705ae3SBjorn Helgaas 		/*
3404f705ae3SBjorn Helgaas 		 * no iounmap() for that ioremap(); it would be a no-op, but
3414f705ae3SBjorn Helgaas 		 * it's so early in setup that sucker gets confused into doing
3424f705ae3SBjorn Helgaas 		 * what it shouldn't if we actually call it.
3434f705ae3SBjorn Helgaas 		 */
3444f705ae3SBjorn Helgaas 		p = dmi_ioremap(0xF0000, 0x10000);
3454f705ae3SBjorn Helgaas 		if (p == NULL)
3464f705ae3SBjorn Helgaas 			goto out;
3474f705ae3SBjorn Helgaas 
3484f705ae3SBjorn Helgaas 		for (q = p; q < p + 0x10000; q += 16) {
3494f705ae3SBjorn Helgaas 			rc = dmi_present(q);
3504f5c791aSLennart Poettering 			if (!rc) {
3514f5c791aSLennart Poettering 				dmi_available = 1;
3524f705ae3SBjorn Helgaas 				return;
3534f705ae3SBjorn Helgaas 			}
3544f705ae3SBjorn Helgaas 		}
3554f5c791aSLennart Poettering 	}
3564f705ae3SBjorn Helgaas  out:	printk(KERN_INFO "DMI not present or invalid.\n");
3574f705ae3SBjorn Helgaas }
3584f705ae3SBjorn Helgaas 
3594f705ae3SBjorn Helgaas /**
3604f705ae3SBjorn Helgaas  *	dmi_check_system - check system DMI data
3614f705ae3SBjorn Helgaas  *	@list: array of dmi_system_id structures to match against
362b0ef371eSRandy Dunlap  *		All non-null elements of the list must match
363b0ef371eSRandy Dunlap  *		their slot's (field index's) data (i.e., each
364b0ef371eSRandy Dunlap  *		list string must be a substring of the specified
365b0ef371eSRandy Dunlap  *		DMI slot's string data) to be considered a
366b0ef371eSRandy Dunlap  *		successful match.
3674f705ae3SBjorn Helgaas  *
3684f705ae3SBjorn Helgaas  *	Walk the blacklist table running matching functions until someone
3694f705ae3SBjorn Helgaas  *	returns non zero or we hit the end. Callback function is called for
370b0ef371eSRandy Dunlap  *	each successful match. Returns the number of matches.
3714f705ae3SBjorn Helgaas  */
3721855256cSJeff Garzik int dmi_check_system(const struct dmi_system_id *list)
3734f705ae3SBjorn Helgaas {
3744f705ae3SBjorn Helgaas 	int i, count = 0;
3751855256cSJeff Garzik 	const struct dmi_system_id *d = list;
3764f705ae3SBjorn Helgaas 
3774f705ae3SBjorn Helgaas 	while (d->ident) {
3784f705ae3SBjorn Helgaas 		for (i = 0; i < ARRAY_SIZE(d->matches); i++) {
3794f705ae3SBjorn Helgaas 			int s = d->matches[i].slot;
3804f705ae3SBjorn Helgaas 			if (s == DMI_NONE)
3814f705ae3SBjorn Helgaas 				continue;
3824f705ae3SBjorn Helgaas 			if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr))
3834f705ae3SBjorn Helgaas 				continue;
3844f705ae3SBjorn Helgaas 			/* No match */
3854f705ae3SBjorn Helgaas 			goto fail;
3864f705ae3SBjorn Helgaas 		}
3874f705ae3SBjorn Helgaas 		count++;
3884f705ae3SBjorn Helgaas 		if (d->callback && d->callback(d))
3894f705ae3SBjorn Helgaas 			break;
3904f705ae3SBjorn Helgaas fail:		d++;
3914f705ae3SBjorn Helgaas 	}
3924f705ae3SBjorn Helgaas 
3934f705ae3SBjorn Helgaas 	return count;
3944f705ae3SBjorn Helgaas }
3954f705ae3SBjorn Helgaas EXPORT_SYMBOL(dmi_check_system);
3964f705ae3SBjorn Helgaas 
3974f705ae3SBjorn Helgaas /**
3984f705ae3SBjorn Helgaas  *	dmi_get_system_info - return DMI data value
399b0ef371eSRandy Dunlap  *	@field: data index (see enum dmi_field)
4004f705ae3SBjorn Helgaas  *
4014f705ae3SBjorn Helgaas  *	Returns one DMI data value, can be used to perform
4024f705ae3SBjorn Helgaas  *	complex DMI data checks.
4034f705ae3SBjorn Helgaas  */
4041855256cSJeff Garzik const char *dmi_get_system_info(int field)
4054f705ae3SBjorn Helgaas {
4064f705ae3SBjorn Helgaas 	return dmi_ident[field];
4074f705ae3SBjorn Helgaas }
4084f705ae3SBjorn Helgaas EXPORT_SYMBOL(dmi_get_system_info);
4094f705ae3SBjorn Helgaas 
410a1bae672SAndi Kleen 
411a1bae672SAndi Kleen /**
412a1bae672SAndi Kleen  *	dmi_name_in_vendors - Check if string is anywhere in the DMI vendor information.
413a1bae672SAndi Kleen  *	@str: 	Case sensitive Name
414a1bae672SAndi Kleen  */
4151855256cSJeff Garzik int dmi_name_in_vendors(const char *str)
416a1bae672SAndi Kleen {
417a1bae672SAndi Kleen 	static int fields[] = { DMI_BIOS_VENDOR, DMI_BIOS_VERSION, DMI_SYS_VENDOR,
418a1bae672SAndi Kleen 				DMI_PRODUCT_NAME, DMI_PRODUCT_VERSION, DMI_BOARD_VENDOR,
419a1bae672SAndi Kleen 				DMI_BOARD_NAME, DMI_BOARD_VERSION, DMI_NONE };
420a1bae672SAndi Kleen 	int i;
421a1bae672SAndi Kleen 	for (i = 0; fields[i] != DMI_NONE; i++) {
422a1bae672SAndi Kleen 		int f = fields[i];
423a1bae672SAndi Kleen 		if (dmi_ident[f] && strstr(dmi_ident[f], str))
424a1bae672SAndi Kleen 			return 1;
425a1bae672SAndi Kleen 	}
426a1bae672SAndi Kleen 	return 0;
427a1bae672SAndi Kleen }
428a1bae672SAndi Kleen EXPORT_SYMBOL(dmi_name_in_vendors);
429a1bae672SAndi Kleen 
4304f705ae3SBjorn Helgaas /**
4314f705ae3SBjorn Helgaas  *	dmi_find_device - find onboard device by type/name
4324f705ae3SBjorn Helgaas  *	@type: device type or %DMI_DEV_TYPE_ANY to match all device types
433b0ef371eSRandy Dunlap  *	@name: device name string or %NULL to match all
4344f705ae3SBjorn Helgaas  *	@from: previous device found in search, or %NULL for new search.
4354f705ae3SBjorn Helgaas  *
4364f705ae3SBjorn Helgaas  *	Iterates through the list of known onboard devices. If a device is
4374f705ae3SBjorn Helgaas  *	found with a matching @vendor and @device, a pointer to its device
4384f705ae3SBjorn Helgaas  *	structure is returned.  Otherwise, %NULL is returned.
439b0ef371eSRandy Dunlap  *	A new search is initiated by passing %NULL as the @from argument.
4404f705ae3SBjorn Helgaas  *	If @from is not %NULL, searches continue from next device.
4414f705ae3SBjorn Helgaas  */
4421855256cSJeff Garzik const struct dmi_device * dmi_find_device(int type, const char *name,
4431855256cSJeff Garzik 				    const struct dmi_device *from)
4444f705ae3SBjorn Helgaas {
4451855256cSJeff Garzik 	const struct list_head *head = from ? &from->list : &dmi_devices;
4461855256cSJeff Garzik 	struct list_head *d;
4474f705ae3SBjorn Helgaas 
4484f705ae3SBjorn Helgaas 	for(d = head->next; d != &dmi_devices; d = d->next) {
4491855256cSJeff Garzik 		const struct dmi_device *dev =
4501855256cSJeff Garzik 			list_entry(d, struct dmi_device, list);
4514f705ae3SBjorn Helgaas 
4524f705ae3SBjorn Helgaas 		if (((type == DMI_DEV_TYPE_ANY) || (dev->type == type)) &&
4534f705ae3SBjorn Helgaas 		    ((name == NULL) || (strcmp(dev->name, name) == 0)))
4544f705ae3SBjorn Helgaas 			return dev;
4554f705ae3SBjorn Helgaas 	}
4564f705ae3SBjorn Helgaas 
4574f705ae3SBjorn Helgaas 	return NULL;
4584f705ae3SBjorn Helgaas }
4594f705ae3SBjorn Helgaas EXPORT_SYMBOL(dmi_find_device);
4604f705ae3SBjorn Helgaas 
4614f705ae3SBjorn Helgaas /**
4624f705ae3SBjorn Helgaas  *	dmi_get_year - Return year of a DMI date
4634f705ae3SBjorn Helgaas  *	@field:	data index (like dmi_get_system_info)
4644f705ae3SBjorn Helgaas  *
4654f705ae3SBjorn Helgaas  *	Returns -1 when the field doesn't exist. 0 when it is broken.
4664f705ae3SBjorn Helgaas  */
4674f705ae3SBjorn Helgaas int dmi_get_year(int field)
4684f705ae3SBjorn Helgaas {
4694f705ae3SBjorn Helgaas 	int year;
4701855256cSJeff Garzik 	const char *s = dmi_get_system_info(field);
4714f705ae3SBjorn Helgaas 
4724f705ae3SBjorn Helgaas 	if (!s)
4734f705ae3SBjorn Helgaas 		return -1;
4744f705ae3SBjorn Helgaas 	if (*s == '\0')
4754f705ae3SBjorn Helgaas 		return 0;
4764f705ae3SBjorn Helgaas 	s = strrchr(s, '/');
4774f705ae3SBjorn Helgaas 	if (!s)
4784f705ae3SBjorn Helgaas 		return 0;
4794f705ae3SBjorn Helgaas 
4804f705ae3SBjorn Helgaas 	s += 1;
4814f705ae3SBjorn Helgaas 	year = simple_strtoul(s, NULL, 0);
4824f705ae3SBjorn Helgaas 	if (year && year < 100) {	/* 2-digit year */
4834f705ae3SBjorn Helgaas 		year += 1900;
4844f705ae3SBjorn Helgaas 		if (year < 1996)	/* no dates < spec 1.0 */
4854f705ae3SBjorn Helgaas 			year += 100;
4864f705ae3SBjorn Helgaas 	}
4874f705ae3SBjorn Helgaas 
4884f705ae3SBjorn Helgaas 	return year;
4894f705ae3SBjorn Helgaas }
4904f5c791aSLennart Poettering 
491f89e3b06SLen Brown /**
492f89e3b06SLen Brown  *	dmi_get_slot - return dmi_ident[slot]
493f89e3b06SLen Brown  *	@slot:	index into dmi_ident[]
494f89e3b06SLen Brown  */
495f89e3b06SLen Brown char *dmi_get_slot(int slot)
496f89e3b06SLen Brown {
497f89e3b06SLen Brown 	return(dmi_ident[slot]);
498f89e3b06SLen Brown }
499