summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-firmware-dmi-tables22
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/firmware/dmi-sysfs.c17
-rw-r--r--drivers/firmware/dmi_scan.c78
-rw-r--r--include/linux/dmi.h2
5 files changed, 111 insertions, 9 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-dmi-tables b/Documentation/ABI/testing/sysfs-firmware-dmi-tables
new file mode 100644
index 000000000000..ff3cac8ed0bd
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-firmware-dmi-tables
@@ -0,0 +1,22 @@
+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.
diff --git a/MAINTAINERS b/MAINTAINERS
index 0502f9a7087f..ca473689d0f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3289,6 +3289,7 @@ DMI/SMBIOS SUPPORT
M: Jean Delvare <jdelvare@suse.de>
S: Maintained
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_scan.c
F: include/linux/dmi.h
diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c
index e0f1cb3d3598..ef76e5eecf0b 100644
--- a/drivers/firmware/dmi-sysfs.c
+++ b/drivers/firmware/dmi-sysfs.c
@@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = {
.default_attrs = dmi_sysfs_entry_attrs,
};
-static struct kobject *dmi_kobj;
static struct kset *dmi_kset;
/* Global count of all instances seen. Only for setup */
@@ -648,17 +647,20 @@ static void cleanup_entry_list(void)
static int __init dmi_sysfs_init(void)
{
- int error = -ENOMEM;
+ int error;
int val;
- /* Set up our directory */
- dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
- if (!dmi_kobj)
+ if (!dmi_kobj) {
+ pr_err("dmi-sysfs: dmi entry is absent.\n");
+ error = -ENODATA;
goto err;
+ }
dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
- if (!dmi_kset)
+ if (!dmi_kset) {
+ error = -ENOMEM;
goto err;
+ }
val = 0;
error = dmi_walk(dmi_sysfs_register_handle, &val);
@@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void)
err:
cleanup_entry_list();
kset_unregister(dmi_kset);
- kobject_put(dmi_kobj);
return error;
}
@@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void)
pr_debug("dmi-sysfs: unloading.\n");
cleanup_entry_list();
kset_unregister(dmi_kset);
- kobject_del(dmi_kobj);
- kobject_put(dmi_kobj);
}
module_init(dmi_sysfs_init);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 7fdf2868a276..5e0b770fdae5 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -10,6 +10,9 @@
#include <asm/dmi.h>
#include <asm/unaligned.h>
+struct kobject *dmi_kobj;
+EXPORT_SYMBOL_GPL(dmi_kobj);
+
/*
* DMI stands for "Desktop Management Interface". It is part
* of and an antecedent to, SMBIOS, which stands for System
@@ -20,6 +23,9 @@ static const char dmi_empty_string[] = " ";
static u32 dmi_ver __initdata;
static u32 dmi_len;
static u16 dmi_num;
+static u8 smbios_entry_point[32];
+static int smbios_entry_point_size;
+
/*
* Catch too early calls to dmi_check_system():
*/
@@ -488,6 +494,8 @@ static int __init dmi_present(const u8 *buf)
if (memcmp(buf, "_SM_", 4) == 0 &&
buf[5] < 32 && dmi_checksum(buf, buf[5])) {
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 */
switch (smbios_ver) {
@@ -522,6 +530,9 @@ static int __init dmi_present(const u8 *buf)
pr_info("SMBIOS %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
} else {
+ smbios_entry_point_size = 15;
+ memcpy(smbios_entry_point, buf,
+ smbios_entry_point_size);
pr_info("Legacy DMI %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
}
@@ -548,6 +559,8 @@ static int __init dmi_smbios3_present(const u8 *buf)
dmi_num = 0; /* No longer specified */
dmi_len = get_unaligned_le32(buf + 12);
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) {
pr_info("SMBIOS %d.%d.%d present.\n",
@@ -639,6 +652,71 @@ void __init dmi_scan_machine(void)
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()
*
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index f820f0a336c9..2f9f98827c0a 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -2,6 +2,7 @@
#define __DMI_H__
#include <linux/list.h>
+#include <linux/kobject.h>
#include <linux/mod_devicetable.h>
/* enum dmi_field is in mod_devicetable.h */
@@ -93,6 +94,7 @@ struct dmi_dev_onboard {
int devfn;
};
+extern struct kobject *dmi_kobj;
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);
extern const char * dmi_get_system_info(int field);