1223 lines · 29.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "DMI: " fmt #include <linux/types.h>#include <linux/string.h>#include <linux/init.h>#include <linux/module.h>#include <linux/ctype.h>#include <linux/dmi.h>#include <linux/efi.h>#include <linux/memblock.h>#include <linux/random.h>#include <asm/dmi.h>#include <linux/unaligned.h> #ifndef SMBIOS_ENTRY_POINT_SCAN_START#define SMBIOS_ENTRY_POINT_SCAN_START 0xF0000#endif struct kobject *dmi_kobj;EXPORT_SYMBOL_GPL(dmi_kobj); /* * DMI stands for "Desktop Management Interface".  It is part * of and an antecedent to, SMBIOS, which stands for System * Management BIOS.  See further: https://www.dmtf.org/standards */static const char dmi_empty_string[] = ""; static u32 dmi_ver __initdata;static u32 dmi_len;static u16 dmi_num;static u8 smbios_entry_point[32];static int smbios_entry_point_size; /* DMI system identification string used during boot */static char dmi_ids_string[128] __initdata; static struct dmi_memdev_info {	const char *device;	const char *bank;	u64 size;		/* bytes */	u16 handle;	u8 type;		/* DDR2, DDR3, DDR4 etc */} *dmi_memdev;static int dmi_memdev_nr;static int dmi_memdev_populated_nr __initdata; const char *dmi_string_nosave(const struct dmi_header *dm, u8 s){	const u8 *bp = ((u8 *) dm) + dm->length;	const u8 *nsp; 	if (s) {		while (--s > 0 && *bp)			bp += strlen(bp) + 1; 		/* Strings containing only spaces are considered empty */		nsp = bp;		while (*nsp == ' ')			nsp++;		if (*nsp != '\0')			return bp;	} 	return dmi_empty_string;}EXPORT_SYMBOL_GPL(dmi_string_nosave); static const 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); 	return str;} /* *	We have to be cautious here. We have seen BIOSes with DMI pointers *	pointing to completely the wrong place for example */static void dmi_decode_table(u8 *buf,			     void (*decode)(const struct dmi_header *, void *),			     void *private_data){	u8 *data = buf;	int i = 0; 	/*	 * Stop when we have seen all the items the table claimed to have	 * (SMBIOS < 3.0 only) OR we reach an end-of-table marker (SMBIOS	 * >= 3.0 only) OR we run off the end of the table (should never	 * happen but sometimes does on bogus implementations.)	 */	while ((!dmi_num || i < dmi_num) &&	       (data - buf + sizeof(struct dmi_header)) <= dmi_len) {		const struct dmi_header *dm = (const struct dmi_header *)data; 		/*		 * If a short entry is found (less than 4 bytes), not only it		 * is invalid, but we cannot reliably locate the next entry.		 */		if (dm->length < sizeof(struct dmi_header)) {			pr_warn(FW_BUG				"Corrupted DMI table, offset %zd (only %d entries processed)\n",				data - buf, i);			break;		} 		/*		 *  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 < dmi_len - 1) && (data[0] || data[1]))			data++;		if (data - buf < dmi_len - 1)			decode(dm, private_data); 		data += 2;		i++; 		/*		 * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]		 * For tables behind a 64-bit entry point, we have no item		 * count and no exact table length, so stop on end-of-table		 * marker. For tables behind a 32-bit entry point, we have		 * seen OEM structures behind the end-of-table marker on		 * some systems, so don't trust it.		 */		if (!dmi_num && dm->type == DMI_ENTRY_END_OF_TABLE)			break;	} 	/* Trim DMI table length if needed */	if (dmi_len > data - buf)		dmi_len = data - buf;} static phys_addr_t dmi_base; static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,		void *)){	u8 *buf;	u32 orig_dmi_len = dmi_len; 	buf = dmi_early_remap(dmi_base, orig_dmi_len);	if (buf == NULL)		return -ENOMEM; 	dmi_decode_table(buf, decode, NULL); 	add_device_randomness(buf, dmi_len); 	dmi_early_unmap(buf, orig_dmi_len);	return 0;} static int __init dmi_checksum(const u8 *buf, u8 len){	u8 sum = 0;	int a; 	for (a = 0; a < len; a++)		sum += buf[a]; 	return sum == 0;} static const char *dmi_ident[DMI_STRING_MAX];static LIST_HEAD(dmi_devices);int dmi_available;EXPORT_SYMBOL_GPL(dmi_available); /* *	Save a DMI string */static void __init dmi_save_ident(const struct dmi_header *dm, int slot,		int string){	const char *d = (const char *) dm;	const char *p; 	if (dmi_ident[slot] || dm->length <= string)		return; 	p = dmi_string(dm, d[string]);	if (p == NULL)		return; 	dmi_ident[slot] = p;} static void __init dmi_save_release(const struct dmi_header *dm, int slot,		int index){	const u8 *minor, *major;	char *s; 	/* If the table doesn't have the field, let's return */	if (dmi_ident[slot] || dm->length < index)		return; 	minor = (u8 *) dm + index;	major = (u8 *) dm + index - 1; 	/* As per the spec, if the system doesn't support this field,	 * the value is FF	 */	if (*major == 0xFF && *minor == 0xFF)		return; 	s = dmi_alloc(8);	if (!s)		return; 	sprintf(s, "%u.%u", *major, *minor); 	dmi_ident[slot] = s;} static void __init dmi_save_uuid(const struct dmi_header *dm, int slot,		int index){	const u8 *d;	char *s;	int is_ff = 1, is_00 = 1, i; 	if (dmi_ident[slot] || dm->length < index + 16)		return; 	d = (u8 *) dm + index;	for (i = 0; i < 16 && (is_ff || is_00); i++) {		if (d[i] != 0x00)			is_00 = 0;		if (d[i] != 0xFF)			is_ff = 0;	} 	if (is_ff || is_00)		return; 	s = dmi_alloc(16*2+4+1);	if (!s)		return; 	/*	 * As of version 2.6 of the SMBIOS specification, the first 3 fields of	 * the UUID are supposed to be little-endian encoded.  The specification	 * says that this is the defacto standard.	 */	if (dmi_ver >= 0x020600)		sprintf(s, "%pUl", d);	else		sprintf(s, "%pUb", d); 	dmi_ident[slot] = s;} static void __init dmi_save_type(const struct dmi_header *dm, int slot,		int index){	const u8 *d;	char *s; 	if (dmi_ident[slot] || dm->length <= index)		return; 	s = dmi_alloc(4);	if (!s)		return; 	d = (u8 *) dm + index;	sprintf(s, "%u", *d & 0x7F);	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)		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; 	for (i = 0; i < count; i++) {		const char *d = (char *)(dm + 1) + (i * 2); 		/* Skip disabled device */		if ((*d & 0x80) == 0)			continue; 		dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d + 1)));	}} static void __init dmi_save_oem_strings_devices(const struct dmi_header *dm){	int i, count;	struct dmi_device *dev; 	if (dm->length < 0x05)		return; 	count = *(u8 *)(dm + 1);	for (i = 1; i <= count; i++) {		const char *devname = dmi_string(dm, i); 		if (devname == dmi_empty_string)			continue; 		dev = dmi_alloc(sizeof(*dev));		if (!dev)			break; 		dev->type = DMI_DEV_TYPE_OEM_STRING;		dev->name = devname;		dev->device_data = NULL; 		list_add(&dev->list, &dmi_devices);	}} static void __init dmi_save_ipmi_device(const struct dmi_header *dm){	struct dmi_device *dev;	void *data; 	data = dmi_alloc(dm->length);	if (data == NULL)		return; 	memcpy(data, dm, dm->length); 	dev = dmi_alloc(sizeof(*dev));	if (!dev)		return; 	dev->type = DMI_DEV_TYPE_IPMI;	dev->name = "IPMI controller";	dev->device_data = data; 	list_add_tail(&dev->list, &dmi_devices);} static void __init dmi_save_dev_pciaddr(int instance, int segment, int bus,					int devfn, const char *name, int type){	struct dmi_dev_onboard *dev; 	/* Ignore invalid values */	if (type == DMI_DEV_TYPE_DEV_SLOT &&	    segment == 0xFFFF && bus == 0xFF && devfn == 0xFF)		return; 	dev = dmi_alloc(sizeof(*dev) + strlen(name) + 1);	if (!dev)		return; 	dev->instance = instance;	dev->segment = segment;	dev->bus = bus;	dev->devfn = devfn; 	strcpy((char *)&dev[1], name);	dev->dev.type = type;	dev->dev.name = (char *)&dev[1];	dev->dev.device_data = dev; 	list_add(&dev->dev.list, &dmi_devices);} static void __init dmi_save_extended_devices(const struct dmi_header *dm){	const char *name;	const u8 *d = (u8 *)dm; 	if (dm->length < 0x0B)		return; 	/* Skip disabled device */	if ((d[0x5] & 0x80) == 0)		return; 	name = dmi_string_nosave(dm, d[0x4]);	dmi_save_dev_pciaddr(d[0x6], *(u16 *)(d + 0x7), d[0x9], d[0xA], name,			     DMI_DEV_TYPE_DEV_ONBOARD);	dmi_save_one_device(d[0x5] & 0x7f, name);} static void __init dmi_save_system_slot(const struct dmi_header *dm){	const u8 *d = (u8 *)dm; 	/* Need SMBIOS 2.6+ structure */	if (dm->length < 0x11)		return;	dmi_save_dev_pciaddr(*(u16 *)(d + 0x9), *(u16 *)(d + 0xD), d[0xF],			     d[0x10], dmi_string_nosave(dm, d[0x4]),			     DMI_DEV_TYPE_DEV_SLOT);} static void __init count_mem_devices(const struct dmi_header *dm, void *v){	if (dm->type != DMI_ENTRY_MEM_DEVICE)		return;	dmi_memdev_nr++;} static void __init save_mem_devices(const struct dmi_header *dm, void *v){	const char *d = (const char *)dm;	static int nr;	u64 bytes;	u16 size; 	if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x13)		return;	if (nr >= dmi_memdev_nr) {		pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n");		return;	}	dmi_memdev[nr].handle = get_unaligned(&dm->handle);	dmi_memdev[nr].device = dmi_string(dm, d[0x10]);	dmi_memdev[nr].bank = dmi_string(dm, d[0x11]);	dmi_memdev[nr].type = d[0x12]; 	size = get_unaligned((u16 *)&d[0xC]);	if (size == 0)		bytes = 0;	else if (size == 0xffff)		bytes = ~0ull;	else if (size & 0x8000)		bytes = (u64)(size & 0x7fff) << 10;	else if (size != 0x7fff || dm->length < 0x20)		bytes = (u64)size << 20;	else		bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20; 	if (bytes)		dmi_memdev_populated_nr++; 	dmi_memdev[nr].size = bytes;	nr++;} static void __init dmi_memdev_walk(void){	if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) {		dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr);		if (dmi_memdev)			dmi_walk_early(save_mem_devices);	}} /* *	Process a DMI table entry. Right now all we care about are the BIOS *	and machine entries. For 2.5 we should pull the smbus controller info *	out of here. */static void __init dmi_decode(const struct dmi_header *dm, void *dummy){	switch (dm->type) {	case DMI_ENTRY_BIOS:		dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);		dmi_save_ident(dm, DMI_BIOS_VERSION, 5);		dmi_save_ident(dm, DMI_BIOS_DATE, 8);		dmi_save_release(dm, DMI_BIOS_RELEASE, 21);		dmi_save_release(dm, DMI_EC_FIRMWARE_RELEASE, 23);		break;	case DMI_ENTRY_SYSTEM:		dmi_save_ident(dm, DMI_SYS_VENDOR, 4);		dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);

Lines 1–500 of 1223.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t