fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / firmware / efivars.c
index ddaf82f..c6281cc 100644 (file)
  *
  * Changelog:
  *
+ *  17 May 2004 - Matt Domsch <Matt_Domsch@dell.com>
+ *   remove check for efi_enabled in exit
+ *   add MODULE_VERSION
+ *
  *  26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com>
  *   minor bug fixes
  *
  *   v0.01 release to linux-ia64@linuxia64.org
  */
 
-#include <linux/config.h>
+#include <linux/capability.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/sched.h>               /* for capable() */
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/string.h>
 
 #include <asm/uaccess.h>
 
+#define EFIVARS_VERSION "0.08"
+#define EFIVARS_DATE "2004-May-17"
+
 MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
 MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 MODULE_LICENSE("GPL");
-
-#define EFIVARS_VERSION "0.07 2004-Apr-26"
+MODULE_VERSION(EFIVARS_VERSION);
 
 /*
  * efivars_lock protects two things:
@@ -91,7 +96,7 @@ MODULE_LICENSE("GPL");
  * efi.get_next_variable() is only called from efivars_init(),
  * which is protected by the BKL, so that path is safe.
  */
-static spinlock_t efivars_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(efivars_lock);
 static LIST_HEAD(efivar_list);
 
 /*
@@ -117,8 +122,6 @@ struct efivar_entry {
        struct kobject kobj;
 };
 
-#define get_efivar_entry(n) list_entry(n, struct efivar_entry, list)
-
 struct efivar_attribute {
        struct attribute attr;
        ssize_t (*show) (struct efivar_entry *entry, char *buf);
@@ -128,7 +131,7 @@ struct efivar_attribute {
 
 #define EFI_ATTR(_name, _mode, _show, _store) \
 struct subsys_attribute efi_attr_##_name = { \
-       .attr {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+       .attr {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
        .show = _show, \
        .store = _store, \
 };
@@ -346,7 +349,7 @@ static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr,
 {
        struct efivar_entry *var = to_efivar_entry(kobj);
        struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
-       ssize_t ret = 0;
+       ssize_t ret = -EIO;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -362,7 +365,7 @@ static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr,
 {
        struct efivar_entry *var = to_efivar_entry(kobj);
        struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
-       ssize_t ret = 0;
+       ssize_t ret = -EIO;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -381,9 +384,6 @@ static struct sysfs_ops efivar_attr_ops = {
 static void efivar_release(struct kobject *kobj)
 {
        struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj);
-       spin_lock(&efivars_lock);
-       list_del(&var->list);
-       spin_unlock(&efivars_lock);
        kfree(var);
 }
 
@@ -425,9 +425,8 @@ static ssize_t
 efivar_create(struct subsystem *sub, const char *buf, size_t count)
 {
        struct efi_variable *new_var = (struct efi_variable *)buf;
-       struct efivar_entry *search_efivar = NULL;
+       struct efivar_entry *search_efivar, *n;
        unsigned long strsize1, strsize2;
-       struct list_head *pos, *n;
        efi_status_t status = EFI_NOT_FOUND;
        int found = 0;
 
@@ -439,8 +438,7 @@ efivar_create(struct subsystem *sub, const char *buf, size_t count)
        /*
         * Does this variable already exist?
         */
-       list_for_each_safe(pos, n, &efivar_list) {
-               search_efivar = get_efivar_entry(pos);
+       list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
                strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
                strsize2 = utf8_strsize(new_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
@@ -485,9 +483,8 @@ static ssize_t
 efivar_delete(struct subsystem *sub, const char *buf, size_t count)
 {
        struct efi_variable *del_var = (struct efi_variable *)buf;
-       struct efivar_entry *search_efivar = NULL;
+       struct efivar_entry *search_efivar, *n;
        unsigned long strsize1, strsize2;
-       struct list_head *pos, *n;
        efi_status_t status = EFI_NOT_FOUND;
        int found = 0;
 
@@ -499,8 +496,7 @@ efivar_delete(struct subsystem *sub, const char *buf, size_t count)
        /*
         * Does this variable already exist?
         */
-       list_for_each_safe(pos, n, &efivar_list) {
-               search_efivar = get_efivar_entry(pos);
+       list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
                strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
                strsize2 = utf8_strsize(del_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
@@ -532,9 +528,9 @@ efivar_delete(struct subsystem *sub, const char *buf, size_t count)
                spin_unlock(&efivars_lock);
                return -EIO;
        }
+       list_del(&search_efivar->list);
        /* We need to release this lock before unregistering. */
        spin_unlock(&efivars_lock);
-
        efivar_unregister(search_efivar);
 
        /* It's dead Jim.... */
@@ -562,20 +558,20 @@ systab_read(struct subsystem *entry, char *buf)
        if (!entry || !buf)
                return -EINVAL;
 
-       if (efi.mps)
-               str += sprintf(str, "MPS=0x%lx\n", __pa(efi.mps));
-       if (efi.acpi20)
-               str += sprintf(str, "ACPI20=0x%lx\n", __pa(efi.acpi20));
-       if (efi.acpi)
-               str += sprintf(str, "ACPI=0x%lx\n", __pa(efi.acpi));
-       if (efi.smbios)
-               str += sprintf(str, "SMBIOS=0x%lx\n", __pa(efi.smbios));
-       if (efi.hcdp)
-               str += sprintf(str, "HCDP=0x%lx\n", __pa(efi.hcdp));
-       if (efi.boot_info)
-               str += sprintf(str, "BOOTINFO=0x%lx\n", __pa(efi.boot_info));
-       if (efi.uga)
-               str += sprintf(str, "UGA=0x%lx\n", __pa(efi.uga));
+       if (efi.mps != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "MPS=0x%lx\n", efi.mps);
+       if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
+       if (efi.acpi != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
+       if (efi.smbios != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
+       if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
+       if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
+       if (efi.uga != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "UGA=0x%lx\n", efi.uga);
 
        return str - buf;
 }
@@ -608,16 +604,14 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
        char *short_name;
        struct efivar_entry *new_efivar;
 
-       short_name = kmalloc(short_name_size + 1, GFP_KERNEL);
-       new_efivar = kmalloc(sizeof(struct efivar_entry), GFP_KERNEL);
+       short_name = kzalloc(short_name_size + 1, GFP_KERNEL);
+       new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
 
        if (!short_name || !new_efivar)  {
-               if (short_name)        kfree(short_name);
-               if (new_efivar)        kfree(new_efivar);
+               kfree(short_name);
+               kfree(new_efivar);
                return 1;
        }
-       memset(short_name, 0, short_name_size+1);
-       memset(new_efivar, 0, sizeof(struct efivar_entry));
 
        memcpy(new_efivar->var.VariableName, variable_name,
                variable_name_size);
@@ -634,11 +628,17 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
        *(short_name + strlen(short_name)) = '-';
        efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
 
-       kobject_set_name(&new_efivar->kobj, short_name);
+       kobject_set_name(&new_efivar->kobj, "%s", short_name);
        kobj_set_kset_s(new_efivar, vars_subsys);
-       kobject_register(&new_efivar->kobj);
+       i = kobject_register(&new_efivar->kobj);
+       if (i) {
+               kfree(short_name);
+               kfree(new_efivar);
+               return 1;
+       }
 
-       kfree(short_name); short_name = NULL;
+       kfree(short_name);
+       short_name = NULL;
 
        spin_lock(&efivars_lock);
        list_add(&new_efivar->list, &efivar_list);
@@ -659,32 +659,48 @@ efivars_init(void)
 {
        efi_status_t status = EFI_NOT_FOUND;
        efi_guid_t vendor_guid;
-       efi_char16_t *variable_name = kmalloc(1024, GFP_KERNEL);
+       efi_char16_t *variable_name;
        struct subsys_attribute *attr;
        unsigned long variable_name_size = 1024;
-       int i, rc = 0, error = 0;
+       int i, error = 0;
 
-       printk(KERN_INFO "EFI Variables Facility v%s\n", EFIVARS_VERSION);
+       if (!efi_enabled)
+               return -ENODEV;
+
+       variable_name = kzalloc(variable_name_size, GFP_KERNEL);
+       if (!variable_name) {
+               printk(KERN_ERR "efivars: Memory allocation failed.\n");
+               return -ENOMEM;
+       }
+
+       printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
+              EFIVARS_DATE);
 
        /*
         * For now we'll register the efi subsys within this driver
         */
 
-       rc = firmware_register(&efi_subsys);
+       error = firmware_register(&efi_subsys);
 
-       if (rc)
-               return rc;
+       if (error) {
+               printk(KERN_ERR "efivars: Firmware registration failed with error %d.\n", error);
+               goto out_free;
+       }
 
        kset_set_kset_s(&vars_subsys, efi_subsys);
-       subsystem_register(&vars_subsys);
+
+       error = subsystem_register(&vars_subsys);
+
+       if (error) {
+               printk(KERN_ERR "efivars: Subsystem registration failed with error %d.\n", error);
+               goto out_firmware_unregister;
+       }
 
        /*
         * Per EFI spec, the maximum storage allocated for both
         * the variable name and variable data is 1024 bytes.
         */
 
-       memset(variable_name, 0, 1024);
-
        do {
                variable_name_size = 1024;
 
@@ -724,17 +740,33 @@ efivars_init(void)
                        error = subsys_create_file(&efi_subsys, attr);
        }
 
+       if (error)
+               printk(KERN_ERR "efivars: Sysfs attribute export failed with error %d.\n", error);
+       else
+               goto out_free;
+
+       subsystem_unregister(&vars_subsys);
+
+out_firmware_unregister:
+       firmware_unregister(&efi_subsys);
+
+out_free:
        kfree(variable_name);
-       return 0;
+
+       return error;
 }
 
 static void __exit
 efivars_exit(void)
 {
-       struct list_head *pos, *n;
+       struct efivar_entry *entry, *n;
 
-       list_for_each_safe(pos, n, &efivar_list)
-               efivar_unregister(get_efivar_entry(pos));
+       list_for_each_entry_safe(entry, n, &efivar_list, list) {
+               spin_lock(&efivars_lock);
+               list_del(&entry->list);
+               spin_unlock(&efivars_lock);
+               efivar_unregister(entry);
+       }
 
        subsystem_unregister(&vars_subsys);
        firmware_unregister(&efi_subsys);