Commit d7f96f97 authored by Ivan Khoronzhuk's avatar Ivan Khoronzhuk Committed by Jean Delvare

firmware: dmi_scan: add SBMIOS entry and DMI tables

Some utils, like dmidecode and smbios, need to access SMBIOS entry
table area in order to get information like SMBIOS version, size, etc.
Currently it's done via /dev/mem. But for situation when /dev/mem
usage is disabled, the utils have to use dmi sysfs instead, which
doesn't represent SMBIOS entry and adds code/delay redundancy when direct
access for table is needed.

So this patch creates dmi/tables and adds SMBIOS entry point to allow
utils in question to work correctly without /dev/mem. Also patch adds
raw dmi table to simplify dmi table processing in user space, as
proposed by Jean Delvare.
Tested-by: default avatarRoy Franz <roy.franz@linaro.org>
Signed-off-by: default avatarIvan Khoronzhuk <ivan.khoronzhuk@globallogic.com>
Signed-off-by: default avatarJean Delvare <jdelvare@suse.de>
parent 6e0ad59e
What: /sys/firmware/dmi/tables/
Date: April 2015
Contact: Ivan Khoronzhuk <ivan.khoronzhuk@globallogic.com>
Description:
The firmware provides DMI structures as a packed list of
data referenced by a SMBIOS table entry point. The SMBIOS
entry point contains general information, like SMBIOS
version, DMI table size, etc. The structure, content and
size of SMBIOS entry point is dependent on SMBIOS version.
The format of SMBIOS entry point and DMI structures
can be read in SMBIOS specification.
The dmi/tables provides raw SMBIOS entry point and DMI tables
through sysfs as an alternative to utilities reading them
from /dev/mem. The raw SMBIOS entry point and DMI table are
presented as binary attributes and are accessible via:
/sys/firmware/dmi/tables/smbios_entry_point
/sys/firmware/dmi/tables/DMI
The complete DMI information can be obtained using these two
tables.
...@@ -3289,6 +3289,7 @@ DMI/SMBIOS SUPPORT ...@@ -3289,6 +3289,7 @@ DMI/SMBIOS SUPPORT
M: Jean Delvare <jdelvare@suse.de> M: Jean Delvare <jdelvare@suse.de>
S: Maintained S: Maintained
T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-dmi/ T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-dmi/
F: Documentation/ABI/testing/sysfs-firmware-dmi-tables
F: drivers/firmware/dmi-id.c F: drivers/firmware/dmi-id.c
F: drivers/firmware/dmi_scan.c F: drivers/firmware/dmi_scan.c
F: include/linux/dmi.h F: include/linux/dmi.h
......
...@@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = { ...@@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = {
.default_attrs = dmi_sysfs_entry_attrs, .default_attrs = dmi_sysfs_entry_attrs,
}; };
static struct kobject *dmi_kobj;
static struct kset *dmi_kset; static struct kset *dmi_kset;
/* Global count of all instances seen. Only for setup */ /* Global count of all instances seen. Only for setup */
...@@ -648,17 +647,20 @@ static void cleanup_entry_list(void) ...@@ -648,17 +647,20 @@ static void cleanup_entry_list(void)
static int __init dmi_sysfs_init(void) static int __init dmi_sysfs_init(void)
{ {
int error = -ENOMEM; int error;
int val; int val;
/* Set up our directory */ if (!dmi_kobj) {
dmi_kobj = kobject_create_and_add("dmi", firmware_kobj); pr_err("dmi-sysfs: dmi entry is absent.\n");
if (!dmi_kobj) error = -ENODATA;
goto err; goto err;
}
dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj); dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
if (!dmi_kset) if (!dmi_kset) {
error = -ENOMEM;
goto err; goto err;
}
val = 0; val = 0;
error = dmi_walk(dmi_sysfs_register_handle, &val); error = dmi_walk(dmi_sysfs_register_handle, &val);
...@@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void) ...@@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void)
err: err:
cleanup_entry_list(); cleanup_entry_list();
kset_unregister(dmi_kset); kset_unregister(dmi_kset);
kobject_put(dmi_kobj);
return error; return error;
} }
...@@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void) ...@@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void)
pr_debug("dmi-sysfs: unloading.\n"); pr_debug("dmi-sysfs: unloading.\n");
cleanup_entry_list(); cleanup_entry_list();
kset_unregister(dmi_kset); kset_unregister(dmi_kset);
kobject_del(dmi_kobj);
kobject_put(dmi_kobj);
} }
module_init(dmi_sysfs_init); module_init(dmi_sysfs_init);
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <asm/dmi.h> #include <asm/dmi.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
struct kobject *dmi_kobj;
EXPORT_SYMBOL_GPL(dmi_kobj);
/* /*
* DMI stands for "Desktop Management Interface". It is part * DMI stands for "Desktop Management Interface". It is part
* of and an antecedent to, SMBIOS, which stands for System * of and an antecedent to, SMBIOS, which stands for System
...@@ -20,6 +23,9 @@ static const char dmi_empty_string[] = " "; ...@@ -20,6 +23,9 @@ static const char dmi_empty_string[] = " ";
static u32 dmi_ver __initdata; static u32 dmi_ver __initdata;
static u32 dmi_len; static u32 dmi_len;
static u16 dmi_num; static u16 dmi_num;
static u8 smbios_entry_point[32];
static int smbios_entry_point_size;
/* /*
* Catch too early calls to dmi_check_system(): * Catch too early calls to dmi_check_system():
*/ */
...@@ -488,6 +494,8 @@ static int __init dmi_present(const u8 *buf) ...@@ -488,6 +494,8 @@ static int __init dmi_present(const u8 *buf)
if (memcmp(buf, "_SM_", 4) == 0 && if (memcmp(buf, "_SM_", 4) == 0 &&
buf[5] < 32 && dmi_checksum(buf, buf[5])) { buf[5] < 32 && dmi_checksum(buf, buf[5])) {
smbios_ver = get_unaligned_be16(buf + 6); smbios_ver = get_unaligned_be16(buf + 6);
smbios_entry_point_size = buf[5];
memcpy(smbios_entry_point, buf, smbios_entry_point_size);
/* Some BIOS report weird SMBIOS version, fix that up */ /* Some BIOS report weird SMBIOS version, fix that up */
switch (smbios_ver) { switch (smbios_ver) {
...@@ -522,6 +530,9 @@ static int __init dmi_present(const u8 *buf) ...@@ -522,6 +530,9 @@ static int __init dmi_present(const u8 *buf)
pr_info("SMBIOS %d.%d present.\n", pr_info("SMBIOS %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF); dmi_ver >> 8, dmi_ver & 0xFF);
} else { } else {
smbios_entry_point_size = 15;
memcpy(smbios_entry_point, buf,
smbios_entry_point_size);
pr_info("Legacy DMI %d.%d present.\n", pr_info("Legacy DMI %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF); dmi_ver >> 8, dmi_ver & 0xFF);
} }
...@@ -548,6 +559,8 @@ static int __init dmi_smbios3_present(const u8 *buf) ...@@ -548,6 +559,8 @@ static int __init dmi_smbios3_present(const u8 *buf)
dmi_num = 0; /* No longer specified */ dmi_num = 0; /* No longer specified */
dmi_len = get_unaligned_le32(buf + 12); dmi_len = get_unaligned_le32(buf + 12);
dmi_base = get_unaligned_le64(buf + 16); dmi_base = get_unaligned_le64(buf + 16);
smbios_entry_point_size = buf[6];
memcpy(smbios_entry_point, buf, smbios_entry_point_size);
if (dmi_walk_early(dmi_decode) == 0) { if (dmi_walk_early(dmi_decode) == 0) {
pr_info("SMBIOS %d.%d.%d present.\n", pr_info("SMBIOS %d.%d.%d present.\n",
...@@ -639,6 +652,71 @@ void __init dmi_scan_machine(void) ...@@ -639,6 +652,71 @@ void __init dmi_scan_machine(void)
dmi_initialized = 1; dmi_initialized = 1;
} }
static ssize_t raw_table_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t count)
{
memcpy(buf, attr->private + pos, count);
return count;
}
static BIN_ATTR(smbios_entry_point, S_IRUSR, raw_table_read, NULL, 0);
static BIN_ATTR(DMI, S_IRUSR, raw_table_read, NULL, 0);
static int __init dmi_init(void)
{
struct kobject *tables_kobj;
u8 *dmi_table;
int ret = -ENOMEM;
if (!dmi_available) {
ret = -ENODATA;
goto err;
}
/*
* Set up dmi directory at /sys/firmware/dmi. This entry should stay
* even after farther error, as it can be used by other modules like
* dmi-sysfs.
*/
dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
if (!dmi_kobj)
goto err;
tables_kobj = kobject_create_and_add("tables", dmi_kobj);
if (!tables_kobj)
goto err;
dmi_table = dmi_remap(dmi_base, dmi_len);
if (!dmi_table)
goto err_tables;
bin_attr_smbios_entry_point.size = smbios_entry_point_size;
bin_attr_smbios_entry_point.private = smbios_entry_point;
ret = sysfs_create_bin_file(tables_kobj, &bin_attr_smbios_entry_point);
if (ret)
goto err_unmap;
bin_attr_DMI.size = dmi_len;
bin_attr_DMI.private = dmi_table;
ret = sysfs_create_bin_file(tables_kobj, &bin_attr_DMI);
if (!ret)
return 0;
sysfs_remove_bin_file(tables_kobj,
&bin_attr_smbios_entry_point);
err_unmap:
dmi_unmap(dmi_table);
err_tables:
kobject_del(tables_kobj);
kobject_put(tables_kobj);
err:
pr_err("dmi: Firmware registration failed.\n");
return ret;
}
subsys_initcall(dmi_init);
/** /**
* dmi_set_dump_stack_arch_desc - set arch description for dump_stack() * dmi_set_dump_stack_arch_desc - set arch description for dump_stack()
* *
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define __DMI_H__ #define __DMI_H__
#include <linux/list.h> #include <linux/list.h>
#include <linux/kobject.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
/* enum dmi_field is in mod_devicetable.h */ /* enum dmi_field is in mod_devicetable.h */
...@@ -93,6 +94,7 @@ struct dmi_dev_onboard { ...@@ -93,6 +94,7 @@ struct dmi_dev_onboard {
int devfn; int devfn;
}; };
extern struct kobject *dmi_kobj;
extern int dmi_check_system(const struct dmi_system_id *list); extern int dmi_check_system(const struct dmi_system_id *list);
const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list); const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
extern const char * dmi_get_system_info(int field); extern const char * dmi_get_system_info(int field);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment