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