diff --git a/Documentation/ABI/testing/sysfs-firmware-sfi b/Documentation/ABI/testing/sysfs-firmware-sfi new file mode 100644 index 00000000000..4be7d44aeac --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-sfi @@ -0,0 +1,15 @@ +What: /sys/firmware/sfi/tables/ +Date: May 2010 +Contact: Len Brown +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 diff --git a/drivers/sfi/sfi_acpi.c b/drivers/sfi/sfi_acpi.c index 34aba30eb84..f5b4ca58154 100644 --- a/drivers/sfi/sfi_acpi.c +++ b/drivers/sfi/sfi_acpi.c @@ -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; +} diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c index aba6a461365..00519595864 100644 --- a/drivers/sfi/sfi_core.c +++ b/drivers/sfi/sfi_core.c @@ -67,6 +67,7 @@ #include #include #include +#include #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); diff --git a/drivers/sfi/sfi_core.h b/drivers/sfi/sfi_core.h index da82d39e104..b7cf220d44e 100644 --- a/drivers/sfi/sfi_core.h +++ b/drivers/sfi/sfi_core.h @@ -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);