Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
[pandora-kernel.git] / drivers / firmware / efivars.c
index 2a62ec6..ff0c373 100644 (file)
@@ -89,17 +89,6 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EFIVARS_VERSION);
 
-/*
- * efivars_lock protects two things:
- * 1) efivar_list - adds, removals, reads, writes
- * 2) efi.[gs]et_variable() calls.
- * It must not be held when creating sysfs entries or calling kmalloc.
- * efi.get_next_variable() is only called from efivars_init(),
- * which is protected by the BKL, so that path is safe.
- */
-static DEFINE_SPINLOCK(efivars_lock);
-static LIST_HEAD(efivar_list);
-
 /*
  * The maximum size of VariableName + Data = 1024
  * Therefore, it's reasonable to save that much
@@ -118,6 +107,7 @@ struct efi_variable {
 
 
 struct efivar_entry {
+       struct efivars *efivars;
        struct efi_variable var;
        struct list_head list;
        struct kobject kobj;
@@ -144,9 +134,10 @@ struct efivar_attribute efivar_attr_##_name = { \
  * Prototype for sysfs creation function
  */
 static int
-efivar_create_sysfs_entry(unsigned long variable_name_size,
-                               efi_char16_t *variable_name,
-                               efi_guid_t *vendor_guid);
+efivar_create_sysfs_entry(struct efivars *efivars,
+                         unsigned long variable_name_size,
+                         efi_char16_t *variable_name,
+                         efi_guid_t *vendor_guid);
 
 /* Return the number of unicode characters in data */
 static unsigned long
@@ -170,18 +161,18 @@ utf8_strsize(efi_char16_t *data, unsigned long maxlength)
 }
 
 static efi_status_t
-get_var_data(struct efi_variable *var)
+get_var_data(struct efivars *efivars, struct efi_variable *var)
 {
        efi_status_t status;
 
-       spin_lock(&efivars_lock);
+       spin_lock(&efivars->lock);
        var->DataSize = 1024;
-       status = efi.get_variable(var->VariableName,
-                               &var->VendorGuid,
-                               &var->Attributes,
-                               &var->DataSize,
-                               var->Data);
-       spin_unlock(&efivars_lock);
+       status = efivars->ops->get_variable(var->VariableName,
+                                           &var->VendorGuid,
+                                           &var->Attributes,
+                                           &var->DataSize,
+                                           var->Data);
+       spin_unlock(&efivars->lock);
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
                        status);
@@ -215,7 +206,7 @@ efivar_attr_read(struct efivar_entry *entry, char *buf)
        if (!entry || !buf)
                return -EINVAL;
 
-       status = get_var_data(var);
+       status = get_var_data(entry->efivars, var);
        if (status != EFI_SUCCESS)
                return -EIO;
 
@@ -238,7 +229,7 @@ efivar_size_read(struct efivar_entry *entry, char *buf)
        if (!entry || !buf)
                return -EINVAL;
 
-       status = get_var_data(var);
+       status = get_var_data(entry->efivars, var);
        if (status != EFI_SUCCESS)
                return -EIO;
 
@@ -255,7 +246,7 @@ efivar_data_read(struct efivar_entry *entry, char *buf)
        if (!entry || !buf)
                return -EINVAL;
 
-       status = get_var_data(var);
+       status = get_var_data(entry->efivars, var);
        if (status != EFI_SUCCESS)
                return -EIO;
 
@@ -270,6 +261,7 @@ static ssize_t
 efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
 {
        struct efi_variable *new_var, *var = &entry->var;
+       struct efivars *efivars = entry->efivars;
        efi_status_t status = EFI_NOT_FOUND;
 
        if (count != sizeof(struct efi_variable))
@@ -291,14 +283,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
                return -EINVAL;
        }
 
-       spin_lock(&efivars_lock);
-       status = efi.set_variable(new_var->VariableName,
-                                       &new_var->VendorGuid,
-                                       new_var->Attributes,
-                                       new_var->DataSize,
-                                       new_var->Data);
+       spin_lock(&efivars->lock);
+       status = efivars->ops->set_variable(new_var->VariableName,
+                                           &new_var->VendorGuid,
+                                           new_var->Attributes,
+                                           new_var->DataSize,
+                                           new_var->Data);
 
-       spin_unlock(&efivars_lock);
+       spin_unlock(&efivars->lock);
 
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
@@ -319,7 +311,7 @@ efivar_show_raw(struct efivar_entry *entry, char *buf)
        if (!entry || !buf)
                return 0;
 
-       status = get_var_data(var);
+       status = get_var_data(entry->efivars, var);
        if (status != EFI_SUCCESS)
                return -EIO;
 
@@ -407,6 +399,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
                             char *buf, loff_t pos, size_t count)
 {
        struct efi_variable *new_var = (struct efi_variable *)buf;
+       struct efivars *efivars = bin_attr->private;
        struct efivar_entry *search_efivar, *n;
        unsigned long strsize1, strsize2;
        efi_status_t status = EFI_NOT_FOUND;
@@ -415,12 +408,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       spin_lock(&efivars_lock);
+       spin_lock(&efivars->lock);
 
        /*
         * Does this variable already exist?
         */
-       list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
+       list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
                strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
                strsize2 = utf8_strsize(new_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
@@ -433,28 +426,31 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
                }
        }
        if (found) {
-               spin_unlock(&efivars_lock);
+               spin_unlock(&efivars->lock);
                return -EINVAL;
        }
 
        /* now *really* create the variable via EFI */
-       status = efi.set_variable(new_var->VariableName,
-                       &new_var->VendorGuid,
-                       new_var->Attributes,
-                       new_var->DataSize,
-                       new_var->Data);
+       status = efivars->ops->set_variable(new_var->VariableName,
+                                           &new_var->VendorGuid,
+                                           new_var->Attributes,
+                                           new_var->DataSize,
+                                           new_var->Data);
 
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
                        status);
-               spin_unlock(&efivars_lock);
+               spin_unlock(&efivars->lock);
                return -EIO;
        }
-       spin_unlock(&efivars_lock);
+       spin_unlock(&efivars->lock);
 
        /* Create the entry in sysfs.  Locking is not required here */
-       status = efivar_create_sysfs_entry(utf8_strsize(new_var->VariableName,
-                       1024), new_var->VariableName, &new_var->VendorGuid);
+       status = efivar_create_sysfs_entry(efivars,
+                                          utf8_strsize(new_var->VariableName,
+                                                       1024),
+                                          new_var->VariableName,
+                                          &new_var->VendorGuid);
        if (status) {
                printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n");
        }
@@ -466,6 +462,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
                             char *buf, loff_t pos, size_t count)
 {
        struct efi_variable *del_var = (struct efi_variable *)buf;
+       struct efivars *efivars = bin_attr->private;
        struct efivar_entry *search_efivar, *n;
        unsigned long strsize1, strsize2;
        efi_status_t status = EFI_NOT_FOUND;
@@ -474,12 +471,12 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       spin_lock(&efivars_lock);
+       spin_lock(&efivars->lock);
 
        /*
         * Does this variable already exist?
         */
-       list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
+       list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
                strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
                strsize2 = utf8_strsize(del_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
@@ -492,44 +489,34 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
                }
        }
        if (!found) {
-               spin_unlock(&efivars_lock);
+               spin_unlock(&efivars->lock);
                return -EINVAL;
        }
        /* force the Attributes/DataSize to 0 to ensure deletion */
        del_var->Attributes = 0;
        del_var->DataSize = 0;
 
-       status = efi.set_variable(del_var->VariableName,
-                       &del_var->VendorGuid,
-                       del_var->Attributes,
-                       del_var->DataSize,
-                       del_var->Data);
+       status = efivars->ops->set_variable(del_var->VariableName,
+                                           &del_var->VendorGuid,
+                                           del_var->Attributes,
+                                           del_var->DataSize,
+                                           del_var->Data);
 
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
                        status);
-               spin_unlock(&efivars_lock);
+               spin_unlock(&efivars->lock);
                return -EIO;
        }
        list_del(&search_efivar->list);
        /* We need to release this lock before unregistering. */
-       spin_unlock(&efivars_lock);
+       spin_unlock(&efivars->lock);
        efivar_unregister(search_efivar);
 
        /* It's dead Jim.... */
        return count;
 }
 
-static struct bin_attribute var_subsys_attr_new_var = {
-       .attr = {.name = "new_var", .mode = 0200},
-       .write = efivar_create,
-};
-
-static struct bin_attribute var_subsys_attr_del_var = {
-       .attr = {.name = "del_var", .mode = 0200},
-       .write = efivar_delete,
-};
-
 /*
  * Let's not leave out systab information that snuck into
  * the efivars driver
@@ -572,8 +559,6 @@ static struct attribute_group efi_subsys_attr_group = {
        .attrs = efi_subsys_attrs,
 };
 
-
-static struct kset *vars_kset;
 static struct kobject *efi_kobj;
 
 /*
@@ -582,13 +567,14 @@ static struct kobject *efi_kobj;
  *    variable_name_size = number of bytes required to hold
  *                         variable_name (not counting the NULL
  *                         character at the end.
- *    efivars_lock is not held on entry or exit.
+ *    efivars->lock is not held on entry or exit.
  * Returns 1 on failure, 0 on success
  */
 static int
-efivar_create_sysfs_entry(unsigned long variable_name_size,
-                       efi_char16_t *variable_name,
-                       efi_guid_t *vendor_guid)
+efivar_create_sysfs_entry(struct efivars *efivars,
+                         unsigned long variable_name_size,
+                         efi_char16_t *variable_name,
+                         efi_guid_t *vendor_guid)
 {
        int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
        char *short_name;
@@ -603,6 +589,7 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
                return 1;
        }
 
+       new_efivar->efivars = efivars;
        memcpy(new_efivar->var.VariableName, variable_name,
                variable_name_size);
        memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
@@ -618,7 +605,7 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
        *(short_name + strlen(short_name)) = '-';
        efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
 
-       new_efivar->kobj.kset = vars_kset;
+       new_efivar->kobj.kset = efivars->kset;
        i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL,
                                 "%s", short_name);
        if (i) {
@@ -631,22 +618,95 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
        kfree(short_name);
        short_name = NULL;
 
-       spin_lock(&efivars_lock);
-       list_add(&new_efivar->list, &efivar_list);
-       spin_unlock(&efivars_lock);
+       spin_lock(&efivars->lock);
+       list_add(&new_efivar->list, &efivars->list);
+       spin_unlock(&efivars->lock);
 
        return 0;
 }
-/*
- * For now we register the efi subsystem with the firmware subsystem
- * and the vars subsystem with the efi subsystem.  In the future, it
- * might make sense to split off the efi subsystem into its own
- * driver, but for now only efivars will register with it, so just
- * include it here.
- */
 
-static int __init
-efivars_init(void)
+static int
+create_efivars_bin_attributes(struct efivars *efivars)
+{
+       struct bin_attribute *attr;
+       int error;
+
+       /* new_var */
+       attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+       if (!attr)
+               return -ENOMEM;
+
+       attr->attr.name = "new_var";
+       attr->attr.mode = 0200;
+       attr->write = efivar_create;
+       attr->private = efivars;
+       efivars->new_var = attr;
+
+       /* del_var */
+       attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+       if (!attr) {
+               error = -ENOMEM;
+               goto out_free;
+       }
+       attr->attr.name = "del_var";
+       attr->attr.mode = 0200;
+       attr->write = efivar_delete;
+       attr->private = efivars;
+       efivars->del_var = attr;
+
+       sysfs_bin_attr_init(efivars->new_var);
+       sysfs_bin_attr_init(efivars->del_var);
+
+       /* Register */
+       error = sysfs_create_bin_file(&efivars->kset->kobj,
+                                     efivars->new_var);
+       if (error) {
+               printk(KERN_ERR "efivars: unable to create new_var sysfs file"
+                       " due to error %d\n", error);
+               goto out_free;
+       }
+       error = sysfs_create_bin_file(&efivars->kset->kobj,
+                                     efivars->del_var);
+       if (error) {
+               printk(KERN_ERR "efivars: unable to create del_var sysfs file"
+                       " due to error %d\n", error);
+               sysfs_remove_bin_file(&efivars->kset->kobj,
+                                     efivars->new_var);
+               goto out_free;
+       }
+
+       return 0;
+out_free:
+       kfree(efivars->new_var);
+       efivars->new_var = NULL;
+       kfree(efivars->new_var);
+       efivars->new_var = NULL;
+       return error;
+}
+
+void unregister_efivars(struct efivars *efivars)
+{
+       struct efivar_entry *entry, *n;
+
+       list_for_each_entry_safe(entry, n, &efivars->list, list) {
+               spin_lock(&efivars->lock);
+               list_del(&entry->list);
+               spin_unlock(&efivars->lock);
+               efivar_unregister(entry);
+       }
+       if (efivars->new_var)
+               sysfs_remove_bin_file(&efivars->kset->kobj, efivars->new_var);
+       if (efivars->del_var)
+               sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var);
+       kfree(efivars->new_var);
+       kfree(efivars->del_var);
+       kset_unregister(efivars->kset);
+}
+EXPORT_SYMBOL_GPL(unregister_efivars);
+
+int register_efivars(struct efivars *efivars,
+                    const struct efivar_operations *ops,
+                    struct kobject *parent_kobj)
 {
        efi_status_t status = EFI_NOT_FOUND;
        efi_guid_t vendor_guid;
@@ -654,31 +714,21 @@ efivars_init(void)
        unsigned long variable_name_size = 1024;
        int error = 0;
 
-       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);
+       spin_lock_init(&efivars->lock);
+       INIT_LIST_HEAD(&efivars->list);
+       efivars->ops = ops;
 
-       /* For now we'll register the efi directory at /sys/firmware/efi */
-       efi_kobj = kobject_create_and_add("efi", firmware_kobj);
-       if (!efi_kobj) {
-               printk(KERN_ERR "efivars: Firmware registration failed.\n");
-               error = -ENOMEM;
-               goto out_free;
-       }
-
-       vars_kset = kset_create_and_add("vars", NULL, efi_kobj);
-       if (!vars_kset) {
+       efivars->kset = kset_create_and_add("vars", NULL, parent_kobj);
+       if (!efivars->kset) {
                printk(KERN_ERR "efivars: Subsystem registration failed.\n");
                error = -ENOMEM;
-               goto out_firmware_unregister;
+               goto out;
        }
 
        /*
@@ -689,14 +739,15 @@ efivars_init(void)
        do {
                variable_name_size = 1024;
 
-               status = efi.get_next_variable(&variable_name_size,
+               status = ops->get_next_variable(&variable_name_size,
                                                variable_name,
                                                &vendor_guid);
                switch (status) {
                case EFI_SUCCESS:
-                       efivar_create_sysfs_entry(variable_name_size,
-                                                       variable_name,
-                                                       &vendor_guid);
+                       efivar_create_sysfs_entry(efivars,
+                                                 variable_name_size,
+                                                 variable_name,
+                                                 &vendor_guid);
                        break;
                case EFI_NOT_FOUND:
                        break;
@@ -708,35 +759,60 @@ efivars_init(void)
                }
        } while (status != EFI_NOT_FOUND);
 
-       /*
-        * Now add attributes to allow creation of new vars
-        * and deletion of existing ones...
-        */
-       error = sysfs_create_bin_file(&vars_kset->kobj,
-                                     &var_subsys_attr_new_var);
-       if (error)
-               printk(KERN_ERR "efivars: unable to create new_var sysfs file"
-                       " due to error %d\n", error);
-       error = sysfs_create_bin_file(&vars_kset->kobj,
-                                     &var_subsys_attr_del_var);
+       error = create_efivars_bin_attributes(efivars);
        if (error)
-               printk(KERN_ERR "efivars: unable to create del_var sysfs file"
-                       " due to error %d\n", error);
+               unregister_efivars(efivars);
 
-       /* Don't forget the systab entry */
-       error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
-       if (error)
-               printk(KERN_ERR "efivars: Sysfs attribute export failed with error %d.\n", error);
-       else
-               goto out_free;
+out:
+       kfree(variable_name);
 
-       kset_unregister(vars_kset);
+       return error;
+}
+EXPORT_SYMBOL_GPL(register_efivars);
 
-out_firmware_unregister:
-       kobject_put(efi_kobj);
+static struct efivars __efivars;
+static struct efivar_operations ops;
 
-out_free:
-       kfree(variable_name);
+/*
+ * For now we register the efi subsystem with the firmware subsystem
+ * and the vars subsystem with the efi subsystem.  In the future, it
+ * might make sense to split off the efi subsystem into its own
+ * driver, but for now only efivars will register with it, so just
+ * include it here.
+ */
+
+static int __init
+efivars_init(void)
+{
+       int error = 0;
+
+       printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
+              EFIVARS_DATE);
+
+       if (!efi_enabled)
+               return 0;
+
+       /* For now we'll register the efi directory at /sys/firmware/efi */
+       efi_kobj = kobject_create_and_add("efi", firmware_kobj);
+       if (!efi_kobj) {
+               printk(KERN_ERR "efivars: Firmware registration failed.\n");
+               return -ENOMEM;
+       }
+
+       ops.get_variable = efi.get_variable;
+       ops.set_variable = efi.set_variable;
+       ops.get_next_variable = efi.get_next_variable;
+       error = register_efivars(&__efivars, &ops, efi_kobj);
+
+       /* Don't forget the systab entry */
+       error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
+       if (error) {
+               printk(KERN_ERR
+                      "efivars: Sysfs attribute export failed with error %d.\n",
+                      error);
+               unregister_efivars(&__efivars);
+               kobject_put(efi_kobj);
+       }
 
        return error;
 }
@@ -744,16 +820,7 @@ out_free:
 static void __exit
 efivars_exit(void)
 {
-       struct efivar_entry *entry, *n;
-
-       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);
-       }
-
-       kset_unregister(vars_kset);
+       unregister_efivars(&__efivars);
        kobject_put(efi_kobj);
 }