162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/types.h>
362306a36Sopenharmony_ci#include <linux/string.h>
462306a36Sopenharmony_ci#include <linux/init.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/ctype.h>
762306a36Sopenharmony_ci#include <linux/dmi.h>
862306a36Sopenharmony_ci#include <linux/efi.h>
962306a36Sopenharmony_ci#include <linux/memblock.h>
1062306a36Sopenharmony_ci#include <linux/random.h>
1162306a36Sopenharmony_ci#include <asm/dmi.h>
1262306a36Sopenharmony_ci#include <asm/unaligned.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#ifndef SMBIOS_ENTRY_POINT_SCAN_START
1562306a36Sopenharmony_ci#define SMBIOS_ENTRY_POINT_SCAN_START 0xF0000
1662306a36Sopenharmony_ci#endif
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct kobject *dmi_kobj;
1962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_kobj);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * DMI stands for "Desktop Management Interface".  It is part
2362306a36Sopenharmony_ci * of and an antecedent to, SMBIOS, which stands for System
2462306a36Sopenharmony_ci * Management BIOS.  See further: https://www.dmtf.org/standards
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic const char dmi_empty_string[] = "";
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic u32 dmi_ver __initdata;
2962306a36Sopenharmony_cistatic u32 dmi_len;
3062306a36Sopenharmony_cistatic u16 dmi_num;
3162306a36Sopenharmony_cistatic u8 smbios_entry_point[32];
3262306a36Sopenharmony_cistatic int smbios_entry_point_size;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* DMI system identification string used during boot */
3562306a36Sopenharmony_cistatic char dmi_ids_string[128] __initdata;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct dmi_memdev_info {
3862306a36Sopenharmony_ci	const char *device;
3962306a36Sopenharmony_ci	const char *bank;
4062306a36Sopenharmony_ci	u64 size;		/* bytes */
4162306a36Sopenharmony_ci	u16 handle;
4262306a36Sopenharmony_ci	u8 type;		/* DDR2, DDR3, DDR4 etc */
4362306a36Sopenharmony_ci} *dmi_memdev;
4462306a36Sopenharmony_cistatic int dmi_memdev_nr;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	const u8 *bp = ((u8 *) dm) + dm->length;
4962306a36Sopenharmony_ci	const u8 *nsp;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (s) {
5262306a36Sopenharmony_ci		while (--s > 0 && *bp)
5362306a36Sopenharmony_ci			bp += strlen(bp) + 1;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		/* Strings containing only spaces are considered empty */
5662306a36Sopenharmony_ci		nsp = bp;
5762306a36Sopenharmony_ci		while (*nsp == ' ')
5862306a36Sopenharmony_ci			nsp++;
5962306a36Sopenharmony_ci		if (*nsp != '\0')
6062306a36Sopenharmony_ci			return bp;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return dmi_empty_string;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic const char * __init dmi_string(const struct dmi_header *dm, u8 s)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	const char *bp = dmi_string_nosave(dm, s);
6962306a36Sopenharmony_ci	char *str;
7062306a36Sopenharmony_ci	size_t len;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (bp == dmi_empty_string)
7362306a36Sopenharmony_ci		return dmi_empty_string;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	len = strlen(bp) + 1;
7662306a36Sopenharmony_ci	str = dmi_alloc(len);
7762306a36Sopenharmony_ci	if (str != NULL)
7862306a36Sopenharmony_ci		strcpy(str, bp);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return str;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci *	We have to be cautious here. We have seen BIOSes with DMI pointers
8562306a36Sopenharmony_ci *	pointing to completely the wrong place for example
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic void dmi_decode_table(u8 *buf,
8862306a36Sopenharmony_ci			     void (*decode)(const struct dmi_header *, void *),
8962306a36Sopenharmony_ci			     void *private_data)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	u8 *data = buf;
9262306a36Sopenharmony_ci	int i = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/*
9562306a36Sopenharmony_ci	 * Stop when we have seen all the items the table claimed to have
9662306a36Sopenharmony_ci	 * (SMBIOS < 3.0 only) OR we reach an end-of-table marker (SMBIOS
9762306a36Sopenharmony_ci	 * >= 3.0 only) OR we run off the end of the table (should never
9862306a36Sopenharmony_ci	 * happen but sometimes does on bogus implementations.)
9962306a36Sopenharmony_ci	 */
10062306a36Sopenharmony_ci	while ((!dmi_num || i < dmi_num) &&
10162306a36Sopenharmony_ci	       (data - buf + sizeof(struct dmi_header)) <= dmi_len) {
10262306a36Sopenharmony_ci		const struct dmi_header *dm = (const struct dmi_header *)data;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		/*
10562306a36Sopenharmony_ci		 *  We want to know the total length (formatted area and
10662306a36Sopenharmony_ci		 *  strings) before decoding to make sure we won't run off the
10762306a36Sopenharmony_ci		 *  table in dmi_decode or dmi_string
10862306a36Sopenharmony_ci		 */
10962306a36Sopenharmony_ci		data += dm->length;
11062306a36Sopenharmony_ci		while ((data - buf < dmi_len - 1) && (data[0] || data[1]))
11162306a36Sopenharmony_ci			data++;
11262306a36Sopenharmony_ci		if (data - buf < dmi_len - 1)
11362306a36Sopenharmony_ci			decode(dm, private_data);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		data += 2;
11662306a36Sopenharmony_ci		i++;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		/*
11962306a36Sopenharmony_ci		 * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
12062306a36Sopenharmony_ci		 * For tables behind a 64-bit entry point, we have no item
12162306a36Sopenharmony_ci		 * count and no exact table length, so stop on end-of-table
12262306a36Sopenharmony_ci		 * marker. For tables behind a 32-bit entry point, we have
12362306a36Sopenharmony_ci		 * seen OEM structures behind the end-of-table marker on
12462306a36Sopenharmony_ci		 * some systems, so don't trust it.
12562306a36Sopenharmony_ci		 */
12662306a36Sopenharmony_ci		if (!dmi_num && dm->type == DMI_ENTRY_END_OF_TABLE)
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Trim DMI table length if needed */
13162306a36Sopenharmony_ci	if (dmi_len > data - buf)
13262306a36Sopenharmony_ci		dmi_len = data - buf;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic phys_addr_t dmi_base;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
13862306a36Sopenharmony_ci		void *))
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	u8 *buf;
14162306a36Sopenharmony_ci	u32 orig_dmi_len = dmi_len;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	buf = dmi_early_remap(dmi_base, orig_dmi_len);
14462306a36Sopenharmony_ci	if (buf == NULL)
14562306a36Sopenharmony_ci		return -ENOMEM;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	dmi_decode_table(buf, decode, NULL);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	add_device_randomness(buf, dmi_len);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dmi_early_unmap(buf, orig_dmi_len);
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int __init dmi_checksum(const u8 *buf, u8 len)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	u8 sum = 0;
15862306a36Sopenharmony_ci	int a;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	for (a = 0; a < len; a++)
16162306a36Sopenharmony_ci		sum += buf[a];
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return sum == 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic const char *dmi_ident[DMI_STRING_MAX];
16762306a36Sopenharmony_cistatic LIST_HEAD(dmi_devices);
16862306a36Sopenharmony_ciint dmi_available;
16962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_available);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/*
17262306a36Sopenharmony_ci *	Save a DMI string
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_cistatic void __init dmi_save_ident(const struct dmi_header *dm, int slot,
17562306a36Sopenharmony_ci		int string)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	const char *d = (const char *) dm;
17862306a36Sopenharmony_ci	const char *p;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (dmi_ident[slot] || dm->length <= string)
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	p = dmi_string(dm, d[string]);
18462306a36Sopenharmony_ci	if (p == NULL)
18562306a36Sopenharmony_ci		return;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	dmi_ident[slot] = p;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void __init dmi_save_release(const struct dmi_header *dm, int slot,
19162306a36Sopenharmony_ci		int index)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	const u8 *minor, *major;
19462306a36Sopenharmony_ci	char *s;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* If the table doesn't have the field, let's return */
19762306a36Sopenharmony_ci	if (dmi_ident[slot] || dm->length < index)
19862306a36Sopenharmony_ci		return;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	minor = (u8 *) dm + index;
20162306a36Sopenharmony_ci	major = (u8 *) dm + index - 1;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* As per the spec, if the system doesn't support this field,
20462306a36Sopenharmony_ci	 * the value is FF
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci	if (*major == 0xFF && *minor == 0xFF)
20762306a36Sopenharmony_ci		return;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	s = dmi_alloc(8);
21062306a36Sopenharmony_ci	if (!s)
21162306a36Sopenharmony_ci		return;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	sprintf(s, "%u.%u", *major, *minor);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	dmi_ident[slot] = s;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void __init dmi_save_uuid(const struct dmi_header *dm, int slot,
21962306a36Sopenharmony_ci		int index)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	const u8 *d;
22262306a36Sopenharmony_ci	char *s;
22362306a36Sopenharmony_ci	int is_ff = 1, is_00 = 1, i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (dmi_ident[slot] || dm->length < index + 16)
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	d = (u8 *) dm + index;
22962306a36Sopenharmony_ci	for (i = 0; i < 16 && (is_ff || is_00); i++) {
23062306a36Sopenharmony_ci		if (d[i] != 0x00)
23162306a36Sopenharmony_ci			is_00 = 0;
23262306a36Sopenharmony_ci		if (d[i] != 0xFF)
23362306a36Sopenharmony_ci			is_ff = 0;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (is_ff || is_00)
23762306a36Sopenharmony_ci		return;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	s = dmi_alloc(16*2+4+1);
24062306a36Sopenharmony_ci	if (!s)
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/*
24462306a36Sopenharmony_ci	 * As of version 2.6 of the SMBIOS specification, the first 3 fields of
24562306a36Sopenharmony_ci	 * the UUID are supposed to be little-endian encoded.  The specification
24662306a36Sopenharmony_ci	 * says that this is the defacto standard.
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	if (dmi_ver >= 0x020600)
24962306a36Sopenharmony_ci		sprintf(s, "%pUl", d);
25062306a36Sopenharmony_ci	else
25162306a36Sopenharmony_ci		sprintf(s, "%pUb", d);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	dmi_ident[slot] = s;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void __init dmi_save_type(const struct dmi_header *dm, int slot,
25762306a36Sopenharmony_ci		int index)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	const u8 *d;
26062306a36Sopenharmony_ci	char *s;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (dmi_ident[slot] || dm->length <= index)
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	s = dmi_alloc(4);
26662306a36Sopenharmony_ci	if (!s)
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	d = (u8 *) dm + index;
27062306a36Sopenharmony_ci	sprintf(s, "%u", *d & 0x7F);
27162306a36Sopenharmony_ci	dmi_ident[slot] = s;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void __init dmi_save_one_device(int type, const char *name)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct dmi_device *dev;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* No duplicate device */
27962306a36Sopenharmony_ci	if (dmi_find_device(type, name, NULL))
28062306a36Sopenharmony_ci		return;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev = dmi_alloc(sizeof(*dev) + strlen(name) + 1);
28362306a36Sopenharmony_ci	if (!dev)
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	dev->type = type;
28762306a36Sopenharmony_ci	strcpy((char *)(dev + 1), name);
28862306a36Sopenharmony_ci	dev->name = (char *)(dev + 1);
28962306a36Sopenharmony_ci	dev->device_data = NULL;
29062306a36Sopenharmony_ci	list_add(&dev->list, &dmi_devices);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void __init dmi_save_devices(const struct dmi_header *dm)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int i, count = (dm->length - sizeof(struct dmi_header)) / 2;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
29862306a36Sopenharmony_ci		const char *d = (char *)(dm + 1) + (i * 2);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		/* Skip disabled device */
30162306a36Sopenharmony_ci		if ((*d & 0x80) == 0)
30262306a36Sopenharmony_ci			continue;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d + 1)));
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void __init dmi_save_oem_strings_devices(const struct dmi_header *dm)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int i, count;
31162306a36Sopenharmony_ci	struct dmi_device *dev;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (dm->length < 0x05)
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	count = *(u8 *)(dm + 1);
31762306a36Sopenharmony_ci	for (i = 1; i <= count; i++) {
31862306a36Sopenharmony_ci		const char *devname = dmi_string(dm, i);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		if (devname == dmi_empty_string)
32162306a36Sopenharmony_ci			continue;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		dev = dmi_alloc(sizeof(*dev));
32462306a36Sopenharmony_ci		if (!dev)
32562306a36Sopenharmony_ci			break;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		dev->type = DMI_DEV_TYPE_OEM_STRING;
32862306a36Sopenharmony_ci		dev->name = devname;
32962306a36Sopenharmony_ci		dev->device_data = NULL;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		list_add(&dev->list, &dmi_devices);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void __init dmi_save_ipmi_device(const struct dmi_header *dm)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct dmi_device *dev;
33862306a36Sopenharmony_ci	void *data;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	data = dmi_alloc(dm->length);
34162306a36Sopenharmony_ci	if (data == NULL)
34262306a36Sopenharmony_ci		return;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	memcpy(data, dm, dm->length);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	dev = dmi_alloc(sizeof(*dev));
34762306a36Sopenharmony_ci	if (!dev)
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	dev->type = DMI_DEV_TYPE_IPMI;
35162306a36Sopenharmony_ci	dev->name = "IPMI controller";
35262306a36Sopenharmony_ci	dev->device_data = data;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	list_add_tail(&dev->list, &dmi_devices);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void __init dmi_save_dev_pciaddr(int instance, int segment, int bus,
35862306a36Sopenharmony_ci					int devfn, const char *name, int type)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct dmi_dev_onboard *dev;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* Ignore invalid values */
36362306a36Sopenharmony_ci	if (type == DMI_DEV_TYPE_DEV_SLOT &&
36462306a36Sopenharmony_ci	    segment == 0xFFFF && bus == 0xFF && devfn == 0xFF)
36562306a36Sopenharmony_ci		return;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	dev = dmi_alloc(sizeof(*dev) + strlen(name) + 1);
36862306a36Sopenharmony_ci	if (!dev)
36962306a36Sopenharmony_ci		return;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	dev->instance = instance;
37262306a36Sopenharmony_ci	dev->segment = segment;
37362306a36Sopenharmony_ci	dev->bus = bus;
37462306a36Sopenharmony_ci	dev->devfn = devfn;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	strcpy((char *)&dev[1], name);
37762306a36Sopenharmony_ci	dev->dev.type = type;
37862306a36Sopenharmony_ci	dev->dev.name = (char *)&dev[1];
37962306a36Sopenharmony_ci	dev->dev.device_data = dev;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	list_add(&dev->dev.list, &dmi_devices);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic void __init dmi_save_extended_devices(const struct dmi_header *dm)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	const char *name;
38762306a36Sopenharmony_ci	const u8 *d = (u8 *)dm;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (dm->length < 0x0B)
39062306a36Sopenharmony_ci		return;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Skip disabled device */
39362306a36Sopenharmony_ci	if ((d[0x5] & 0x80) == 0)
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	name = dmi_string_nosave(dm, d[0x4]);
39762306a36Sopenharmony_ci	dmi_save_dev_pciaddr(d[0x6], *(u16 *)(d + 0x7), d[0x9], d[0xA], name,
39862306a36Sopenharmony_ci			     DMI_DEV_TYPE_DEV_ONBOARD);
39962306a36Sopenharmony_ci	dmi_save_one_device(d[0x5] & 0x7f, name);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void __init dmi_save_system_slot(const struct dmi_header *dm)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	const u8 *d = (u8 *)dm;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Need SMBIOS 2.6+ structure */
40762306a36Sopenharmony_ci	if (dm->length < 0x11)
40862306a36Sopenharmony_ci		return;
40962306a36Sopenharmony_ci	dmi_save_dev_pciaddr(*(u16 *)(d + 0x9), *(u16 *)(d + 0xD), d[0xF],
41062306a36Sopenharmony_ci			     d[0x10], dmi_string_nosave(dm, d[0x4]),
41162306a36Sopenharmony_ci			     DMI_DEV_TYPE_DEV_SLOT);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void __init count_mem_devices(const struct dmi_header *dm, void *v)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	if (dm->type != DMI_ENTRY_MEM_DEVICE)
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci	dmi_memdev_nr++;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void __init save_mem_devices(const struct dmi_header *dm, void *v)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	const char *d = (const char *)dm;
42462306a36Sopenharmony_ci	static int nr;
42562306a36Sopenharmony_ci	u64 bytes;
42662306a36Sopenharmony_ci	u16 size;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x13)
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci	if (nr >= dmi_memdev_nr) {
43162306a36Sopenharmony_ci		pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n");
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	dmi_memdev[nr].handle = get_unaligned(&dm->handle);
43562306a36Sopenharmony_ci	dmi_memdev[nr].device = dmi_string(dm, d[0x10]);
43662306a36Sopenharmony_ci	dmi_memdev[nr].bank = dmi_string(dm, d[0x11]);
43762306a36Sopenharmony_ci	dmi_memdev[nr].type = d[0x12];
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	size = get_unaligned((u16 *)&d[0xC]);
44062306a36Sopenharmony_ci	if (size == 0)
44162306a36Sopenharmony_ci		bytes = 0;
44262306a36Sopenharmony_ci	else if (size == 0xffff)
44362306a36Sopenharmony_ci		bytes = ~0ull;
44462306a36Sopenharmony_ci	else if (size & 0x8000)
44562306a36Sopenharmony_ci		bytes = (u64)(size & 0x7fff) << 10;
44662306a36Sopenharmony_ci	else if (size != 0x7fff || dm->length < 0x20)
44762306a36Sopenharmony_ci		bytes = (u64)size << 20;
44862306a36Sopenharmony_ci	else
44962306a36Sopenharmony_ci		bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	dmi_memdev[nr].size = bytes;
45262306a36Sopenharmony_ci	nr++;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void __init dmi_memdev_walk(void)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) {
45862306a36Sopenharmony_ci		dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr);
45962306a36Sopenharmony_ci		if (dmi_memdev)
46062306a36Sopenharmony_ci			dmi_walk_early(save_mem_devices);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/*
46562306a36Sopenharmony_ci *	Process a DMI table entry. Right now all we care about are the BIOS
46662306a36Sopenharmony_ci *	and machine entries. For 2.5 we should pull the smbus controller info
46762306a36Sopenharmony_ci *	out of here.
46862306a36Sopenharmony_ci */
46962306a36Sopenharmony_cistatic void __init dmi_decode(const struct dmi_header *dm, void *dummy)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	switch (dm->type) {
47262306a36Sopenharmony_ci	case 0:		/* BIOS Information */
47362306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
47462306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
47562306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BIOS_DATE, 8);
47662306a36Sopenharmony_ci		dmi_save_release(dm, DMI_BIOS_RELEASE, 21);
47762306a36Sopenharmony_ci		dmi_save_release(dm, DMI_EC_FIRMWARE_RELEASE, 23);
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	case 1:		/* System Information */
48062306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
48162306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
48262306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
48362306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
48462306a36Sopenharmony_ci		dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8);
48562306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_PRODUCT_SKU, 25);
48662306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_PRODUCT_FAMILY, 26);
48762306a36Sopenharmony_ci		break;
48862306a36Sopenharmony_ci	case 2:		/* Base Board Information */
48962306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
49062306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BOARD_NAME, 5);
49162306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
49262306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BOARD_SERIAL, 7);
49362306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_BOARD_ASSET_TAG, 8);
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci	case 3:		/* Chassis Information */
49662306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_CHASSIS_VENDOR, 4);
49762306a36Sopenharmony_ci		dmi_save_type(dm, DMI_CHASSIS_TYPE, 5);
49862306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_CHASSIS_VERSION, 6);
49962306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_CHASSIS_SERIAL, 7);
50062306a36Sopenharmony_ci		dmi_save_ident(dm, DMI_CHASSIS_ASSET_TAG, 8);
50162306a36Sopenharmony_ci		break;
50262306a36Sopenharmony_ci	case 9:		/* System Slots */
50362306a36Sopenharmony_ci		dmi_save_system_slot(dm);
50462306a36Sopenharmony_ci		break;
50562306a36Sopenharmony_ci	case 10:	/* Onboard Devices Information */
50662306a36Sopenharmony_ci		dmi_save_devices(dm);
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	case 11:	/* OEM Strings */
50962306a36Sopenharmony_ci		dmi_save_oem_strings_devices(dm);
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	case 38:	/* IPMI Device Information */
51262306a36Sopenharmony_ci		dmi_save_ipmi_device(dm);
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci	case 41:	/* Onboard Devices Extended Information */
51562306a36Sopenharmony_ci		dmi_save_extended_devices(dm);
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int __init print_filtered(char *buf, size_t len, const char *info)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	int c = 0;
52262306a36Sopenharmony_ci	const char *p;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (!info)
52562306a36Sopenharmony_ci		return c;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	for (p = info; *p; p++)
52862306a36Sopenharmony_ci		if (isprint(*p))
52962306a36Sopenharmony_ci			c += scnprintf(buf + c, len - c, "%c", *p);
53062306a36Sopenharmony_ci		else
53162306a36Sopenharmony_ci			c += scnprintf(buf + c, len - c, "\\x%02x", *p & 0xff);
53262306a36Sopenharmony_ci	return c;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void __init dmi_format_ids(char *buf, size_t len)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int c = 0;
53862306a36Sopenharmony_ci	const char *board;	/* Board Name is optional */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	c += print_filtered(buf + c, len - c,
54162306a36Sopenharmony_ci			    dmi_get_system_info(DMI_SYS_VENDOR));
54262306a36Sopenharmony_ci	c += scnprintf(buf + c, len - c, " ");
54362306a36Sopenharmony_ci	c += print_filtered(buf + c, len - c,
54462306a36Sopenharmony_ci			    dmi_get_system_info(DMI_PRODUCT_NAME));
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	board = dmi_get_system_info(DMI_BOARD_NAME);
54762306a36Sopenharmony_ci	if (board) {
54862306a36Sopenharmony_ci		c += scnprintf(buf + c, len - c, "/");
54962306a36Sopenharmony_ci		c += print_filtered(buf + c, len - c, board);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci	c += scnprintf(buf + c, len - c, ", BIOS ");
55262306a36Sopenharmony_ci	c += print_filtered(buf + c, len - c,
55362306a36Sopenharmony_ci			    dmi_get_system_info(DMI_BIOS_VERSION));
55462306a36Sopenharmony_ci	c += scnprintf(buf + c, len - c, " ");
55562306a36Sopenharmony_ci	c += print_filtered(buf + c, len - c,
55662306a36Sopenharmony_ci			    dmi_get_system_info(DMI_BIOS_DATE));
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/*
56062306a36Sopenharmony_ci * Check for DMI/SMBIOS headers in the system firmware image.  Any
56162306a36Sopenharmony_ci * SMBIOS header must start 16 bytes before the DMI header, so take a
56262306a36Sopenharmony_ci * 32 byte buffer and check for DMI at offset 16 and SMBIOS at offset
56362306a36Sopenharmony_ci * 0.  If the DMI header is present, set dmi_ver accordingly (SMBIOS
56462306a36Sopenharmony_ci * takes precedence) and return 0.  Otherwise return 1.
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_cistatic int __init dmi_present(const u8 *buf)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	u32 smbios_ver;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/*
57162306a36Sopenharmony_ci	 * The size of this structure is 31 bytes, but we also accept value
57262306a36Sopenharmony_ci	 * 30 due to a mistake in SMBIOS specification version 2.1.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	if (memcmp(buf, "_SM_", 4) == 0 &&
57562306a36Sopenharmony_ci	    buf[5] >= 30 && buf[5] <= 32 &&
57662306a36Sopenharmony_ci	    dmi_checksum(buf, buf[5])) {
57762306a36Sopenharmony_ci		smbios_ver = get_unaligned_be16(buf + 6);
57862306a36Sopenharmony_ci		smbios_entry_point_size = buf[5];
57962306a36Sopenharmony_ci		memcpy(smbios_entry_point, buf, smbios_entry_point_size);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* Some BIOS report weird SMBIOS version, fix that up */
58262306a36Sopenharmony_ci		switch (smbios_ver) {
58362306a36Sopenharmony_ci		case 0x021F:
58462306a36Sopenharmony_ci		case 0x0221:
58562306a36Sopenharmony_ci			pr_debug("SMBIOS version fixup (2.%d->2.%d)\n",
58662306a36Sopenharmony_ci				 smbios_ver & 0xFF, 3);
58762306a36Sopenharmony_ci			smbios_ver = 0x0203;
58862306a36Sopenharmony_ci			break;
58962306a36Sopenharmony_ci		case 0x0233:
59062306a36Sopenharmony_ci			pr_debug("SMBIOS version fixup (2.%d->2.%d)\n", 51, 6);
59162306a36Sopenharmony_ci			smbios_ver = 0x0206;
59262306a36Sopenharmony_ci			break;
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci	} else {
59562306a36Sopenharmony_ci		smbios_ver = 0;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	buf += 16;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) {
60162306a36Sopenharmony_ci		if (smbios_ver)
60262306a36Sopenharmony_ci			dmi_ver = smbios_ver;
60362306a36Sopenharmony_ci		else
60462306a36Sopenharmony_ci			dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F);
60562306a36Sopenharmony_ci		dmi_ver <<= 8;
60662306a36Sopenharmony_ci		dmi_num = get_unaligned_le16(buf + 12);
60762306a36Sopenharmony_ci		dmi_len = get_unaligned_le16(buf + 6);
60862306a36Sopenharmony_ci		dmi_base = get_unaligned_le32(buf + 8);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (dmi_walk_early(dmi_decode) == 0) {
61162306a36Sopenharmony_ci			if (smbios_ver) {
61262306a36Sopenharmony_ci				pr_info("SMBIOS %d.%d present.\n",
61362306a36Sopenharmony_ci					dmi_ver >> 16, (dmi_ver >> 8) & 0xFF);
61462306a36Sopenharmony_ci			} else {
61562306a36Sopenharmony_ci				smbios_entry_point_size = 15;
61662306a36Sopenharmony_ci				memcpy(smbios_entry_point, buf,
61762306a36Sopenharmony_ci				       smbios_entry_point_size);
61862306a36Sopenharmony_ci				pr_info("Legacy DMI %d.%d present.\n",
61962306a36Sopenharmony_ci					dmi_ver >> 16, (dmi_ver >> 8) & 0xFF);
62062306a36Sopenharmony_ci			}
62162306a36Sopenharmony_ci			dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
62262306a36Sopenharmony_ci			pr_info("DMI: %s\n", dmi_ids_string);
62362306a36Sopenharmony_ci			return 0;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return 1;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci/*
63162306a36Sopenharmony_ci * Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy
63262306a36Sopenharmony_ci * 32-bit entry point, there is no embedded DMI header (_DMI_) in here.
63362306a36Sopenharmony_ci */
63462306a36Sopenharmony_cistatic int __init dmi_smbios3_present(const u8 *buf)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	if (memcmp(buf, "_SM3_", 5) == 0 &&
63762306a36Sopenharmony_ci	    buf[6] >= 24 && buf[6] <= 32 &&
63862306a36Sopenharmony_ci	    dmi_checksum(buf, buf[6])) {
63962306a36Sopenharmony_ci		dmi_ver = get_unaligned_be24(buf + 7);
64062306a36Sopenharmony_ci		dmi_num = 0;			/* No longer specified */
64162306a36Sopenharmony_ci		dmi_len = get_unaligned_le32(buf + 12);
64262306a36Sopenharmony_ci		dmi_base = get_unaligned_le64(buf + 16);
64362306a36Sopenharmony_ci		smbios_entry_point_size = buf[6];
64462306a36Sopenharmony_ci		memcpy(smbios_entry_point, buf, smbios_entry_point_size);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		if (dmi_walk_early(dmi_decode) == 0) {
64762306a36Sopenharmony_ci			pr_info("SMBIOS %d.%d.%d present.\n",
64862306a36Sopenharmony_ci				dmi_ver >> 16, (dmi_ver >> 8) & 0xFF,
64962306a36Sopenharmony_ci				dmi_ver & 0xFF);
65062306a36Sopenharmony_ci			dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
65162306a36Sopenharmony_ci			pr_info("DMI: %s\n", dmi_ids_string);
65262306a36Sopenharmony_ci			return 0;
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	return 1;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic void __init dmi_scan_machine(void)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	char __iomem *p, *q;
66162306a36Sopenharmony_ci	char buf[32];
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (efi_enabled(EFI_CONFIG_TABLES)) {
66462306a36Sopenharmony_ci		/*
66562306a36Sopenharmony_ci		 * According to the DMTF SMBIOS reference spec v3.0.0, it is
66662306a36Sopenharmony_ci		 * allowed to define both the 64-bit entry point (smbios3) and
66762306a36Sopenharmony_ci		 * the 32-bit entry point (smbios), in which case they should
66862306a36Sopenharmony_ci		 * either both point to the same SMBIOS structure table, or the
66962306a36Sopenharmony_ci		 * table pointed to by the 64-bit entry point should contain a
67062306a36Sopenharmony_ci		 * superset of the table contents pointed to by the 32-bit entry
67162306a36Sopenharmony_ci		 * point (section 5.2)
67262306a36Sopenharmony_ci		 * This implies that the 64-bit entry point should have
67362306a36Sopenharmony_ci		 * precedence if it is defined and supported by the OS. If we
67462306a36Sopenharmony_ci		 * have the 64-bit entry point, but fail to decode it, fall
67562306a36Sopenharmony_ci		 * back to the legacy one (if available)
67662306a36Sopenharmony_ci		 */
67762306a36Sopenharmony_ci		if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) {
67862306a36Sopenharmony_ci			p = dmi_early_remap(efi.smbios3, 32);
67962306a36Sopenharmony_ci			if (p == NULL)
68062306a36Sopenharmony_ci				goto error;
68162306a36Sopenharmony_ci			memcpy_fromio(buf, p, 32);
68262306a36Sopenharmony_ci			dmi_early_unmap(p, 32);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci			if (!dmi_smbios3_present(buf)) {
68562306a36Sopenharmony_ci				dmi_available = 1;
68662306a36Sopenharmony_ci				return;
68762306a36Sopenharmony_ci			}
68862306a36Sopenharmony_ci		}
68962306a36Sopenharmony_ci		if (efi.smbios == EFI_INVALID_TABLE_ADDR)
69062306a36Sopenharmony_ci			goto error;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		/* This is called as a core_initcall() because it isn't
69362306a36Sopenharmony_ci		 * needed during early boot.  This also means we can
69462306a36Sopenharmony_ci		 * iounmap the space when we're done with it.
69562306a36Sopenharmony_ci		 */
69662306a36Sopenharmony_ci		p = dmi_early_remap(efi.smbios, 32);
69762306a36Sopenharmony_ci		if (p == NULL)
69862306a36Sopenharmony_ci			goto error;
69962306a36Sopenharmony_ci		memcpy_fromio(buf, p, 32);
70062306a36Sopenharmony_ci		dmi_early_unmap(p, 32);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (!dmi_present(buf)) {
70362306a36Sopenharmony_ci			dmi_available = 1;
70462306a36Sopenharmony_ci			return;
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci	} else if (IS_ENABLED(CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK)) {
70762306a36Sopenharmony_ci		p = dmi_early_remap(SMBIOS_ENTRY_POINT_SCAN_START, 0x10000);
70862306a36Sopenharmony_ci		if (p == NULL)
70962306a36Sopenharmony_ci			goto error;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		/*
71262306a36Sopenharmony_ci		 * Same logic as above, look for a 64-bit entry point
71362306a36Sopenharmony_ci		 * first, and if not found, fall back to 32-bit entry point.
71462306a36Sopenharmony_ci		 */
71562306a36Sopenharmony_ci		memcpy_fromio(buf, p, 16);
71662306a36Sopenharmony_ci		for (q = p + 16; q < p + 0x10000; q += 16) {
71762306a36Sopenharmony_ci			memcpy_fromio(buf + 16, q, 16);
71862306a36Sopenharmony_ci			if (!dmi_smbios3_present(buf)) {
71962306a36Sopenharmony_ci				dmi_available = 1;
72062306a36Sopenharmony_ci				dmi_early_unmap(p, 0x10000);
72162306a36Sopenharmony_ci				return;
72262306a36Sopenharmony_ci			}
72362306a36Sopenharmony_ci			memcpy(buf, buf + 16, 16);
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		/*
72762306a36Sopenharmony_ci		 * Iterate over all possible DMI header addresses q.
72862306a36Sopenharmony_ci		 * Maintain the 32 bytes around q in buf.  On the
72962306a36Sopenharmony_ci		 * first iteration, substitute zero for the
73062306a36Sopenharmony_ci		 * out-of-range bytes so there is no chance of falsely
73162306a36Sopenharmony_ci		 * detecting an SMBIOS header.
73262306a36Sopenharmony_ci		 */
73362306a36Sopenharmony_ci		memset(buf, 0, 16);
73462306a36Sopenharmony_ci		for (q = p; q < p + 0x10000; q += 16) {
73562306a36Sopenharmony_ci			memcpy_fromio(buf + 16, q, 16);
73662306a36Sopenharmony_ci			if (!dmi_present(buf)) {
73762306a36Sopenharmony_ci				dmi_available = 1;
73862306a36Sopenharmony_ci				dmi_early_unmap(p, 0x10000);
73962306a36Sopenharmony_ci				return;
74062306a36Sopenharmony_ci			}
74162306a36Sopenharmony_ci			memcpy(buf, buf + 16, 16);
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci		dmi_early_unmap(p, 0x10000);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci error:
74662306a36Sopenharmony_ci	pr_info("DMI not present or invalid.\n");
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic ssize_t raw_table_read(struct file *file, struct kobject *kobj,
75062306a36Sopenharmony_ci			      struct bin_attribute *attr, char *buf,
75162306a36Sopenharmony_ci			      loff_t pos, size_t count)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	memcpy(buf, attr->private + pos, count);
75462306a36Sopenharmony_ci	return count;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic BIN_ATTR(smbios_entry_point, S_IRUSR, raw_table_read, NULL, 0);
75862306a36Sopenharmony_cistatic BIN_ATTR(DMI, S_IRUSR, raw_table_read, NULL, 0);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int __init dmi_init(void)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct kobject *tables_kobj;
76362306a36Sopenharmony_ci	u8 *dmi_table;
76462306a36Sopenharmony_ci	int ret = -ENOMEM;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (!dmi_available)
76762306a36Sopenharmony_ci		return 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/*
77062306a36Sopenharmony_ci	 * Set up dmi directory at /sys/firmware/dmi. This entry should stay
77162306a36Sopenharmony_ci	 * even after farther error, as it can be used by other modules like
77262306a36Sopenharmony_ci	 * dmi-sysfs.
77362306a36Sopenharmony_ci	 */
77462306a36Sopenharmony_ci	dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
77562306a36Sopenharmony_ci	if (!dmi_kobj)
77662306a36Sopenharmony_ci		goto err;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	tables_kobj = kobject_create_and_add("tables", dmi_kobj);
77962306a36Sopenharmony_ci	if (!tables_kobj)
78062306a36Sopenharmony_ci		goto err;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	dmi_table = dmi_remap(dmi_base, dmi_len);
78362306a36Sopenharmony_ci	if (!dmi_table)
78462306a36Sopenharmony_ci		goto err_tables;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	bin_attr_smbios_entry_point.size = smbios_entry_point_size;
78762306a36Sopenharmony_ci	bin_attr_smbios_entry_point.private = smbios_entry_point;
78862306a36Sopenharmony_ci	ret = sysfs_create_bin_file(tables_kobj, &bin_attr_smbios_entry_point);
78962306a36Sopenharmony_ci	if (ret)
79062306a36Sopenharmony_ci		goto err_unmap;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	bin_attr_DMI.size = dmi_len;
79362306a36Sopenharmony_ci	bin_attr_DMI.private = dmi_table;
79462306a36Sopenharmony_ci	ret = sysfs_create_bin_file(tables_kobj, &bin_attr_DMI);
79562306a36Sopenharmony_ci	if (!ret)
79662306a36Sopenharmony_ci		return 0;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	sysfs_remove_bin_file(tables_kobj,
79962306a36Sopenharmony_ci			      &bin_attr_smbios_entry_point);
80062306a36Sopenharmony_ci err_unmap:
80162306a36Sopenharmony_ci	dmi_unmap(dmi_table);
80262306a36Sopenharmony_ci err_tables:
80362306a36Sopenharmony_ci	kobject_del(tables_kobj);
80462306a36Sopenharmony_ci	kobject_put(tables_kobj);
80562306a36Sopenharmony_ci err:
80662306a36Sopenharmony_ci	pr_err("dmi: Firmware registration failed.\n");
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return ret;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_cisubsys_initcall(dmi_init);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci/**
81362306a36Sopenharmony_ci *	dmi_setup - scan and setup DMI system information
81462306a36Sopenharmony_ci *
81562306a36Sopenharmony_ci *	Scan the DMI system information. This setups DMI identifiers
81662306a36Sopenharmony_ci *	(dmi_system_id) for printing it out on task dumps and prepares
81762306a36Sopenharmony_ci *	DIMM entry information (dmi_memdev_info) from the SMBIOS table
81862306a36Sopenharmony_ci *	for using this when reporting memory errors.
81962306a36Sopenharmony_ci */
82062306a36Sopenharmony_civoid __init dmi_setup(void)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	dmi_scan_machine();
82362306a36Sopenharmony_ci	if (!dmi_available)
82462306a36Sopenharmony_ci		return;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	dmi_memdev_walk();
82762306a36Sopenharmony_ci	dump_stack_set_arch_desc("%s", dmi_ids_string);
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci/**
83162306a36Sopenharmony_ci *	dmi_matches - check if dmi_system_id structure matches system DMI data
83262306a36Sopenharmony_ci *	@dmi: pointer to the dmi_system_id structure to check
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cistatic bool dmi_matches(const struct dmi_system_id *dmi)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	int i;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
83962306a36Sopenharmony_ci		int s = dmi->matches[i].slot;
84062306a36Sopenharmony_ci		if (s == DMI_NONE)
84162306a36Sopenharmony_ci			break;
84262306a36Sopenharmony_ci		if (s == DMI_OEM_STRING) {
84362306a36Sopenharmony_ci			/* DMI_OEM_STRING must be exact match */
84462306a36Sopenharmony_ci			const struct dmi_device *valid;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci			valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
84762306a36Sopenharmony_ci						dmi->matches[i].substr, NULL);
84862306a36Sopenharmony_ci			if (valid)
84962306a36Sopenharmony_ci				continue;
85062306a36Sopenharmony_ci		} else if (dmi_ident[s]) {
85162306a36Sopenharmony_ci			if (dmi->matches[i].exact_match) {
85262306a36Sopenharmony_ci				if (!strcmp(dmi_ident[s],
85362306a36Sopenharmony_ci					    dmi->matches[i].substr))
85462306a36Sopenharmony_ci					continue;
85562306a36Sopenharmony_ci			} else {
85662306a36Sopenharmony_ci				if (strstr(dmi_ident[s],
85762306a36Sopenharmony_ci					   dmi->matches[i].substr))
85862306a36Sopenharmony_ci					continue;
85962306a36Sopenharmony_ci			}
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		/* No match */
86362306a36Sopenharmony_ci		return false;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci	return true;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci/**
86962306a36Sopenharmony_ci *	dmi_is_end_of_table - check for end-of-table marker
87062306a36Sopenharmony_ci *	@dmi: pointer to the dmi_system_id structure to check
87162306a36Sopenharmony_ci */
87262306a36Sopenharmony_cistatic bool dmi_is_end_of_table(const struct dmi_system_id *dmi)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	return dmi->matches[0].slot == DMI_NONE;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/**
87862306a36Sopenharmony_ci *	dmi_check_system - check system DMI data
87962306a36Sopenharmony_ci *	@list: array of dmi_system_id structures to match against
88062306a36Sopenharmony_ci *		All non-null elements of the list must match
88162306a36Sopenharmony_ci *		their slot's (field index's) data (i.e., each
88262306a36Sopenharmony_ci *		list string must be a substring of the specified
88362306a36Sopenharmony_ci *		DMI slot's string data) to be considered a
88462306a36Sopenharmony_ci *		successful match.
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci *	Walk the blacklist table running matching functions until someone
88762306a36Sopenharmony_ci *	returns non zero or we hit the end. Callback function is called for
88862306a36Sopenharmony_ci *	each successful match. Returns the number of matches.
88962306a36Sopenharmony_ci *
89062306a36Sopenharmony_ci *	dmi_setup must be called before this function is called.
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_ciint dmi_check_system(const struct dmi_system_id *list)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	int count = 0;
89562306a36Sopenharmony_ci	const struct dmi_system_id *d;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	for (d = list; !dmi_is_end_of_table(d); d++)
89862306a36Sopenharmony_ci		if (dmi_matches(d)) {
89962306a36Sopenharmony_ci			count++;
90062306a36Sopenharmony_ci			if (d->callback && d->callback(d))
90162306a36Sopenharmony_ci				break;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return count;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_check_system);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/**
90962306a36Sopenharmony_ci *	dmi_first_match - find dmi_system_id structure matching system DMI data
91062306a36Sopenharmony_ci *	@list: array of dmi_system_id structures to match against
91162306a36Sopenharmony_ci *		All non-null elements of the list must match
91262306a36Sopenharmony_ci *		their slot's (field index's) data (i.e., each
91362306a36Sopenharmony_ci *		list string must be a substring of the specified
91462306a36Sopenharmony_ci *		DMI slot's string data) to be considered a
91562306a36Sopenharmony_ci *		successful match.
91662306a36Sopenharmony_ci *
91762306a36Sopenharmony_ci *	Walk the blacklist table until the first match is found.  Return the
91862306a36Sopenharmony_ci *	pointer to the matching entry or NULL if there's no match.
91962306a36Sopenharmony_ci *
92062306a36Sopenharmony_ci *	dmi_setup must be called before this function is called.
92162306a36Sopenharmony_ci */
92262306a36Sopenharmony_ciconst struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	const struct dmi_system_id *d;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	for (d = list; !dmi_is_end_of_table(d); d++)
92762306a36Sopenharmony_ci		if (dmi_matches(d))
92862306a36Sopenharmony_ci			return d;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return NULL;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_first_match);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/**
93562306a36Sopenharmony_ci *	dmi_get_system_info - return DMI data value
93662306a36Sopenharmony_ci *	@field: data index (see enum dmi_field)
93762306a36Sopenharmony_ci *
93862306a36Sopenharmony_ci *	Returns one DMI data value, can be used to perform
93962306a36Sopenharmony_ci *	complex DMI data checks.
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_ciconst char *dmi_get_system_info(int field)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	return dmi_ident[field];
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_get_system_info);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/**
94862306a36Sopenharmony_ci * dmi_name_in_serial - Check if string is in the DMI product serial information
94962306a36Sopenharmony_ci * @str: string to check for
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_ciint dmi_name_in_serial(const char *str)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	int f = DMI_PRODUCT_SERIAL;
95462306a36Sopenharmony_ci	if (dmi_ident[f] && strstr(dmi_ident[f], str))
95562306a36Sopenharmony_ci		return 1;
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/**
96062306a36Sopenharmony_ci *	dmi_name_in_vendors - Check if string is in the DMI system or board vendor name
96162306a36Sopenharmony_ci *	@str: Case sensitive Name
96262306a36Sopenharmony_ci */
96362306a36Sopenharmony_ciint dmi_name_in_vendors(const char *str)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	static int fields[] = { DMI_SYS_VENDOR, DMI_BOARD_VENDOR, DMI_NONE };
96662306a36Sopenharmony_ci	int i;
96762306a36Sopenharmony_ci	for (i = 0; fields[i] != DMI_NONE; i++) {
96862306a36Sopenharmony_ci		int f = fields[i];
96962306a36Sopenharmony_ci		if (dmi_ident[f] && strstr(dmi_ident[f], str))
97062306a36Sopenharmony_ci			return 1;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_name_in_vendors);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci/**
97762306a36Sopenharmony_ci *	dmi_find_device - find onboard device by type/name
97862306a36Sopenharmony_ci *	@type: device type or %DMI_DEV_TYPE_ANY to match all device types
97962306a36Sopenharmony_ci *	@name: device name string or %NULL to match all
98062306a36Sopenharmony_ci *	@from: previous device found in search, or %NULL for new search.
98162306a36Sopenharmony_ci *
98262306a36Sopenharmony_ci *	Iterates through the list of known onboard devices. If a device is
98362306a36Sopenharmony_ci *	found with a matching @type and @name, a pointer to its device
98462306a36Sopenharmony_ci *	structure is returned.  Otherwise, %NULL is returned.
98562306a36Sopenharmony_ci *	A new search is initiated by passing %NULL as the @from argument.
98662306a36Sopenharmony_ci *	If @from is not %NULL, searches continue from next device.
98762306a36Sopenharmony_ci */
98862306a36Sopenharmony_ciconst struct dmi_device *dmi_find_device(int type, const char *name,
98962306a36Sopenharmony_ci				    const struct dmi_device *from)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	const struct list_head *head = from ? &from->list : &dmi_devices;
99262306a36Sopenharmony_ci	struct list_head *d;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	for (d = head->next; d != &dmi_devices; d = d->next) {
99562306a36Sopenharmony_ci		const struct dmi_device *dev =
99662306a36Sopenharmony_ci			list_entry(d, struct dmi_device, list);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		if (((type == DMI_DEV_TYPE_ANY) || (dev->type == type)) &&
99962306a36Sopenharmony_ci		    ((name == NULL) || (strcmp(dev->name, name) == 0)))
100062306a36Sopenharmony_ci			return dev;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return NULL;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_find_device);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/**
100862306a36Sopenharmony_ci *	dmi_get_date - parse a DMI date
100962306a36Sopenharmony_ci *	@field:	data index (see enum dmi_field)
101062306a36Sopenharmony_ci *	@yearp: optional out parameter for the year
101162306a36Sopenharmony_ci *	@monthp: optional out parameter for the month
101262306a36Sopenharmony_ci *	@dayp: optional out parameter for the day
101362306a36Sopenharmony_ci *
101462306a36Sopenharmony_ci *	The date field is assumed to be in the form resembling
101562306a36Sopenharmony_ci *	[mm[/dd]]/yy[yy] and the result is stored in the out
101662306a36Sopenharmony_ci *	parameters any or all of which can be omitted.
101762306a36Sopenharmony_ci *
101862306a36Sopenharmony_ci *	If the field doesn't exist, all out parameters are set to zero
101962306a36Sopenharmony_ci *	and false is returned.  Otherwise, true is returned with any
102062306a36Sopenharmony_ci *	invalid part of date set to zero.
102162306a36Sopenharmony_ci *
102262306a36Sopenharmony_ci *	On return, year, month and day are guaranteed to be in the
102362306a36Sopenharmony_ci *	range of [0,9999], [0,12] and [0,31] respectively.
102462306a36Sopenharmony_ci */
102562306a36Sopenharmony_cibool dmi_get_date(int field, int *yearp, int *monthp, int *dayp)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	int year = 0, month = 0, day = 0;
102862306a36Sopenharmony_ci	bool exists;
102962306a36Sopenharmony_ci	const char *s, *y;
103062306a36Sopenharmony_ci	char *e;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	s = dmi_get_system_info(field);
103362306a36Sopenharmony_ci	exists = s;
103462306a36Sopenharmony_ci	if (!exists)
103562306a36Sopenharmony_ci		goto out;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/*
103862306a36Sopenharmony_ci	 * Determine year first.  We assume the date string resembles
103962306a36Sopenharmony_ci	 * mm/dd/yy[yy] but the original code extracted only the year
104062306a36Sopenharmony_ci	 * from the end.  Keep the behavior in the spirit of no
104162306a36Sopenharmony_ci	 * surprises.
104262306a36Sopenharmony_ci	 */
104362306a36Sopenharmony_ci	y = strrchr(s, '/');
104462306a36Sopenharmony_ci	if (!y)
104562306a36Sopenharmony_ci		goto out;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	y++;
104862306a36Sopenharmony_ci	year = simple_strtoul(y, &e, 10);
104962306a36Sopenharmony_ci	if (y != e && year < 100) {	/* 2-digit year */
105062306a36Sopenharmony_ci		year += 1900;
105162306a36Sopenharmony_ci		if (year < 1996)	/* no dates < spec 1.0 */
105262306a36Sopenharmony_ci			year += 100;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci	if (year > 9999)		/* year should fit in %04d */
105562306a36Sopenharmony_ci		year = 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* parse the mm and dd */
105862306a36Sopenharmony_ci	month = simple_strtoul(s, &e, 10);
105962306a36Sopenharmony_ci	if (s == e || *e != '/' || !month || month > 12) {
106062306a36Sopenharmony_ci		month = 0;
106162306a36Sopenharmony_ci		goto out;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	s = e + 1;
106562306a36Sopenharmony_ci	day = simple_strtoul(s, &e, 10);
106662306a36Sopenharmony_ci	if (s == y || s == e || *e != '/' || day > 31)
106762306a36Sopenharmony_ci		day = 0;
106862306a36Sopenharmony_ciout:
106962306a36Sopenharmony_ci	if (yearp)
107062306a36Sopenharmony_ci		*yearp = year;
107162306a36Sopenharmony_ci	if (monthp)
107262306a36Sopenharmony_ci		*monthp = month;
107362306a36Sopenharmony_ci	if (dayp)
107462306a36Sopenharmony_ci		*dayp = day;
107562306a36Sopenharmony_ci	return exists;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_get_date);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci/**
108062306a36Sopenharmony_ci *	dmi_get_bios_year - get a year out of DMI_BIOS_DATE field
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci *	Returns year on success, -ENXIO if DMI is not selected,
108362306a36Sopenharmony_ci *	or a different negative error code if DMI field is not present
108462306a36Sopenharmony_ci *	or not parseable.
108562306a36Sopenharmony_ci */
108662306a36Sopenharmony_ciint dmi_get_bios_year(void)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	bool exists;
108962306a36Sopenharmony_ci	int year;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	exists = dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
109262306a36Sopenharmony_ci	if (!exists)
109362306a36Sopenharmony_ci		return -ENODATA;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return year ? year : -ERANGE;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ciEXPORT_SYMBOL(dmi_get_bios_year);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/**
110062306a36Sopenharmony_ci *	dmi_walk - Walk the DMI table and get called back for every record
110162306a36Sopenharmony_ci *	@decode: Callback function
110262306a36Sopenharmony_ci *	@private_data: Private data to be passed to the callback function
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci *	Returns 0 on success, -ENXIO if DMI is not selected or not present,
110562306a36Sopenharmony_ci *	or a different negative error code if DMI walking fails.
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_ciint dmi_walk(void (*decode)(const struct dmi_header *, void *),
110862306a36Sopenharmony_ci	     void *private_data)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	u8 *buf;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (!dmi_available)
111362306a36Sopenharmony_ci		return -ENXIO;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	buf = dmi_remap(dmi_base, dmi_len);
111662306a36Sopenharmony_ci	if (buf == NULL)
111762306a36Sopenharmony_ci		return -ENOMEM;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	dmi_decode_table(buf, decode, private_data);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	dmi_unmap(buf);
112262306a36Sopenharmony_ci	return 0;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_walk);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci/**
112762306a36Sopenharmony_ci * dmi_match - compare a string to the dmi field (if exists)
112862306a36Sopenharmony_ci * @f: DMI field identifier
112962306a36Sopenharmony_ci * @str: string to compare the DMI field to
113062306a36Sopenharmony_ci *
113162306a36Sopenharmony_ci * Returns true if the requested field equals to the str (including NULL).
113262306a36Sopenharmony_ci */
113362306a36Sopenharmony_cibool dmi_match(enum dmi_field f, const char *str)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	const char *info = dmi_get_system_info(f);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (info == NULL || str == NULL)
113862306a36Sopenharmony_ci		return info == str;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return !strcmp(info, str);
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_match);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_civoid dmi_memdev_name(u16 handle, const char **bank, const char **device)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	int n;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (dmi_memdev == NULL)
114962306a36Sopenharmony_ci		return;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	for (n = 0; n < dmi_memdev_nr; n++) {
115262306a36Sopenharmony_ci		if (handle == dmi_memdev[n].handle) {
115362306a36Sopenharmony_ci			*bank = dmi_memdev[n].bank;
115462306a36Sopenharmony_ci			*device = dmi_memdev[n].device;
115562306a36Sopenharmony_ci			break;
115662306a36Sopenharmony_ci		}
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_memdev_name);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ciu64 dmi_memdev_size(u16 handle)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	int n;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (dmi_memdev) {
116662306a36Sopenharmony_ci		for (n = 0; n < dmi_memdev_nr; n++) {
116762306a36Sopenharmony_ci			if (handle == dmi_memdev[n].handle)
116862306a36Sopenharmony_ci				return dmi_memdev[n].size;
116962306a36Sopenharmony_ci		}
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci	return ~0ull;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_memdev_size);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci/**
117662306a36Sopenharmony_ci * dmi_memdev_type - get the memory type
117762306a36Sopenharmony_ci * @handle: DMI structure handle
117862306a36Sopenharmony_ci *
117962306a36Sopenharmony_ci * Return the DMI memory type of the module in the slot associated with the
118062306a36Sopenharmony_ci * given DMI handle, or 0x0 if no such DMI handle exists.
118162306a36Sopenharmony_ci */
118262306a36Sopenharmony_ciu8 dmi_memdev_type(u16 handle)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	int n;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (dmi_memdev) {
118762306a36Sopenharmony_ci		for (n = 0; n < dmi_memdev_nr; n++) {
118862306a36Sopenharmony_ci			if (handle == dmi_memdev[n].handle)
118962306a36Sopenharmony_ci				return dmi_memdev[n].type;
119062306a36Sopenharmony_ci		}
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci	return 0x0;	/* Not a valid value */
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_memdev_type);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci/**
119762306a36Sopenharmony_ci *	dmi_memdev_handle - get the DMI handle of a memory slot
119862306a36Sopenharmony_ci *	@slot: slot number
119962306a36Sopenharmony_ci *
120062306a36Sopenharmony_ci *	Return the DMI handle associated with a given memory slot, or %0xFFFF
120162306a36Sopenharmony_ci *      if there is no such slot.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_ciu16 dmi_memdev_handle(int slot)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	if (dmi_memdev && slot >= 0 && slot < dmi_memdev_nr)
120662306a36Sopenharmony_ci		return dmi_memdev[slot].handle;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	return 0xffff;	/* Not a valid value */
120962306a36Sopenharmony_ci}
121062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dmi_memdev_handle);
1211