summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/think-lmi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-10-31 17:53:00 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2023-10-31 17:53:00 -1000
commit59fff63cc2b75dcfe08f9eeb4b2187d73e53843d (patch)
treeec74d583e9e90699e915f06b84b210a4f5217312 /drivers/platform/x86/think-lmi.c
parent3475b91ff258b998b891964e8c263cff48384c01 (diff)
parent94ace9eda88229c73698b8dd8d3c06dd0831319c (diff)
downloadlinux-stable-59fff63cc2b75dcfe08f9eeb4b2187d73e53843d.tar.gz
linux-stable-59fff63cc2b75dcfe08f9eeb4b2187d73e53843d.tar.bz2
linux-stable-59fff63cc2b75dcfe08f9eeb4b2187d73e53843d.zip
Merge tag 'platform-drivers-x86-v6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86
Pull x86 platform driver updates from Ilpo Järvinen: - asus-wmi: Support for screenpad and solve brightness key press duplication - int3472: Eliminate the last use of deprecated GPIO functions - mlxbf-pmc: New HW support - msi-ec: Support new EC configurations - thinkpad_acpi: Support reading aux MAC address during passthrough - wmi: Fixes & improvements - x86-android-tablets: Detection fix and avoid use of GPIO private APIs - Debug & metrics interface improvements - Miscellaneous cleanups / fixes / improvements * tag 'platform-drivers-x86-v6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (80 commits) platform/x86: inspur-platform-profile: Add platform profile support platform/x86: thinkpad_acpi: Add battery quirk for Thinkpad X120e platform/x86: wmi: Decouple WMI device removal from wmi_block_list platform/x86: wmi: Fix opening of char device platform/x86: wmi: Fix probe failure when failing to register WMI devices platform/x86: wmi: Fix refcounting of WMI devices in legacy functions platform/x86: wmi: Decouple probe deferring from wmi_block_list platform/x86/amd/hsmp: Fix iomem handling platform/x86: asus-wmi: Do not report brightness up/down keys when also reported by acpi_video platform/x86: thinkpad_acpi: replace deprecated strncpy with memcpy tools/power/x86/intel-speed-select: v1.18 release tools/power/x86/intel-speed-select: Use cgroup isolate for CPU 0 tools/power/x86/intel-speed-select: Increase max CPUs in one request tools/power/x86/intel-speed-select: Display error for core-power support tools/power/x86/intel-speed-select: No TRL for non compute domains tools/power/x86/intel-speed-select: turbo-mode enable disable swapped tools/power/x86/intel-speed-select: Update help for TRL tools/power/x86/intel-speed-select: Sanitize integer arguments platform/x86: acer-wmi: Remove void function return platform/x86/amd/pmc: Add dump_custom_stb module parameter ...
Diffstat (limited to 'drivers/platform/x86/think-lmi.c')
-rw-r--r--drivers/platform/x86/think-lmi.c214
1 files changed, 156 insertions, 58 deletions
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
index aee869769843..3a396b763c49 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,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);