Merge tag 'platform-drivers-x86-v6.7-1' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / drivers / platform / x86 / think-lmi.c
index aee869769843f5614ed373dd68df9764d816f5d4..3a396b763c4963d1f965e1d635967bd3f3d60f18 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/mutex.h>
-#include <linux/string.h>
+#include <linux/string_helpers.h>
 #include <linux/types.h>
 #include <linux/dmi.h>
 #include <linux/wmi.h>
@@ -198,14 +198,6 @@ static struct think_lmi tlmi_priv;
 static struct class *fw_attr_class;
 static DEFINE_MUTEX(tlmi_mutex);
 
-/* ------ Utility functions ------------*/
-/* Strip out CR if one is present */
-static void strip_cr(char *str)
-{
-       char *p = strchrnul(str, '\n');
-       *p = '\0';
-}
-
 /* Convert BIOS WMI error string to suitable error code */
 static int tlmi_errstr_to_err(const char *errstr)
 {
@@ -411,7 +403,7 @@ static ssize_t current_password_store(struct kobject *kobj,
 
        strscpy(setting->password, buf, setting->maxlen);
        /* Strip out CR if one is present, setting password won't work if it is present */
-       strip_cr(setting->password);
+       strreplace(setting->password, '\n', '\0');
        return count;
 }
 
@@ -432,13 +424,11 @@ static ssize_t new_password_store(struct kobject *kobj,
        if (!tlmi_priv.can_set_bios_password)
                return -EOPNOTSUPP;
 
-       new_pwd = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present, setting password won't work if it is present */
+       new_pwd = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!new_pwd)
                return -ENOMEM;
 
-       /* Strip out CR if one is present, setting password won't work if it is present */
-       strip_cr(new_pwd);
-
        /* Use lock in case multiple WMI operations needed */
        mutex_lock(&tlmi_mutex);
 
@@ -709,13 +699,11 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
        if (!setting->signature || !setting->signature[0])
                return -EACCES;
 
-       passwd = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present */
+       passwd = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!passwd)
                return -ENOMEM;
 
-       /* Strip out CR if one is present */
-       strip_cr(passwd);
-
        /* Format: 'Password,Signature' */
        auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
        if (!auth_str) {
@@ -765,11 +753,10 @@ static ssize_t certificate_store(struct kobject *kobj,
                return ret ?: count;
        }
 
-       new_cert = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present */
+       new_cert = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!new_cert)
                return -ENOMEM;
-       /* Strip out CR if one is present */
-       strip_cr(new_cert);
 
        if (setting->cert_installed) {
                /* Certificate is installed so this is an update */
@@ -817,13 +804,11 @@ static ssize_t signature_store(struct kobject *kobj,
        if (!tlmi_priv.certificate_support)
                return -EOPNOTSUPP;
 
-       new_signature = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present */
+       new_signature = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!new_signature)
                return -ENOMEM;
 
-       /* Strip out CR if one is present */
-       strip_cr(new_signature);
-
        /* Free any previous signature */
        kfree(setting->signature);
        setting->signature = new_signature;
@@ -846,13 +831,11 @@ static ssize_t save_signature_store(struct kobject *kobj,
        if (!tlmi_priv.certificate_support)
                return -EOPNOTSUPP;
 
-       new_signature = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present */
+       new_signature = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!new_signature)
                return -ENOMEM;
 
-       /* Strip out CR if one is present */
-       strip_cr(new_signature);
-
        /* Free any previous signature */
        kfree(setting->save_signature);
        setting->save_signature = new_signature;
@@ -930,7 +913,7 @@ static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *at
 static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 {
        struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
-       char *item, *value, *p;
+       char *item, *value;
        int ret;
 
        ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID);
@@ -943,8 +926,7 @@ static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *a
                ret = -EINVAL;
        else {
                /* On Workstations remove the Options part after the value */
-               p = strchrnul(value, ';');
-               *p = '\0';
+               strreplace(value, ';', '\0');
                ret = sysfs_emit(buf, "%s\n", value + 1);
        }
        kfree(item);
@@ -985,12 +967,17 @@ static ssize_t current_value_store(struct kobject *kobj,
        if (!tlmi_priv.can_set_bios_settings)
                return -EOPNOTSUPP;
 
-       new_setting = kstrdup(buf, GFP_KERNEL);
-       if (!new_setting)
-               return -ENOMEM;
+       /*
+        * If we are using bulk saves a reboot should be done once save has
+        * been called
+        */
+       if (tlmi_priv.save_mode == TLMI_SAVE_BULK && tlmi_priv.reboot_required)
+               return -EPERM;
 
        /* Strip out CR if one is present */
-       strip_cr(new_setting);
+       new_setting = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
+       if (!new_setting)
+               return -ENOMEM;
 
        /* Use lock in case multiple WMI operations needed */
        mutex_lock(&tlmi_mutex);
@@ -1011,10 +998,11 @@ static ssize_t current_value_store(struct kobject *kobj,
                ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str);
                if (ret)
                        goto out;
-               ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
-                               tlmi_priv.pwd_admin->save_signature);
-               if (ret)
-                       goto out;
+               if (tlmi_priv.save_mode == TLMI_SAVE_BULK)
+                       tlmi_priv.save_required = true;
+               else
+                       ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
+                                              tlmi_priv.pwd_admin->save_signature);
        } else if (tlmi_priv.opcode_support) {
                /*
                 * If opcode support is present use that interface.
@@ -1033,14 +1021,17 @@ static ssize_t current_value_store(struct kobject *kobj,
                if (ret)
                        goto out;
 
-               if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
-                       ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
-                                                 tlmi_priv.pwd_admin->password);
-                       if (ret)
-                               goto out;
+               if (tlmi_priv.save_mode == TLMI_SAVE_BULK) {
+                       tlmi_priv.save_required = true;
+               } else {
+                       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
+                               ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
+                                                         tlmi_priv.pwd_admin->password);
+                               if (ret)
+                                       goto out;
+                       }
+                       ret = tlmi_save_bios_settings("");
                }
-
-               ret = tlmi_save_bios_settings("");
        } else { /* old non-opcode based authentication method (deprecated) */
                if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
                        auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
@@ -1068,10 +1059,14 @@ static ssize_t current_value_store(struct kobject *kobj,
                if (ret)
                        goto out;
 
-               if (auth_str)
-                       ret = tlmi_save_bios_settings(auth_str);
-               else
-                       ret = tlmi_save_bios_settings("");
+               if (tlmi_priv.save_mode == TLMI_SAVE_BULK) {
+                       tlmi_priv.save_required = true;
+               } else {
+                       if (auth_str)
+                               ret = tlmi_save_bios_settings(auth_str);
+                       else
+                               ret = tlmi_save_bios_settings("");
+               }
        }
        if (!ret && !tlmi_priv.pending_changes) {
                tlmi_priv.pending_changes = true;
@@ -1152,6 +1147,107 @@ static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *
 
 static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
 
+static const char * const save_mode_strings[] = {
+       [TLMI_SAVE_SINGLE] = "single",
+       [TLMI_SAVE_BULK] = "bulk",
+       [TLMI_SAVE_SAVE] = "save"
+};
+
+static ssize_t save_settings_show(struct kobject *kobj, struct kobj_attribute *attr,
+                                 char *buf)
+{
+       /* Check that setting is valid */
+       if (WARN_ON(tlmi_priv.save_mode < TLMI_SAVE_SINGLE ||
+                   tlmi_priv.save_mode > TLMI_SAVE_BULK))
+               return -EIO;
+       return sysfs_emit(buf, "%s\n", save_mode_strings[tlmi_priv.save_mode]);
+}
+
+static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       char *auth_str = NULL;
+       int ret = 0;
+       int cmd;
+
+       cmd = sysfs_match_string(save_mode_strings, buf);
+       if (cmd < 0)
+               return cmd;
+
+       /* Use lock in case multiple WMI operations needed */
+       mutex_lock(&tlmi_mutex);
+
+       switch (cmd) {
+       case TLMI_SAVE_SINGLE:
+       case TLMI_SAVE_BULK:
+               tlmi_priv.save_mode = cmd;
+               goto out;
+       case TLMI_SAVE_SAVE:
+               /* Check if supported*/
+               if (!tlmi_priv.can_set_bios_settings ||
+                   tlmi_priv.save_mode == TLMI_SAVE_SINGLE) {
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
+               /* Check there is actually something to save */
+               if (!tlmi_priv.save_required) {
+                       ret = -ENOENT;
+                       goto out;
+               }
+               /* Check if certificate authentication is enabled and active */
+               if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) {
+                       if (!tlmi_priv.pwd_admin->signature ||
+                           !tlmi_priv.pwd_admin->save_signature) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
+                                              tlmi_priv.pwd_admin->save_signature);
+                       if (ret)
+                               goto out;
+               } else if (tlmi_priv.opcode_support) {
+                       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
+                               ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
+                                                         tlmi_priv.pwd_admin->password);
+                               if (ret)
+                                       goto out;
+                       }
+                       ret = tlmi_save_bios_settings("");
+               } else { /* old non-opcode based authentication method (deprecated) */
+                       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
+                               auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
+                                                    tlmi_priv.pwd_admin->password,
+                                                    encoding_options[tlmi_priv.pwd_admin->encoding],
+                                                    tlmi_priv.pwd_admin->kbdlang);
+                               if (!auth_str) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+                       }
+
+                       if (auth_str)
+                               ret = tlmi_save_bios_settings(auth_str);
+                       else
+                               ret = tlmi_save_bios_settings("");
+               }
+               tlmi_priv.save_required = false;
+               tlmi_priv.reboot_required = true;
+
+               if (!ret && !tlmi_priv.pending_changes) {
+                       tlmi_priv.pending_changes = true;
+                       /* let userland know it may need to check reboot pending again */
+                       kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE);
+               }
+               break;
+       }
+out:
+       mutex_unlock(&tlmi_mutex);
+       kfree(auth_str);
+       return ret ?: count;
+}
+
+static struct kobj_attribute save_settings = __ATTR_RW(save_settings);
+
 /* ---- Debug interface--------------------------------------------------------- */
 static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr,
                                const char *buf, size_t count)
@@ -1163,13 +1259,11 @@ static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr
        if (!tlmi_priv.can_debug_cmd)
                return -EOPNOTSUPP;
 
-       new_setting = kstrdup(buf, GFP_KERNEL);
+       /* Strip out CR if one is present */
+       new_setting = kstrdup_and_replace(buf, '\n', '\0', GFP_KERNEL);
        if (!new_setting)
                return -ENOMEM;
 
-       /* Strip out CR if one is present */
-       strip_cr(new_setting);
-
        if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
                auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
                                tlmi_priv.pwd_admin->password,
@@ -1221,6 +1315,8 @@ static void tlmi_release_attr(void)
                }
        }
        sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr);
+       sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &save_settings.attr);
+
        if (tlmi_priv.can_debug_cmd && debug_support)
                sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr);
 
@@ -1318,6 +1414,10 @@ static int tlmi_sysfs_init(void)
        if (ret)
                goto fail_create_attr;
 
+       ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &save_settings.attr);
+       if (ret)
+               goto fail_create_attr;
+
        if (tlmi_priv.can_debug_cmd && debug_support) {
                ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr);
                if (ret)
@@ -1447,7 +1547,6 @@ static int tlmi_analyze(void)
        for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) {
                struct tlmi_attr_setting *setting;
                char *item = NULL;
-               char *p;
 
                tlmi_priv.setting[i] = NULL;
                ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
@@ -1464,8 +1563,7 @@ static int tlmi_analyze(void)
                strreplace(item, '/', '\\');
 
                /* Remove the value part */
-               p = strchrnul(item, ',');
-               *p = '\0';
+               strreplace(item, ',', '\0');
 
                /* Create a setting entry */
                setting = kzalloc(sizeof(*setting), GFP_KERNEL);