The Dell WMI Systems Management Driver provides a sysfs
interface for systems management to enable BIOS configuration
capability on certain Dell Systems.
This driver allows user to configure Dell systems with a
uniform common interface. To facilitate this, the patch
introduces a generic way for driver to be able to create
configurable BIOS Attributes available in Setup (F2) screen.
Cc: Hans de Goede <hdegoede@xxxxxxxxxx>
Cc: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>
Cc: mark gross <mgross@xxxxxxxxxxxxxxx>
Co-developed-by: Mario Limonciello <mario.limonciello@xxxxxxxx>
Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxxx>
Co-developed-by: Prasanth KSR <prasanth.ksr@xxxxxxxx>
Signed-off-by: Prasanth KSR <prasanth.ksr@xxxxxxxx>
Signed-off-by: Divya Bharathi <divya_bharathi@xxxxxxxx>
---
Changes from v2 to v3:
- Fix a possible NULL pointer error in init
- Add missing newlines to all dev_err/dev_dbg/pr_err/pr_debug statements
- Correct updating passwords when both Admin and System password are set
- Correct the WMI driver name
- Correct some namespace clashing when compiled into the kernel (Reported by Mark Gross)
- Correct some comment typos
- Adopt suggestions made by Hans:
+ Use single global mutex
+ Clarify reason for uevents with a comment
+ Remove functions for set and get current password
+ Rename lower_bound to min_value and upper_bound to max_value
+ Rename possible_value to possible_values
+ Remove references to float
+ Build a separate passwords directory again since it behaves differently from the other
attributes
+ Move more calls from pr_err -> dev_err
- Documentation cleanups (see v2 patch feedback)
+ Grouping types
+ Syntax of `modifier` output
Changes from v1 to v2:
- use pr_fmt instead of pr_err(DRIVER_NAME
- re-order variables reverse xmas tree order
- correct returns of -1 to error codes
- correct usage of {} on some split line statements
- Refine all documentation deficiencies suggested by Hans
- Merge all attributes to a single directory
- Overhaul WMI interface interaction as suggested by Hans
* Move WMI driver registration to start of module
* Remove usage of lists that only use first entry for WMI interfaces
* Create a global structure shared across interface source files
* Make get_current_password function static
* Remove get_pending changes function, shared across global structure now.
- Overhaul use of mutexes
* Make kset list mutex shared across source files
* Remove unneeded dell-wmi-sysman call_mutex
* Keep remaining call_mutexes in WMI functions
- Move security area calculation into a function
- Use NLS helper for utf8->utf16 conversion
.../testing/sysfs-platform-dell-wmi-sysman | 175 ++++++
MAINTAINERS | 9 +
drivers/platform/x86/Kconfig | 12 +
drivers/platform/x86/Makefile | 8 +
.../x86/dell-wmi-biosattr-interface.c | 197 ++++++
.../platform/x86/dell-wmi-enum-attributes.c | 208 +++++++
.../platform/x86/dell-wmi-int-attributes.c | 189 ++++++
.../x86/dell-wmi-passobj-attributes.c | 165 +++++
.../x86/dell-wmi-passwordattr-interface.c | 167 +++++
.../platform/x86/dell-wmi-string-attributes.c | 171 +++++
.../platform/x86/dell-wmi-sysman-attributes.c | 589 ++++++++++++++++++
.../platform/x86/dell-wmi-sysman-attributes.h | 137 ++++
12 files changed, 2027 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman
create mode 100644 drivers/platform/x86/dell-wmi-biosattr-interface.c
create mode 100644 drivers/platform/x86/dell-wmi-enum-attributes.c
create mode 100644 drivers/platform/x86/dell-wmi-int-attributes.c
create mode 100644 drivers/platform/x86/dell-wmi-passobj-attributes.c
create mode 100644 drivers/platform/x86/dell-wmi-passwordattr-interface.c
create mode 100644 drivers/platform/x86/dell-wmi-string-attributes.c
create mode 100644 drivers/platform/x86/dell-wmi-sysman-attributes.c
create mode 100644 drivers/platform/x86/dell-wmi-sysman-attributes.h
diff --git a/drivers/platform/x86/dell-wmi-enum-attributes.c b/drivers/platform/x86/dell-wmi-enum-attributes.c
new file mode 100644
index 000000000000..4598c4e27310
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-enum-attributes.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions corresponding to enumeration type attributes under BIOS Enumeration GUID for use
+ * with dell-wmi-sysman
+ *
+ * Copyright (c) 2020 Dell Inc.
+ */
+
+#include "dell-wmi-sysman-attributes.h"
+
+/* kept variable names same as in sysfs file name for sysfs_show macro definition */
+struct enumeration_data {
+ char display_name_language_code[MAX_BUFF];
+ char attribute_name[MAX_BUFF];
+ char display_name[MAX_BUFF];
+ char default_value[MAX_BUFF];
+ char current_value[MAX_BUFF];
+ char modifier[MAX_BUFF];
+ int value_modifier_count;
+ char value_modifier[MAX_BUFF];
+ int possible_values_count;
+ char possible_values[MAX_BUFF];
+ char type[MAX_BUFF];
+};
+
+static struct enumeration_data *enumeration_data;
+static int enumeration_instances_count;
+get_instance_id(enumeration);
+
+static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int instance_id;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ instance_id = get_enumeration_instance_id(kobj);
+ if (instance_id >= 0) {
+ union acpi_object *obj;
+
+ /* need to use specific instance_id and guid combination to get right data */
+ obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
+ if (!obj)
+ return -AE_ERROR;
+ strncpy_attr(enumeration_data[instance_id].current_value,
+ obj->package.elements[CURRENT_VAL].string.pointer);
+ kfree(obj);
+ return sprintf(buf, "%s\n", enumeration_data[instance_id].current_value);
+ }
+ return -EIO;
+}
+
+/**
+ * validate_enumeration_input() - Validate input of current_value against possible values
+ * @instance_id: The instance on which input is validated
+ * @buf: Input value
+ **/
+int validate_enumeration_input(int instance_id, const char *buf)
+{
+ char *options, *tmp, *p;
+ int ret = -EINVAL;
+
+ options = tmp = kstrdup((enumeration_data[instance_id].possible_values), GFP_KERNEL);
+ if (!options)
+ return -ENOMEM;
+
+ while ((p = strsep(&options, ";")) != NULL) {
+ if (!*p)
+ continue;
+ if (!strncasecmp(p, buf, strlen(p))) {
+ ret = 0;
+ break;
+ }
+ }
+
+ kfree(tmp);
+ return ret;
+}
+
+attribute_s_property_show(display_name_language_code, enumeration);
+static struct kobj_attribute displ_langcode =
+ __ATTR_RO(display_name_language_code);
+
+attribute_s_property_show(display_name, enumeration);
+struct kobj_attribute displ_name =
+ __ATTR_RO(display_name);
+
+attribute_s_property_show(default_value, enumeration);
+struct kobj_attribute default_val =
+ __ATTR_RO(default_value);
+
+attribute_property_store(current_value, enumeration);
+struct kobj_attribute current_val =
+ __ATTR_RW(current_value);
+
+attribute_s_property_show(modifier, enumeration);
+struct kobj_attribute modifier =
+ __ATTR_RO(modifier);
+
+attribute_s_property_show(value_modifier, enumeration);
+struct kobj_attribute value_modfr =
+ __ATTR_RO(value_modifier);
+
+attribute_s_property_show(possible_values, enumeration);
+struct kobj_attribute poss_val =
+ __ATTR_RO(possible_values);
+
+attribute_s_property_show(type, enumeration);
+struct kobj_attribute type =
+ __ATTR_RO(type);
+
+static struct attribute *enumeration_attrs[] = {
+ &displ_langcode.attr,
+ &displ_name.attr,
+ &default_val.attr,
+ ¤t_val.attr,
+ &modifier.attr,
+ &value_modfr.attr,
+ &poss_val.attr,
+ &type.attr,
+ NULL,
+};
+
+static const struct attribute_group enumeration_attr_group = {
+ .attrs = enumeration_attrs,
+};
+
+int alloc_enum_data(void)
+{
+ int ret = 0;
+
+ enumeration_instances_count = get_instance_count(DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
+ enumeration_data = kzalloc((sizeof(struct enumeration_data) * enumeration_instances_count),
+ GFP_KERNEL);
+ if (!enumeration_data)
+ ret = -ENOMEM;
+ return ret;
+}
+
+/**
+ * populate_enum_data() - Populate all properties of an instance under enumeration attribute
+ * @enumeration_obj: ACPI object with enumeration data
+ * @instance_id: The instance to enumerate
+ * @attr_name_kobj: The parent kernel object
+ **/
+int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,
+ struct kobject *attr_name_kobj)
+{
+ int retval = sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
+ int i, next_obj;
+
+ if (retval)
+ goto out;
+
+ mutex_lock(&wmi_priv.mutex);
+ strncpy_attr(enumeration_data[instance_id].attribute_name,
+ enumeration_obj[ATTR_NAME].string.pointer);
+ strncpy_attr(enumeration_data[instance_id].display_name_language_code,
+ enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);
+ strncpy_attr(enumeration_data[instance_id].display_name,
+ enumeration_obj[DISPLAY_NAME].string.pointer);
+ strncpy_attr(enumeration_data[instance_id].default_value,
+ enumeration_obj[DEFAULT_VAL].string.pointer);
+ strncpy_attr(enumeration_data[instance_id].current_value,
+ enumeration_obj[CURRENT_VAL].string.pointer);
+ strncpy_attr(enumeration_data[instance_id].modifier,
+ enumeration_obj[MODIFIER].string.pointer);
+
+ next_obj = MODIFIER + 1;
+
+ enumeration_data[instance_id].value_modifier_count =
+ (uintptr_t)enumeration_obj[next_obj].string.pointer;
+
+ for (i = 0; i < enumeration_data[instance_id].value_modifier_count; i++) {
+ strcat(enumeration_data[instance_id].value_modifier,
+ enumeration_obj[++next_obj].string.pointer);
+ strcat(enumeration_data[instance_id].value_modifier, ";");
+ }
+
+ enumeration_data[instance_id].possible_values_count =
+ (uintptr_t) enumeration_obj[++next_obj].string.pointer;
+
+ for (i = 0; i < enumeration_data[instance_id].possible_values_count; i++) {
+ strcat(enumeration_data[instance_id].possible_values,
+ enumeration_obj[++next_obj].string.pointer);
+ strcat(enumeration_data[instance_id].possible_values, ";");
+ }
+ strncpy_attr(enumeration_data[instance_id].type, "enumeration");
+ mutex_unlock(&wmi_priv.mutex);
+
+out:
+ return retval;
+}
+
+/**
+ * exit_enum_attributes() - Clear all attribute data
+ *
+ * Clears all data allocated for this group of attributes
+ **/
+void exit_enum_attributes(void)
+{
+ struct kobject *pos, *next;
+
+ list_for_each_entry_safe(pos, next, &wmi_priv.main_dir_kset->list, entry) {
+ /* this removes only enumeration atribute group under main_dir_kset */
+ sysfs_remove_group(pos, &enumeration_attr_group);
+ }
+ kfree(enumeration_data);
+}
diff --git a/drivers/platform/x86/dell-wmi-passobj-attributes.c b/drivers/platform/x86/dell-wmi-passobj-attributes.c
new file mode 100644
index 000000000000..6884e6153dd6
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions corresponding to password object type attributes under BIOS Password Object GUID for
+ * use with dell-wmi-sysman
+ *
+ * Copyright (c) 2020 Dell Inc.
+ */
+
+#include "dell-wmi-sysman-attributes.h"
+
+enum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN};
+
+/* kept variable names same as in sysfs file name for sysfs_show macro definition */
+struct po_data {
+ char attribute_name[MAX_BUFF];
+ int is_password_set;
+ int min_password_length;
+ int max_password_length;
+ char type[MAX_BUFF];
+};
+
+static struct po_data *po_data;
+static int po_instances_count;
+get_instance_id(po);
+
+static ssize_t is_password_set_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int instance_id = get_po_instance_id(kobj);
+
+ if (instance_id >= 0) {
+ union acpi_object *obj;
+
+ /* need to use specific instance_id and guid combination to get right data */
+ obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
+ if (!obj)
+ return -AE_ERROR;
+ po_data[instance_id].is_password_set =
+ (uintptr_t)obj->package.elements[IS_PASS_SET].string.pointer;
+ kfree(obj);
+ return sprintf(buf, "%d\n", po_data[instance_id].is_password_set);
+ }
+ return -EIO;
+}
+
+struct kobj_attribute po_is_pass_set =
+ __ATTR_RO(is_password_set);
+
+static ssize_t current_password_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *p = memchr(buf, '\n', count);
+ int ret;
+
+ if (p != NULL)
+ *p = '\0';
+ if (strlen(buf) > MAX_BUFF)
+ return -EINVAL;
+
+ if (strcmp(kobj->name, "Admin") == 0)
+ strncpy(wmi_priv.current_admin_password, buf, (strlen(buf) + 1));
+ if (strcmp(kobj->name, "System") == 0)
+ strncpy(wmi_priv.current_system_password, buf, (strlen(buf) + 1));
+ return ret ? ret : count;
+}
+
+struct kobj_attribute po_current_password =
+ __ATTR_WO(current_password);
+
+static ssize_t new_password_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *p = memchr(buf, '\n', count);
+ int ret;
+
+ if (p != NULL)
+ *p = '\0';
+ if (strlen(buf) > MAX_BUFF)
+ return -EINVAL;
+
+ ret = set_new_password(kobj->name, buf);
+ return ret ? ret : count;
+}
+
+struct kobj_attribute po_new_password =
+ __ATTR_WO(new_password);
+
+attribute_n_property_show(min_password_length, po);
+struct kobj_attribute po_min_pass_length =
+ __ATTR_RO(min_password_length);
+
+attribute_n_property_show(max_password_length, po);
+struct kobj_attribute po_max_pass_length =
+ __ATTR_RO(max_password_length);
+
+attribute_s_property_show(type, po);
+struct kobj_attribute po_type =
+ __ATTR_RO(type);
+
+static struct attribute *po_attrs[] = {
+ &po_is_pass_set.attr,
+ &po_min_pass_length.attr,
+ &po_max_pass_length.attr,
+ &po_current_password.attr,
+ &po_new_password.attr,
+ &po_type.attr,
+ NULL,
+};
+
+static const struct attribute_group po_attr_group = {
+ .attrs = po_attrs,
+};
+
+int alloc_po_data(void)
+{
+ int ret = 0;
+
+ po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
+ po_data = kzalloc((sizeof(struct po_data) * po_instances_count), GFP_KERNEL);
+ if (!po_data)
+ ret = -ENOMEM;
+ return ret;
+}
+
+/**
+ * populate_po_data() - Populate all properties of an instance under password object attribute
+ * @po_obj: ACPI object with password object data
+ * @instance_id: The instance to enumerate
+ * @attr_name_kobj: The parent kernel object
+ **/
+int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)
+{
+ int retval = sysfs_create_group(attr_name_kobj, &po_attr_group);
+
+ if (retval)
+ goto out;
+
+ mutex_lock(&wmi_priv.mutex);
+ strncpy_attr(po_data[instance_id].attribute_name, po_obj[ATTR_NAME].string.pointer);
+ po_data[instance_id].is_password_set = (uintptr_t)po_obj[IS_PASS_SET].string.pointer;
+ po_data[instance_id].min_password_length = (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
+ po_data[instance_id].max_password_length = (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
+ strncpy_attr(po_data[instance_id].type, "password_object");
+ mutex_unlock(&wmi_priv.mutex);
+
+out:
+ return retval;
+}
+
+/**
+ * exit_po_attributes() - Clear all attribute data
+ *
+ * Clears all data allocated for this group of attributes
+ **/
+void exit_po_attributes(void)
+{
+ struct kobject *pos, *next;
+
+ list_for_each_entry_safe(pos, next, &wmi_priv.password_dir_kset->list, entry) {
+ /* this removes only enumeration atribute group under main_dir_kset */
+ sysfs_remove_group(pos, &po_attr_group);
+ }
+ kfree(po_data);
+}
diff --git a/drivers/platform/x86/dell-wmi-passwordattr-interface.c b/drivers/platform/x86/dell-wmi-passwordattr-interface.c
new file mode 100644
index 000000000000..bd19d6c15002
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions corresponding to SET password methods under BIOS attributes interface GUID
+ *
+ * Copyright (c) 2020 Dell Inc.
+ */
+
+#include <linux/nls.h>
+#include <linux/wmi.h>
+#include "dell-wmi-sysman-attributes.h"
+
+static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size)
+{
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer input;
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = -EIO;
+
+ input.length = (acpi_size) size;
+ input.pointer = in_args;
+ status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ obj = (union acpi_object *)output.pointer;
+ if (obj->type == ACPI_TYPE_INTEGER)
+ ret = obj->integer.value;
+
+ kfree(output.pointer);
+ /* let userland know it may need to check is_password_set again */
+ kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);