diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
commit | dc50eab76b709d68175a358d6e23a5a3890764d3 (patch) | |
tree | c754d0390db060af0213ff994f0ac310e4cfd6e9 /drivers/platform/x86/think-lmi.c | |
parent | Adding debian version 6.6.15-2. (diff) | |
download | linux-dc50eab76b709d68175a358d6e23a5a3890764d3.tar.xz linux-dc50eab76b709d68175a358d6e23a5a3890764d3.zip |
Merging upstream version 6.7.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/platform/x86/think-lmi.c')
-rw-r--r-- | drivers/platform/x86/think-lmi.c | 218 |
1 files changed, 159 insertions, 59 deletions
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index aee8697698..ce3e08815a 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -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,17 +998,27 @@ 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. * Note - this sets the variable and then the password as separate * WMI calls. Function tlmi_save_bios_settings will error if the * password is incorrect. + * Workstation's require the opcode to be set before changing the + * attribute. */ + 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; + } + set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name, new_setting); if (!set_str) { @@ -1033,14 +1030,10 @@ 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; - } - - ret = tlmi_save_bios_settings(""); + if (tlmi_priv.save_mode == TLMI_SAVE_BULK) + tlmi_priv.save_required = true; + else + 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 +1061,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 +1149,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 +1261,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 +1317,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 +1416,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 +1549,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 +1565,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); |