SFI: add sysfs interface for SFI tables.
authorFeng Tang <feng.tang@intel.com>
Wed, 26 May 2010 03:28:08 +0000 (11:28 +0800)
committerLen Brown <len.brown@intel.com>
Thu, 27 May 2010 16:46:20 +0000 (12:46 -0400)
Analogous to ACPI's /sys/firmware/acpi/tables/...

create /sys/firmware/sfi/tables/

The tables are primariy for the kernel,
but sometimes it is useful for user-space to be
able to read them.

Signed-off-by: Feng Tang <feng.tang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Documentation/ABI/testing/sysfs-firmware-sfi [new file with mode: 0644]
drivers/sfi/sfi_acpi.c
drivers/sfi/sfi_core.c
drivers/sfi/sfi_core.h

diff --git a/Documentation/ABI/testing/sysfs-firmware-sfi b/Documentation/ABI/testing/sysfs-firmware-sfi
new file mode 100644 (file)
index 0000000..4be7d44
--- /dev/null
@@ -0,0 +1,15 @@
+What:          /sys/firmware/sfi/tables/
+Date:          May 2010
+Contact:       Len Brown <lenb@kernel.org>
+Description:
+               SFI defines a number of small static memory tables
+               so the kernel can get platform information from firmware.
+
+               The tables are defined in the latest SFI specification:
+               http://simplefirmware.org/documentation
+
+               While the tables are used by the kernel, user-space
+               can observe them this way:
+
+               # cd /sys/firmware/sfi/tables
+               # cat $TABLENAME > $TABLENAME.bin
index 34aba30eb84bec5047f359586b9ea65b62e09c82..f5b4ca581541539f8336aaec73c286de1f0773fb 100644 (file)
@@ -173,3 +173,44 @@ int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id,
        sfi_acpi_put_table(table);
        return ret;
 }
+
+static ssize_t sfi_acpi_table_show(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr, char *buf,
+                              loff_t offset, size_t count)
+{
+       struct sfi_table_attr *tbl_attr =
+           container_of(bin_attr, struct sfi_table_attr, attr);
+       struct acpi_table_header *th = NULL;
+       struct sfi_table_key key;
+       ssize_t cnt;
+
+       key.sig = tbl_attr->name;
+       key.oem_id = NULL;
+       key.oem_table_id = NULL;
+
+       th = sfi_acpi_get_table(&key);
+       if (!th)
+               return 0;
+
+       cnt =  memory_read_from_buffer(buf, count, &offset,
+                                       th, th->length);
+       sfi_acpi_put_table(th);
+
+       return cnt;
+}
+
+
+void __init sfi_acpi_sysfs_init(void)
+{
+       u32 tbl_cnt, i;
+       struct sfi_table_attr *tbl_attr;
+
+       tbl_cnt = XSDT_GET_NUM_ENTRIES(xsdt_va, u64);
+       for (i = 0; i < tbl_cnt; i++) {
+               tbl_attr =
+                       sfi_sysfs_install_table(xsdt_va->table_offset_entry[i]);
+               tbl_attr->attr.read = sfi_acpi_table_show;
+       }
+
+       return;
+}
index aba6a461365b9907a1eae6386eb585fbfd16f284..005195958647b03acded28a822bfd7374744d210 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/acpi.h>
 #include <linux/init.h>
 #include <linux/sfi.h>
+#include <linux/slab.h>
 
 #include "sfi_core.h"
 
@@ -382,6 +383,102 @@ static __init int sfi_find_syst(void)
        return -1;
 }
 
+static struct kobject *sfi_kobj;
+static struct kobject *tables_kobj;
+
+static ssize_t sfi_table_show(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr, char *buf,
+                              loff_t offset, size_t count)
+{
+       struct sfi_table_attr *tbl_attr =
+           container_of(bin_attr, struct sfi_table_attr, attr);
+       struct sfi_table_header *th = NULL;
+       struct sfi_table_key key;
+       ssize_t cnt;
+
+       key.sig = tbl_attr->name;
+       key.oem_id = NULL;
+       key.oem_table_id = NULL;
+
+       if (strncmp(SFI_SIG_SYST, tbl_attr->name, SFI_SIGNATURE_SIZE)) {
+               th = sfi_get_table(&key);
+               if (!th)
+                       return 0;
+
+               cnt =  memory_read_from_buffer(buf, count, &offset,
+                                               th, th->len);
+               sfi_put_table(th);
+       } else
+               cnt =  memory_read_from_buffer(buf, count, &offset,
+                                       syst_va, syst_va->header.len);
+
+       return cnt;
+}
+
+struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa)
+{
+       struct sfi_table_attr *tbl_attr;
+       struct sfi_table_header *th;
+       int ret;
+
+       tbl_attr = kzalloc(sizeof(struct sfi_table_attr), GFP_KERNEL);
+       if (!tbl_attr)
+               return NULL;
+
+       th = sfi_map_table(pa);
+       if (!th || !th->sig[0]) {
+               kfree(tbl_attr);
+               return NULL;
+       }
+
+       sysfs_attr_init(&tbl_attr->attr.attr);
+       memcpy(tbl_attr->name, th->sig, SFI_SIGNATURE_SIZE);
+
+       tbl_attr->attr.size = 0;
+       tbl_attr->attr.read = sfi_table_show;
+       tbl_attr->attr.attr.name = tbl_attr->name;
+       tbl_attr->attr.attr.mode = 0400;
+
+       ret = sysfs_create_bin_file(tables_kobj,
+                                 &tbl_attr->attr);
+       if (ret)
+               kfree(tbl_attr);
+
+       sfi_unmap_table(th);
+       return tbl_attr;
+}
+
+static int __init sfi_sysfs_init(void)
+{
+       int tbl_cnt, i;
+
+       if (sfi_disabled)
+               return 0;
+
+       sfi_kobj = kobject_create_and_add("sfi", firmware_kobj);
+       if (!sfi_kobj)
+               return 0;
+
+       tables_kobj = kobject_create_and_add("tables", sfi_kobj);
+       if (!tables_kobj) {
+               kobject_put(sfi_kobj);
+               return 0;
+       }
+
+       sfi_sysfs_install_table(syst_pa);
+
+       tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
+
+       for (i = 0; i < tbl_cnt; i++)
+               sfi_sysfs_install_table(syst_va->pentry[i]);
+
+       sfi_acpi_sysfs_init();
+       kobject_uevent(sfi_kobj, KOBJ_ADD);
+       kobject_uevent(tables_kobj, KOBJ_ADD);
+       pr_info("SFI sysfs interfaces init success\n");
+       return 0;
+}
+
 void __init sfi_init(void)
 {
        if (!acpi_disabled)
@@ -414,3 +511,9 @@ void __init sfi_init_late(void)
 
        sfi_acpi_init();
 }
+
+/*
+ * The reason we put it here becasue we need wait till the /sys/firmware
+ * is setup, then our interface can be registered in /sys/firmware/sfi
+ */
+core_initcall(sfi_sysfs_init);
index da82d39e104dd18350e8f27a28dbe5735d6ceca3..b7cf220d44ec575f31160926433eefc50653dd87 100644 (file)
@@ -61,6 +61,12 @@ struct sfi_table_key{
        char    *oem_table_id;
 };
 
+/* sysfs interface */
+struct sfi_table_attr {
+       struct bin_attribute attr;
+       char name[8];
+};
+
 #define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL }
 
 extern int __init sfi_acpi_init(void);
@@ -68,3 +74,5 @@ extern  struct sfi_table_header *sfi_check_table(u64 paddr,
                                        struct sfi_table_key *key);
 struct sfi_table_header *sfi_get_table(struct sfi_table_key *key);
 extern void sfi_put_table(struct sfi_table_header *table);
+extern struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa);
+extern void __init sfi_acpi_sysfs_init(void);