Message ID | 20200730143122.10237-1-divya_bharathi@dell.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Series | Introduce support for Systems Management Driver over WMI for Dell Systems | expand |
On Sat, Aug 08, 2020 at 06:37:44PM +0000, Limonciello, Mario wrote: > +Andy > > Andy, can you please review this one? Yes, at some point. It's anyway material to at least v5.10. > Thanks, > > > -----Original Message----- > > From: platform-driver-x86-owner@vger.kernel.org <platform-driver-x86- > > owner@vger.kernel.org> On Behalf Of Divya Bharathi > > Sent: Thursday, July 30, 2020 9:31 AM > > To: dvhart@infradead.org > > Cc: LKML; platform-driver-x86@vger.kernel.org; Bharathi, Divya; Limonciello, > > Mario; Ksr, Prasanth > > Subject: [PATCH] Introduce support for Systems Management Driver over WMI for > > Dell Systems > > > > > > [EXTERNAL EMAIL] > > > > From: Divya Bharathi <divya.bharathi@dell.com> > > > > 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. > > > > Co-developed-by: Mario Limonciello <mario.limonciello@dell.com> > > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com> > > Co-developed-by: Prasanth KSR <prasanth.ksr@dell.com> > > Signed-off-by: Prasanth KSR <prasanth.ksr@dell.com> > > Signed-off-by: Divya Bharathi <divya.bharathi@dell.com> > > > > --- > > NOTE: This patch series is intended to go on top of > > platform-drivers-x86 linux-next. > > > > .../testing/sysfs-platform-dell-wmi-sysman | 201 ++++++ > > MAINTAINERS | 9 + > > drivers/platform/x86/Kconfig | 11 + > > drivers/platform/x86/Makefile | 8 + > > .../x86/dell-wmi-biosattr-interface.c | 263 ++++++++ > > .../platform/x86/dell-wmi-enum-attributes.c | 207 +++++++ > > .../platform/x86/dell-wmi-int-attributes.c | 188 ++++++ > > .../x86/dell-wmi-passobj-attributes.c | 161 +++++ > > .../x86/dell-wmi-passwordattr-interface.c | 230 +++++++ > > .../platform/x86/dell-wmi-string-attributes.c | 170 ++++++ > > .../platform/x86/dell-wmi-sysman-attributes.c | 575 ++++++++++++++++++ > > .../platform/x86/dell-wmi-sysman-attributes.h | 125 ++++ > > 12 files changed, 2148 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/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > > b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > > new file mode 100644 > > index 000000000000..8ca3e502ed89 > > --- /dev/null > > +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > > @@ -0,0 +1,201 @@ > > +What: /sys/devices/platform/dell-wmi-sysman/attributes/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + The Dell WMI Systems Management Driver provides a sysfs interface > > + for systems management software to enable BIOS configuration > > + capability on certain Dell systems. This directory exposes > > + interfaces for interacting with BIOS attributes > > + > > +What: /sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This attribute can be used to reset the BIOS Configuration. > > + Specifically, it tells which type of reset BIOS configuration is > > being > > + requested on the host. > > + > > + Reading from it returns a list of supported options encoded as: > > + > > + 'builtinsafe' (Built in safe configuration profile) > > + 'lastknowngood' (Last known good saved configuration profile) > > + 'factory' (Default factory settings configuration profile) > > + 'custom' (Custom saved configuration profile) > > + > > + The currently selected option is printed in square brackets as > > + shown below: > > + > > + # echo "factory" > sys/devices/platform/dell-wmi- > > sysman/attributes/reset_bios > > + > > + # cat sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > > + # builtinsafe lastknowngood [factory] custom > > + > > + Note that any changes to this attribute requires a reboot > > + for changes to take effect. > > + > > +What: /sys/devices/platform/dell-wmi- > > sysman/attributes/pending_reboot > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + A read-only attribute enumerating if a reboot is pending > > + on any BIOS attribute change. > > + > > + 0: No pending reboot > > + 1: Pending reboot > > + > > +What: /sys/devices/platform/dell-wmi- > > sysman/attributes/enumeration/<attr>/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This directory exposes interfaces for interaction with > > + BIOS enumeration attributes. > > + > > + Enumeration attributes are settings that accept set of > > + pre-defined valid values. > > + > > + current_value: A file that can be read to obtain the current > > + value of the <attr> > > + > > + This file can also be written to in order to update > > + the value of a <attr> > > + > > + default_value: A file that can be read to obtain the default > > + value of the <attr> > > + > > + display_name: A file that can be read to obtain a user friendly > > + description of the at <attr> > > + > > + display_name_language_code: A file that can be read to obtain > > + the language code corresponding to the "display_name" of the <attr> > > + > > + modifier: A file that can be read to obtain attribute-level > > + dependency rule which has to be met to configure <attr> > > + > > + possible_value: A file that can be read to obtain the possible > > + value of the <attr> > > + > > + value_modifier: A file that can be read to obtain value-level > > + dependency on a possible value which has to be met to configure > > <attr> > > + > > +What: /sys/devices/platform/dell-wmi- > > sysman/attributes/integer/<attr>/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This directory exposes interfaces for interaction with > > + BIOS integer attributes. > > + > > + Integer attributes are settings that accept a range of > > + numerical values for inputs. Each BIOS integer has a > > + lower bound and an upper bound on the values that it can take. > > + > > + current_value: A file that can be read to obtain the current > > + value of the <attr> > > + > > + This file can also be written to in order to update > > + the value of an <attr>. > > + > > + default_value: A file that can be read to obtain the default > > + value of the <attr> > > + > > + display_name: A file that can be read to obtain a user friendly > > + description of the at <attr> > > + > > + display_name_language_code: A file that can be read to obtain > > + the language code corresponding to the "display_name" of the <attr> > > + > > + lower_bound: A file that can be read to obtain the lower > > + bound value of the <attr> > > + > > + modifier: A file that can be read to obtain attribute-level > > + dependency rule which has to be met to configure <attr> > > + > > + scalar_increment: A file that can be read to obtain the > > + resolution of the incremental value this attribute accepts. > > + > > + upper_bound: A file that can be read to obtain the upper > > + bound value of the <attr> > > + > > +What: /sys/devices/platform/dell-wmi- > > sysman/attributes/string/<attr>/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This directory exposes interfaces for interaction with > > + BIOS string attributes. > > + > > + String attributes are settings that accept a string for an input. > > + Each BIOS string is characterized by the minimum length of the > > string, > > + the maximum length of the string, and the type of the string. > > + > > + current_value: A file that can be read to obtain the current > > + value of the <attr> > > + > > + This file can also be written to in order to update > > + the value of an <attr>. > > + > > + default_value: A file that can be read to obtain the default > > + value of the <attr> > > + > > + display_name: A file that can be read to obtain a user friendly > > + description of the at <attr> > > + > > + display_name_language_code: A file that can be read to obtain > > + the language code corresponding to the "display_name" of the <attr> > > + > > + max_length: A file that can be read to obtain the maximum > > + length value of the <attr> > > + > > + min_length: A file that can be read to obtain the minimum > > + length value of the <attr> > > + > > + modifier: A file that can be read to obtain attribute-level > > + dependency rule which has to be met to configure <attr> > > + > > +What: /sys/devices/platform/dell-wmi- > > sysman/attributes/password/<attr>/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This directory exposes interfaces for interaction with > > + BIOS password attributes. > > + > > + BIOS Admin password and System Password can be set, reset or > > cleared > > + using these attributes. > > + > > + An "Admin" password is used for preventing modification to the BIOS > > settings. > > + A "System" password is required to boot a machine. > > + > > + is_password_set: A file that can be read > > + to obtain flag to see if a password is set on <attr> > > + > > + max_password_length: A file that can be read to obtain the > > + maximum length of the Password > > + > > + min_password_length: A file that can be read to obtain the > > + minimum length of the Password > > + > > + current_password: A write only value used for privileged access > > + such as setting attributes when a system or admin password is set > > + or resetting to a new password > > + > > + new_password: A write only value that when used in tandem with > > + current_password will reset a system or admin password. > > diff --git a/MAINTAINERS b/MAINTAINERS > > index e64cdde81851..176311d712db 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -4879,6 +4879,15 @@ M: Mario Limonciello <mario.limonciello@dell.com> > > S: Maintained > > F: drivers/platform/x86/dell-wmi-descriptor.c > > > > +DELL WMI SYSMAN DRIVER > > +M: Divya Bharathi <divya.bharathi@dell.com> > > +M: Mario Limonciello <mario.limonciello@dell.com> > > +M: Prasanth Ksr <prasanth.ksr@dell.com> > > +L: platform-driver-x86@vger.kernel.org > > +S: Maintained > > +F: drivers/platform/x86/dell-wmi-*-attributes.* > > +F: drivers/platform/x86/dell-wmi-*-interface.c > > + > > DELL WMI NOTIFICATIONS DRIVER > > M: Matthew Garrett <mjg59@srcf.ucam.org> > > M: Pali Rohár <pali@kernel.org> > > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > > index 0581a54cf562..de66373554c2 100644 > > --- a/drivers/platform/x86/Kconfig > > +++ b/drivers/platform/x86/Kconfig > > @@ -430,6 +430,17 @@ config DELL_WMI > > To compile this driver as a module, choose M here: the module will > > be called dell-wmi. > > > > +config DELL_WMI_SYSMAN > > + tristate "Dell WMI SYSMAN" > > + depends on ACPI_WMI > > + depends on DMI > > + help > > + This driver allows changing BIOS settings on many Dell machines from > > + 2018 and newer without the use of any additional software. > > + > > + To compile this driver as a module, choose M here: the module will > > + be called dell-wmi-sysman. > > + > > config DELL_WMI_DESCRIPTOR > > tristate > > depends on ACPI_WMI > > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile > > index 2b85852a1a87..bee03f1af28f 100644 > > --- a/drivers/platform/x86/Makefile > > +++ b/drivers/platform/x86/Makefile > > @@ -47,6 +47,14 @@ obj-$(CONFIG_DELL_WMI) += dell-wmi.o > > obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o > > obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o > > obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o > > +obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o > > +dell-wmi-sysman-objs := dell-wmi-sysman-attributes.o \ > > + dell-wmi-enum-attributes.o \ > > + dell-wmi-int-attributes.o \ > > + dell-wmi-string-attributes.o \ > > + dell-wmi-passobj-attributes.o \ > > + dell-wmi-passwordattr-interface.o \ > > + dell-wmi-biosattr-interface.o > > > > # Fujitsu > > obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o > > diff --git a/drivers/platform/x86/dell-wmi-biosattr-interface.c > > b/drivers/platform/x86/dell-wmi-biosattr-interface.c > > new file mode 100644 > > index 000000000000..decf124d4388 > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-biosattr-interface.c > > @@ -0,0 +1,263 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Functions corresponding to SET methods under BIOS attributes interface > > GUID for use > > + * with dell-wmi-sysman > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#include <linux/wmi.h> > > +#include "dell-wmi-sysman-attributes.h" > > + > > +#define SETDEFAULTVALUES_METHOD_ID 0x02 > > +#define SETBIOSDEFAULTS_METHOD_ID 0x03 > > +#define SETATTRIBUTE_METHOD_ID 0x04 > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +struct wmi_interface_priv { > > + struct list_head list; > > + struct wmi_device *wdev; > > + struct device *child; > > + bool pending_changes; > > +}; > > +static LIST_HEAD(interface_list); > > + > > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > > +{ > > + return list_first_entry_or_null(&interface_list, > > + struct wmi_interface_priv, > > + list); > > +} > > + > > +static int call_biosattributes_interface(struct wmi_device *wdev, char > > *in_args, size_t size, > > + int method_id) > > +{ > > + 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, method_id, &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); > > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > > + return map_wmi_error(ret); > > +} > > + > > +/** > > + * get_pending_changes() - Fetch if any changes are pending > > + * > > + * Checks if any changes have been written that will be changed > > + * after a system reboot > > + **/ > > +bool get_pending_changes(void) > > +{ > > + struct wmi_interface_priv *priv; > > + > > + priv = get_first_interface_priv(); > > + if (priv) > > + return priv->pending_changes; > > + return 0; > > +} > > + > > +/** > > + * set_attribute() - Update an attribute value > > + * @a_name: The attribute name > > + * @a_value: The attribute value > > + * > > + * Sets an attribute to new value > > + **/ > > +int set_attribute(const char *a_name, const char *a_value) > > +{ > > + int ret = -1; > > + int i; > > + u8 *name_len, *value_len; > > + char *current_password, *attribute_name, *attribute_value; > > + size_t security_area_size; > > + size_t string_area_size; > > + size_t buffer_size; > > + struct wmi_interface_priv *priv; > > + char *buffer; > > + > > + /* look up if user set a password for the requests */ > > + current_password = get_current_password("Admin"); > > + if (!current_password) > > + return -ENODEV; > > + > > + /* password is set */ > > + if (strlen(current_password) > 0) > > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > > + strlen(current_password) % 2; > > + /* password not set */ > > + else > > + security_area_size = sizeof(u32) * 2; > > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; > > + > > + buffer = kzalloc(buffer_size, GFP_KERNEL); > > + if (!buffer) > > + return -ENOMEM; > > + > > + /* build security area */ > > + if (strlen(current_password) > 0) > > + populate_security_buffer(buffer, current_password); > > + > > + name_len = buffer + security_area_size; > > + attribute_name = name_len + sizeof(u16); > > + value_len = attribute_name + strlen(a_name)*2; > > + attribute_value = value_len + sizeof(u16); > > + > > + /* turn into UTF16 strings, no NULL terminator */ > > + *name_len = strlen(a_name)*2; > > + *value_len = strlen(a_value)*2; > > + for (i = 0; i < strlen(a_name); i++) > > + attribute_name[i*2] = a_name[i]; > > + for (i = 0; i < strlen(a_value); i++) > > + attribute_value[i*2] = a_value[i]; > > + > > + mutex_lock(&call_mutex); > > + priv = get_first_interface_priv(); > > + if (!priv) { > > + ret = -ENODEV; > > + pr_err(DRIVER_NAME ": no WMI backend bound"); > > + goto out_set_attribute; > > + } > > + > > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > > + SETATTRIBUTE_METHOD_ID); > > + if (ret == -EOPNOTSUPP) > > + dev_err(&priv->wdev->dev, "admin password must be configured"); > > + else if (ret == -EACCES) > > + dev_err(&priv->wdev->dev, "invalid password"); > > + > > + priv->pending_changes = 1; > > +out_set_attribute: > > + kfree(buffer); > > + mutex_unlock(&call_mutex); > > + > > + return ret; > > +} > > + > > +/** > > + * set_bios_defaults() - Resets BIOS defaults > > + * @deftype: the type of BIOS value reset to issue. > > + * > > + * Resets BIOS defaults > > + **/ > > +int set_bios_defaults(u8 deftype) > > +{ > > + int ret = -1; > > + u8 *defaultType; > > + char *current_password, *buffer; > > + size_t security_area_size; > > + size_t integer_area_size = sizeof(u8); > > + size_t buffer_size; > > + struct wmi_interface_priv *priv; > > + > > + /* look up if user set a password for the requests */ > > + current_password = get_current_password("Admin"); > > + if (!current_password) > > + return -ENODEV; > > + > > + /* password is set */ > > + if (strlen(current_password) > 0) > > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > > + strlen(current_password) % 2; > > + /* password not set */ > > + else > > + security_area_size = sizeof(u32) * 2; > > + > > + buffer_size = security_area_size + integer_area_size; > > + buffer = kzalloc(buffer_size, GFP_KERNEL); > > + if (!buffer) > > + return -ENOMEM; > > + > > + /* build security area */ > > + if (strlen(current_password) > 0) > > + populate_security_buffer(buffer, current_password); > > + > > + mutex_lock(&call_mutex); > > + priv = get_first_interface_priv(); > > + if (!priv) { > > + ret = -ENODEV; > > + pr_err(DRIVER_NAME ": no WMI backend bound"); > > + goto out_bios_defaults; > > + } > > + > > + defaultType = buffer + security_area_size; > > + *defaultType = deftype; > > + > > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > > + SETBIOSDEFAULTS_METHOD_ID); > > + if (ret) > > + dev_err(&priv->wdev->dev, "reset BIOS defaults failed: %d", ret); > > + priv->pending_changes = 1; > > +out_bios_defaults: > > + kfree(buffer); > > + mutex_unlock(&call_mutex); > > + > > + return ret; > > +} > > + > > +static int dell_wmi_bios_attr_set_interface_probe(struct wmi_device *wdev, > > const void *context) > > +{ > > + struct wmi_interface_priv *priv; > > + > > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > > + GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + priv->wdev = wdev; > > + dev_set_drvdata(&wdev->dev, priv); > > + mutex_lock(&list_mutex); > > + list_add_tail(&priv->list, &interface_list); > > + mutex_unlock(&list_mutex); > > + return 0; > > +} > > + > > +static int dell_wmi_bios_attr_set_interface_remove(struct wmi_device *wdev) > > +{ > > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > > + > > + mutex_lock(&call_mutex); > > + mutex_lock(&list_mutex); > > + list_del(&priv->list); > > + mutex_unlock(&list_mutex); > > + mutex_unlock(&call_mutex); > > + return 0; > > +} > > + > > +static const struct wmi_device_id dell_wmi_bios_attr_set_interface_id_table[] > > = { > > + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, > > + { }, > > +}; > > +static struct wmi_driver dell_wmi_bios_attr_set_interface_driver = { > > + .driver = { > > + .name = DRIVER_NAME"-set" > > + }, > > + .probe = dell_wmi_bios_attr_set_interface_probe, > > + .remove = dell_wmi_bios_attr_set_interface_remove, > > + .id_table = dell_wmi_bios_attr_set_interface_id_table, > > +}; > > + > > +int init_dell_wmi_bios_attr_set_interface(void) > > +{ > > + return wmi_driver_register(&dell_wmi_bios_attr_set_interface_driver); > > +} > > + > > +void exit_dell_wmi_bios_attr_set_interface(void) > > +{ > > + wmi_driver_unregister(&dell_wmi_bios_attr_set_interface_driver); > > +} > > + > > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_set_interface_id_table); > > 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..a2d8ae291d5c > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-enum-attributes.c > > @@ -0,0 +1,207 @@ > > +// 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" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +/* kept variable names same as in sysfs file name for sysfs_show macro > > definition */ > > +struct enumeration_data { > > + char attribute_name[MAX_BUFF]; > > + char display_name_language_code[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_value_count; > > + char possible_value[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; > > + > > + 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) > > +{ > > + int ret = -EINVAL; > > + char *options, *tmp, *p; > > + > > + options = tmp = kstrdup((enumeration_data[instance_id].possible_value), > > 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_value, enumeration); > > +struct kobj_attribute poss_val = > > + __ATTR_RO(possible_value); > > + > > +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, > > + 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 i, next_obj; > > + int retval = sysfs_create_group(attr_name_kobj, > > &enumeration_attr_group); > > + > > + if (retval) > > + goto out; > > + > > + mutex_lock(&call_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_value_count = > > + (uintptr_t) enumeration_obj[++next_obj].string.pointer; > > + > > + for (i = 0; i < enumeration_data[instance_id].possible_value_count; i++) > > { > > + strcat(enumeration_data[instance_id].possible_value, > > + enumeration_obj[++next_obj].string.pointer); > > + strcat(enumeration_data[instance_id].possible_value, ";"); > > + } > > + mutex_unlock(&call_mutex); > > + > > +out: > > + return retval; > > +} > > + > > +/** > > + * exit_enum_attributes() - Clear all attribute data > > + * @kset: The kset to free > > + * > > + * Clears all data allocated for this group of attributes > > + **/ > > +void exit_enum_attributes(struct kset *kset) > > +{ > > + struct kobject *pos, *next; > > + > > + mutex_lock(&list_mutex); > > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > > + sysfs_remove_group(pos, &enumeration_attr_group); > > + } > > + mutex_unlock(&list_mutex); > > + mutex_lock(&call_mutex); > > + kfree(enumeration_data); > > + mutex_unlock(&call_mutex); > > +} > > diff --git a/drivers/platform/x86/dell-wmi-int-attributes.c > > b/drivers/platform/x86/dell-wmi-int-attributes.c > > new file mode 100644 > > index 000000000000..222971be2b48 > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-int-attributes.c > > @@ -0,0 +1,188 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Functions corresponding to integer type attributes under BIOS Integer GUID > > for use with > > + * dell-wmi-sysman > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#include "dell-wmi-sysman-attributes.h" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +enum int_properties {LOWER_BOUND = 6, UPPER_BOUND, SCALAR_INCR}; > > + > > +/* kept variable names same as in sysfs file name for sysfs_show macro > > definition */ > > +struct integer_data { > > + char attribute_name[MAX_BUFF]; > > + char display_name_language_code[MAX_BUFF]; > > + char display_name[MAX_BUFF]; > > + int default_value; > > + int current_value; > > + char modifier[MAX_BUFF]; > > + int lower_bound; > > + int upper_bound; > > + int scalar_increment; > > +}; > > + > > +static struct integer_data *integer_data; > > +static int integer_instances_count; > > +get_instance_id(integer); > > + > > +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_integer_instance_id(kobj); > > + if (instance_id >= 0) { > > + union acpi_object *obj; > > + > > + obj = get_wmiobj_pointer(instance_id, > > DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > > + if (!obj) > > + return -AE_ERROR; > > + integer_data[instance_id].current_value = > > + (uintptr_t)obj->package.elements[CURRENT_VAL].string.pointer; > > + kfree(obj); > > + return sprintf(buf, "%d\n", > > integer_data[instance_id].current_value); > > + } > > + return -EIO; > > +} > > + > > +/** > > + * validate_integer_input() - Validate input of current_value against lower > > and upper bound > > + * @instance_id: The instance on which input is validated > > + * @buf: Input value > > + **/ > > +int validate_integer_input(int instance_id, const char *buf) > > +{ > > + int in_val; > > + int ret = -EINVAL; > > + > > + if (kstrtoint(buf, 0, &in_val)) > > + return -EINVAL; > > + if ((in_val >= integer_data[instance_id].lower_bound) && > > + (in_val <= integer_data[instance_id].upper_bound)) > > + ret = 0; > > + > > + return ret; > > +} > > + > > +attribute_s_property_show(display_name_language_code, integer); > > +static struct kobj_attribute integer_displ_langcode = > > + __ATTR_RO(display_name_language_code); > > + > > +attribute_s_property_show(display_name, integer); > > +struct kobj_attribute integer_displ_name = > > + __ATTR_RO(display_name); > > + > > +attribute_n_property_show(default_value, integer); > > +struct kobj_attribute integer_default_val = > > + __ATTR_RO(default_value); > > + > > +attribute_property_store(current_value, integer); > > +struct kobj_attribute integer_current_val = > > + __ATTR_RW(current_value); > > + > > +attribute_s_property_show(modifier, integer); > > +struct kobj_attribute integer_modifier = > > + __ATTR_RO(modifier); > > + > > +attribute_n_property_show(lower_bound, integer); > > +struct kobj_attribute integer_lower_bound = > > + __ATTR_RO(lower_bound); > > + > > +attribute_n_property_show(upper_bound, integer); > > +struct kobj_attribute integer_upper_bound = > > + __ATTR_RO(upper_bound); > > + > > +attribute_n_property_show(scalar_increment, integer); > > +struct kobj_attribute integer_scalar_increment = > > + __ATTR_RO(scalar_increment); > > + > > +static struct attribute *integer_attrs[] = { > > + &integer_displ_langcode.attr, > > + &integer_displ_name.attr, > > + &integer_default_val.attr, > > + &integer_current_val.attr, > > + &integer_modifier.attr, > > + &integer_lower_bound.attr, > > + &integer_upper_bound.attr, > > + &integer_scalar_increment.attr, > > + NULL, > > +}; > > + > > +static const struct attribute_group integer_attr_group = { > > + .attrs = integer_attrs, > > +}; > > + > > +int alloc_int_data(void) > > +{ > > + int ret = 0; > > + > > + integer_instances_count = > > get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > > + integer_data = kzalloc((sizeof(struct integer_data) * > > integer_instances_count), GFP_KERNEL); > > + if (!integer_data) > > + ret = -ENOMEM; > > + return ret; > > +} > > + > > +/** > > + * populate_enum_data() - Populate all properties of an instance under > > integer attribute > > + * @integer_obj: ACPI object with integer data > > + * @instance_id: The instance to enumerate > > + * @attr_name_kobj: The parent kernel object > > + **/ > > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > > + struct kobject *attr_name_kobj) > > +{ > > + int retval = sysfs_create_group(attr_name_kobj, &integer_attr_group); > > + > > + if (retval) > > + goto out; > > + > > + mutex_lock(&call_mutex); > > + strncpy_attr(integer_data[instance_id].attribute_name, > > + integer_obj[ATTR_NAME].string.pointer); > > + strncpy_attr(integer_data[instance_id].display_name_language_code, > > + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); > > + strncpy_attr(integer_data[instance_id].display_name, > > + integer_obj[DISPLAY_NAME].string.pointer); > > + integer_data[instance_id].default_value = > > + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; > > + integer_data[instance_id].current_value = > > + (uintptr_t)integer_obj[CURRENT_VAL].string.pointer; > > + strncpy_attr(integer_data[instance_id].modifier, > > integer_obj[MODIFIER].string.pointer); > > + integer_data[instance_id].lower_bound = > > + (uintptr_t)integer_obj[LOWER_BOUND].string.pointer; > > + integer_data[instance_id].upper_bound = > > + (uintptr_t)integer_obj[UPPER_BOUND].string.pointer; > > + integer_data[instance_id].scalar_increment = > > + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; > > + mutex_unlock(&call_mutex); > > + > > +out: > > + return retval; > > +} > > + > > +/** > > + * exit_int_attributes() - Clear all attribute data > > + * @kset: The kset to free > > + * > > + * Clears all data allocated for this group of attributes > > + **/ > > +void exit_int_attributes(struct kset *kset) > > +{ > > + struct kobject *pos, *next; > > + > > + mutex_lock(&list_mutex); > > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > > + sysfs_remove_group(pos, &integer_attr_group); > > + } > > + mutex_unlock(&list_mutex); > > + mutex_lock(&call_mutex); > > + kfree(integer_data); > > + mutex_unlock(&call_mutex); > > +} > > 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..d430ecf2387b > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c > > @@ -0,0 +1,161 @@ > > +// 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" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +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; > > +}; > > + > > +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; > > + > > + 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; > > + > > + ret = set_current_password(kobj->name, buf); > > + 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); > > + > > +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, > > + 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(&call_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; > > + mutex_unlock(&call_mutex); > > + > > +out: > > + return retval; > > +} > > + > > +/** > > + * exit_po_attributes() - Clear all attribute data > > + * @kset: The kset to free > > + * > > + * Clears all data allocated for this group of attributes > > + **/ > > +void exit_po_attributes(struct kset *kset) > > +{ > > + struct kobject *pos, *next; > > + > > + mutex_lock(&list_mutex); > > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > > + sysfs_remove_group(pos, &po_attr_group); > > + } > > + mutex_unlock(&list_mutex); > > + mutex_lock(&call_mutex); > > + kfree(po_data); > > + mutex_unlock(&call_mutex); > > +} > > 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..9d7ac36e232f > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c > > @@ -0,0 +1,230 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Functions corresponding to SET password methods under BIOS attributes > > interface GUID > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#include <linux/wmi.h> > > +#include "dell-wmi-sysman-attributes.h" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +struct wmi_interface_priv { > > + struct list_head list; > > + struct wmi_device *wdev; > > + struct device *child; > > + char current_admin_password[MAX_BUFF]; > > + char current_system_password[MAX_BUFF]; > > +}; > > +static LIST_HEAD(interface_list); > > + > > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > > +{ > > + return list_first_entry_or_null(&interface_list, > > + struct wmi_interface_priv, > > + list); > > +} > > + > > +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); > > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > > + return map_wmi_error(ret); > > +} > > + > > +/** > > + * get_current_password() - Get the current stored password value > > + * @password_type: The type of password to store > > + **/ > > +char *get_current_password(const char *password_type) > > +{ > > + struct wmi_interface_priv *priv = get_first_interface_priv(); > > + > > + if (!priv) { > > + pr_err(DRIVER_NAME ": no WMI backend bound"); > > + return NULL; > > + } > > + if (strcmp(password_type, "Admin") == 0) > > + return priv->current_admin_password; > > + if (strcmp(password_type, "System") == 0) > > + return priv->current_system_password; > > + dev_err(&priv->wdev->dev, "unknown password type %s", password_type); > > + return NULL; > > +} > > + > > +/** > > + * set_current_password() - Store current password > > + * @password_type: The type of password to store > > + * @current: The current value > > + * > > + * Sets the current value of the password. > > + * This is used for: > > + * - Resetting password > > + * - Basis for any functions requiring password to execute > > + **/ > > +int set_current_password(const char *password_type, const char *cur) > > +{ > > + char *current_password = get_current_password(password_type); > > + > > + if (!current_password) > > + return -ENODEV; > > + strncpy(current_password, cur, (strlen(cur) + 1)); > > + return 0; > > +} > > + > > +/** > > + * set_new_password() - Sets a system admin password > > + * @password_type: The type of password to set > > + * @new: The new password > > + * > > + * Sets the password using plaintext interface > > + **/ > > +int set_new_password(const char *password_type, const char *new) > > +{ > > + int ret = -1; > > + int i; > > + u8 *type_len, *old_len, *new_len; > > + char *current_password, *type_value, *old_value, *new_value; > > + size_t security_area_size; > > + size_t string_area_size; > > + size_t buffer_size; > > + struct wmi_interface_priv *priv; > > + char *buffer; > > + > > + mutex_lock(&call_mutex); > > + priv = get_first_interface_priv(); > > + if (!priv) { > > + ret = -ENODEV; > > + pr_err(DRIVER_NAME ": no WMI backend bound"); > > + goto out_close_mutex; > > + } > > + current_password = get_current_password(password_type); > > + if (!current_password) { > > + ret = -ENODEV; > > + goto out_close_mutex; > > + } > > + /* password is set */ > > + if (strlen(current_password) > 0) > > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > > + strlen(current_password) % 2; > > + /* password not set */ > > + else > > + security_area_size = sizeof(u32) * 2; > > + string_area_size = (strlen(password_type) + strlen(current_password) + > > strlen(new))*2; > > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 3; > > + > > + buffer = kzalloc(buffer_size, GFP_KERNEL); > > + > > + if (!buffer) { > > + ret = -ENOMEM; > > + goto out_close_mutex; > > + } > > + > > + /* build security area */ > > + if (strlen(current_password) > 0) > > + populate_security_buffer(buffer, current_password); > > + > > + type_len = buffer + security_area_size; > > + type_value = type_len + sizeof(u16); > > + old_len = type_value + strlen(password_type)*2; > > + old_value = old_len + sizeof(u16); > > + new_len = old_value + strlen(current_password)*2; > > + new_value = new_len + sizeof(u16); > > + > > + /* turn into UTF16 strings, no NULL terminator */ > > + *type_len = strlen(password_type)*2; > > + *old_len = strlen(current_password)*2; > > + *new_len = strlen(new)*2; > > + for (i = 0; i < strlen(password_type); i++) > > + type_value[i*2] = password_type[i]; > > + for (i = 0; i < strlen(current_password); i++) > > + old_value[i*2] = current_password[i]; > > + for (i = 0; i < strlen(new); i++) > > + new_value[i*2] = new[i]; > > + > > + ret = call_password_interface(priv->wdev, buffer, buffer_size); > > + /* update current password so commands work after reset */ > > + if (!ret) > > + ret = set_current_password(password_type, new); > > + /* explain to user the detailed failure reason */ > > + else if (ret == -EOPNOTSUPP) > > + dev_err(&priv->wdev->dev, "admin password must be configured"); > > + else if (ret == -EACCES) > > + dev_err(&priv->wdev->dev, "invalid password"); > > + kfree(buffer); > > + > > +out_close_mutex: > > + mutex_unlock(&call_mutex); > > + > > + return ret; > > +} > > + > > +static int dell_wmi_bios_attr_pass_interface_probe(struct wmi_device *wdev, > > const void *context) > > +{ > > + struct wmi_interface_priv *priv; > > + > > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > > + GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + priv->wdev = wdev; > > + dev_set_drvdata(&wdev->dev, priv); > > + mutex_lock(&list_mutex); > > + list_add_tail(&priv->list, &interface_list); > > + mutex_unlock(&list_mutex); > > + return 0; > > +} > > + > > +static int dell_wmi_bios_attr_pass_interface_remove(struct wmi_device *wdev) > > +{ > > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > > + > > + mutex_lock(&call_mutex); > > + mutex_lock(&list_mutex); > > + list_del(&priv->list); > > + mutex_unlock(&list_mutex); > > + mutex_unlock(&call_mutex); > > + return 0; > > +} > > + > > +static const struct wmi_device_id > > dell_wmi_bios_attr_pass_interface_id_table[] = { > > + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, > > + { }, > > +}; > > +static struct wmi_driver dell_wmi_bios_attr_pass_interface_driver = { > > + .driver = { > > + .name = DRIVER_NAME"-password" > > + }, > > + .probe = dell_wmi_bios_attr_pass_interface_probe, > > + .remove = dell_wmi_bios_attr_pass_interface_remove, > > + .id_table = dell_wmi_bios_attr_pass_interface_id_table, > > +}; > > + > > +int init_dell_wmi_bios_attr_pass_interface(void) > > +{ > > + return wmi_driver_register(&dell_wmi_bios_attr_pass_interface_driver); > > +} > > + > > +void exit_dell_wmi_bios_attr_pass_interface(void) > > +{ > > + wmi_driver_unregister(&dell_wmi_bios_attr_pass_interface_driver); > > +} > > + > > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_pass_interface_id_table); > > diff --git a/drivers/platform/x86/dell-wmi-string-attributes.c > > b/drivers/platform/x86/dell-wmi-string-attributes.c > > new file mode 100644 > > index 000000000000..562d09055dd1 > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-string-attributes.c > > @@ -0,0 +1,170 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Functions corresponding to string type attributes under BIOS String GUID > > for use with > > + * dell-wmi-sysman > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#include "dell-wmi-sysman-attributes.h" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +enum string_properties {MIN_LEN = 6, MAX_LEN}; > > + > > +/* kept variable names same as in sysfs file name for sysfs_show macro > > definition */ > > +struct str_data { > > + char attribute_name[MAX_BUFF]; > > + char display_name_language_code[MAX_BUFF]; > > + char display_name[MAX_BUFF]; > > + char default_value[MAX_BUFF]; > > + char current_value[MAX_BUFF]; > > + char modifier[MAX_BUFF]; > > + int min_length; > > + int max_length; > > +}; > > + > > +static struct str_data *str_data; > > +static int str_instances_count; > > +get_instance_id(str); > > + > > +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_str_instance_id(kobj); > > + if (instance_id >= 0) { > > + union acpi_object *obj; > > + > > + obj = get_wmiobj_pointer(instance_id, > > DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > > + if (!obj) > > + return -AE_ERROR; > > + strncpy_attr(str_data[instance_id].current_value, > > + obj->package.elements[CURRENT_VAL].string.pointer); > > + kfree(obj); > > + return sprintf(buf, "%s\n", str_data[instance_id].current_value); > > + } > > + return -EIO; > > +} > > + > > +/** > > + * validate_str_input() - Validate input of current_value against min and max > > lengths > > + * @instance_id: The instance on which input is validated > > + * @buf: Input value > > + **/ > > +int validate_str_input(int instance_id, const char *buf) > > +{ > > + int in_len = strlen(buf); > > + > > + if ((in_len >= str_data[instance_id].min_length) && > > + (in_len <= str_data[instance_id].max_length)) > > + return 0; > > + > > + return -EINVAL; > > +} > > + > > +attribute_s_property_show(display_name_language_code, str); > > +static struct kobj_attribute str_displ_langcode = > > + __ATTR_RO(display_name_language_code); > > + > > +attribute_s_property_show(display_name, str); > > +struct kobj_attribute str_displ_name = > > + __ATTR_RO(display_name); > > + > > +attribute_s_property_show(default_value, str); > > +struct kobj_attribute str_default_val = > > + __ATTR_RO(default_value); > > + > > +attribute_property_store(current_value, str); > > +struct kobj_attribute str_current_val = > > + __ATTR_RW(current_value); > > + > > +attribute_s_property_show(modifier, str); > > +struct kobj_attribute str_modifier = > > + __ATTR_RO(modifier); > > + > > +attribute_n_property_show(min_length, str); > > +struct kobj_attribute str_min_length = > > + __ATTR_RO(min_length); > > + > > +attribute_n_property_show(max_length, str); > > +struct kobj_attribute str_max_length = > > + __ATTR_RO(max_length); > > + > > +static struct attribute *str_attrs[] = { > > + &str_displ_langcode.attr, > > + &str_displ_name.attr, > > + &str_default_val.attr, > > + &str_current_val.attr, > > + &str_modifier.attr, > > + &str_min_length.attr, > > + &str_max_length.attr, > > + NULL, > > +}; > > + > > +static const struct attribute_group str_attr_group = { > > + .attrs = str_attrs, > > +}; > > + > > +int alloc_str_data(void) > > +{ > > + int ret = 0; > > + > > + str_instances_count = > > get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > > + str_data = kzalloc((sizeof(struct str_data) * str_instances_count), > > GFP_KERNEL); > > + if (!str_data) > > + ret = -ENOMEM; > > + return ret; > > +} > > + > > +/** > > + * populate_enum_data() - Populate all properties of an instance under string > > attribute > > + * @str_obj: ACPI object with integer data > > + * @instance_id: The instance to enumerate > > + * @attr_name_kobj: The parent kernel object > > + **/ > > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct > > kobject *attr_name_kobj) > > +{ > > + int retval = sysfs_create_group(attr_name_kobj, &str_attr_group); > > + > > + if (retval) > > + goto out; > > + > > + mutex_lock(&call_mutex); > > + strncpy_attr(str_data[instance_id].attribute_name, > > str_obj[ATTR_NAME].string.pointer); > > + strncpy_attr(str_data[instance_id].display_name_language_code, > > + str_obj[DISPL_NAME_LANG_CODE].string.pointer); > > + strncpy_attr(str_data[instance_id].display_name, > > str_obj[DISPLAY_NAME].string.pointer); > > + strncpy_attr(str_data[instance_id].default_value, > > str_obj[DEFAULT_VAL].string.pointer); > > + strncpy_attr(str_data[instance_id].current_value, > > str_obj[CURRENT_VAL].string.pointer); > > + strncpy_attr(str_data[instance_id].modifier, > > str_obj[MODIFIER].string.pointer); > > + str_data[instance_id].min_length = > > (uintptr_t)str_obj[MIN_LEN].string.pointer; > > + str_data[instance_id].max_length = (uintptr_t) > > str_obj[MAX_LEN].string.pointer; > > + mutex_unlock(&call_mutex); > > + > > +out: > > + return retval; > > +} > > + > > +/** > > + * exit_str_attributes() - Clear all attribute data > > + * @kset: The kset to free > > + * > > + * Clears all data allocated for this group of attributes > > + **/ > > +void exit_str_attributes(struct kset *kset) > > +{ > > + struct kobject *pos, *next; > > + > > + mutex_lock(&list_mutex); > > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > > + sysfs_remove_group(pos, &str_attr_group); > > + } > > + mutex_unlock(&list_mutex); > > + mutex_lock(&call_mutex); > > + kfree(str_data); > > + mutex_unlock(&call_mutex); > > +} > > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.c > > b/drivers/platform/x86/dell-wmi-sysman-attributes.c > > new file mode 100644 > > index 000000000000..485545ab6c8b > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.c > > @@ -0,0 +1,575 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Common methods for use with dell-wmi-sysman > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/platform_device.h> > > +#include <linux/dmi.h> > > +#include <linux/wmi.h> > > +#include "dell-wmi-sysman-attributes.h" > > + > > +static DEFINE_MUTEX(call_mutex); > > +static DEFINE_MUTEX(list_mutex); > > + > > +#define MAX_TYPES 4 > > + > > +static struct platform_device *platform_device; > > + > > +static struct platform_driver platform_driver = { > > + .driver = { > > + .name = DRIVER_NAME, > > + }, > > +}; > > + > > +/* attribute directory under platform dev */ > > +struct kset *main_dir_kset; > > +/* subtypes of attributes */ > > +struct kset *type_dir_kset[MAX_TYPES]; > > + > > +/* reset bios to defaults */ > > +static const char * const reset_types[] = {"builtinsafe", "lastknowngood", > > "factory", "custom"}; > > +static int reset_option = -1; > > + > > +/** > > + * populate_security_buffer() - builds a security buffer for authentication > > scheme > > + * @buffer: the buffer to populate > > + * @authentication: the authentication content > > + * > > + * Currently only supported type is PLAIN TEXT > > + **/ > > +void populate_security_buffer(char *buffer, char *authentication) > > +{ > > + u32 *sectype = (u32 *) buffer; > > + u32 *seclen = sectype + 1; > > + char *auth = buffer + sizeof(u32)*2; > > + /* plain text */ > > + *sectype = 1; > > + *seclen = strlen(authentication); > > + strncpy(auth, authentication, *seclen); > > +} > > + > > +/** > > + * map_wmi_error() - map errors from WMI methods to kernel error codes > > + **/ > > +int map_wmi_error(int error_code) > > +{ > > + switch (error_code) { > > + case 0: > > + /* success */ > > + return 0; > > + case 1: > > + /* failed */ > > + return -EIO; > > + case 2: > > + /* invalid parameter */ > > + return -EINVAL; > > + case 3: > > + /* access denied */ > > + return -EACCES; > > + case 4: > > + /* not supported */ > > + return -EOPNOTSUPP; > > + case 5: > > + /* memory error */ > > + return -ENOMEM; > > + case 6: > > + /* protocol error */ > > + return -EPROTO; > > + } > > + /* unspecified error */ > > + return -EIO; > > +} > > + > > +/** > > + * reset_bios_show() - sysfs implementaton for read reset_bios > > + * @kobj: Kernel object for this attribute > > + * @attr: Kernel object attribute > > + * @buf: The buffer to display to userspace > > + **/ > > +static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute > > *attr, char *buf) > > +{ > > + int i; > > + char *start = buf; > > + > > + for (i = 0; i < MAX_TYPES; i++) { > > + if (i == reset_option) > > + buf += sprintf(buf, "[%s] ", reset_types[i]); > > + else > > + buf += sprintf(buf, "%s ", reset_types[i]); > > + } > > + buf += sprintf(buf, "\n"); > > + return buf-start; > > +} > > + > > +/** > > + * reset_bios_store() - sysfs implementaton for write reset_bios > > + * @kobj: Kernel object for this attribute > > + * @attr: Kernel object attribute > > + * @buf: The buffer from userspace > > + * @count: the size of the buffer from userspace > > + **/ > > +static ssize_t reset_bios_store(struct kobject *kobj, > > + struct kobj_attribute *attr, const char *buf, size_t > > count) > > +{ > > + int len, ret, i; > > + int type = -1; > > + char *p; > > + > > + p = memchr(buf, '\n', count); > > + if (p != NULL) > > + *p = '\0'; > > + len = p ? p - buf : count; > > + > > + for (i = 0; i < MAX_TYPES; i++) { > > + if (len == strlen(reset_types[i]) > > + && !strncmp(buf, reset_types[i], len)) { > > + type = i; > > + break; > > + } > > + } > > + > > + if (type < 0 || type >= MAX_TYPES) { > > + ret = -EINVAL; > > + goto out; > > + } > > + > > + ret = set_bios_defaults(type); > > + dev_dbg(&platform_device->dev, "reset all attributes request type %d: > > %d", type, ret); > > + if (ret) { > > + ret = -EINVAL; > > + } else { > > + reset_option = type; > > + ret = count; > > + } > > + > > +out: > > + return ret; > > +} > > + > > +/** > > + * pending_reboot_show() - sysfs implementaton for read pending_reboot > > + * @kobj: Kernel object for this attribute > > + * @attr: Kernel object attribute > > + * @buf: The buffer to display to userspace > > + * > > + * Stores default value as 0 > > + * When current_value is changed this attribute is set to 1 to notify reboot > > may be required > > + **/ > > +static ssize_t pending_reboot_show(struct kobject *kobj, struct > > kobj_attribute *attr, > > + char *buf) > > +{ > > + return sprintf(buf, "%d\n", get_pending_changes()); > > +} > > + > > +static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); > > +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); > > + > > + > > +/** > > + * create_reset_bios() - Creates reset_bios and pending_reboot attributes > > + **/ > > +static int create_reset_bios(void) > > +{ > > + int ret = sysfs_create_file(&main_dir_kset->kobj, &reset_bios.attr); > > + > > + if (ret) { > > + dev_dbg(&platform_device->dev, "could not create reset_bios file"); > > + return ret; > > + } > > + > > + ret = sysfs_create_file(&main_dir_kset->kobj, &pending_reboot.attr); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "could not create > > changing_pending_reboot file"); > > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > > + } > > + return ret; > > +} > > + > > +static void release_reset_bios_data(void) > > +{ > > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > > + sysfs_remove_file(&main_dir_kset->kobj, &pending_reboot.attr); > > +} > > + > > +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, > > + char *buf) > > +{ > > + struct kobj_attribute *kattr; > > + ssize_t ret = -EIO; > > + > > + kattr = container_of(attr, struct kobj_attribute, attr); > > + if (kattr->show) > > + ret = kattr->show(kobj, kattr, buf); > > + return ret; > > +} > > + > > +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, > > + const char *buf, size_t count) > > +{ > > + struct kobj_attribute *kattr; > > + ssize_t ret = -EIO; > > + > > + kattr = container_of(attr, struct kobj_attribute, attr); > > + if (kattr->store) > > + ret = kattr->store(kobj, kattr, buf, count); > > + return ret; > > +} > > + > > +const struct sysfs_ops kobj_sysfs_ops = { > > + .show = kobj_attr_show, > > + .store = kobj_attr_store, > > +}; > > + > > +static void attr_name_release(struct kobject *kobj) > > +{ > > + kfree(kobj); > > +} > > + > > +static struct kobj_type attr_name_ktype = { > > + .release = attr_name_release, > > + .sysfs_ops = &kobj_sysfs_ops, > > +}; > > + > > +/** > > + * strncpy_attr - Copy a length-limited, NULL-terminated string with bound > > checks > > + * @dest: Where to copy the string to > > + * @src: Where to copy the string from > > + **/ > > +void strncpy_attr(char *dest, char *src) > > +{ > > + size_t len = strlen(src) + 1; > > + > > + if (len > 1 && len < MAX_BUFF) > > + strncpy(dest, src, len); > > +} > > + > > +/** > > + * get_wmiobj_pointer() - Get Content of WMI block for particular instance > > + * @instance_id: WMI instance ID > > + * @guid_string: WMI GUID (in str form) > > + * > > + * Fetches the content for WMI block (instance_id) under GUID (guid_string) > > + * Caller must kfree the return > > + **/ > > +union acpi_object *get_wmiobj_pointer(int instance_id, const char > > *guid_string) > > +{ > > + acpi_status status; > > + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; > > + > > + status = wmi_query_block(guid_string, instance_id, &out); > > + > > + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; > > +} > > + > > +/** > > + * get_instance_count() - Compute total number of instances under guid_string > > + * @guid_string: WMI GUID (in string form) > > + **/ > > +int get_instance_count(const char *guid_string) > > +{ > > + int i = 0; > > + union acpi_object *wmi_obj = NULL; > > + > > + do { > > + kfree(wmi_obj); > > + wmi_obj = get_wmiobj_pointer(i, guid_string); > > + i++; > > + } while (wmi_obj); > > + > > + return (i-1); > > +} > > + > > +/** > > + * alloc_attributes_data() - Allocate attributes data for a particular type > > + * @attr_type: Attribute type to allocate > > + **/ > > +static int alloc_attributes_data(int attr_type) > > +{ > > + int retval = 0; > > + char *type[MAX_TYPES] = {"enumeration", "integer", "string", > > "password"}; > > + > > + type_dir_kset[attr_type] = kset_create_and_add(type[attr_type], NULL, > > &main_dir_kset->kobj); > > + if (!type_dir_kset[attr_type]) > > + goto err_bios_attr; > > + > > + switch (attr_type) { > > + case ENUM: > > + retval = alloc_enum_data(); > > + break; > > + case INT: > > + retval = alloc_int_data(); > > + break; > > + case STR: > > + retval = alloc_str_data(); > > + break; > > + case PO: > > + retval = alloc_po_data(); > > + break; > > + default: > > + break; > > + } > > + > > + goto out; > > + > > +err_bios_attr: > > + kset_unregister(main_dir_kset); > > + > > +out: > > + return retval; > > +} > > + > > +/** > > + * destroy_attribute_objs() - Free a kset of kobjects > > + * @kset: The kset to destroy > > + * > > + * Fress kobjects created for each attribute_name under attribute type kset > > + **/ > > +static void destroy_attribute_objs(struct kset *kset) > > +{ > > + struct kobject *pos, *next; > > + > > + mutex_lock(&list_mutex); > > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > > + kobject_put(pos); > > + } > > + mutex_unlock(&list_mutex); > > +} > > + > > +/** > > + * release_attributes_data() - Clean-up all sysfs directories and files > > created > > + **/ > > +static void release_attributes_data(void) > > +{ > > + release_reset_bios_data(); > > + > > + exit_enum_attributes(type_dir_kset[ENUM]); > > + exit_int_attributes(type_dir_kset[INT]); > > + exit_str_attributes(type_dir_kset[STR]); > > + exit_po_attributes(type_dir_kset[PO]); > > + > > + mutex_lock(&call_mutex); > > + if (main_dir_kset) { > > + int i; > > + > > + /* unregister all 4 types ENUM,INT,STR and PO */ > > + for (i = ENUM; i <= PO; i++) { > > + destroy_attribute_objs(type_dir_kset[i]); > > + kset_unregister(type_dir_kset[i]); > > + } > > + kset_unregister(main_dir_kset); > > + } > > + mutex_unlock(&call_mutex); > > +} > > + > > +/** > > + * init_bios_attributes() - Initialize all attributes for a type > > + * @attr_type: The attribute type to initialize > > + * @guid: The WMI GUID associated with this type to initialize > > + * > > + * Initialiaze all 4 types of attributes enumeration, integer, string and > > password object. > > + * Populates each attrbute typ's respective properties under sysfs files > > + **/ > > +static int init_bios_attributes(int attr_type, const char *guid) > > +{ > > + union acpi_object *obj = NULL; > > + union acpi_object *elements; > > + int retval = 0; > > + int instance_id = 0; > > + struct kobject *attr_name_kobj; //individual attribute names > > + > > + retval = alloc_attributes_data(attr_type); > > + if (retval) > > + return retval; > > + obj = get_wmiobj_pointer(instance_id, guid); > > + if (!obj) > > + return -ENODEV; > > + elements = obj->package.elements; > > + > > + while (elements) { > > + /* sanity checking */ > > + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { > > + dev_dbg(&platform_device->dev, "empty attribute found"); > > + goto nextobj; > > + } > > + if (kset_find_obj(type_dir_kset[attr_type], > > elements[ATTR_NAME].string.pointer)) { > > + dev_dbg(&platform_device->dev, "duplicate attribute name found > > - %s", > > + elements[ATTR_NAME].string.pointer); > > + goto nextobj; > > + } > > + > > + /* build attribute */ > > + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); > > + if (!attr_name_kobj) > > + goto err_attr_init; > > + > > + attr_name_kobj->kset = type_dir_kset[attr_type]; > > + > > + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, > > NULL, "%s", > > + elements[ATTR_NAME].string.pointer); > > + if (retval) { > > + kobject_put(attr_name_kobj); > > + goto err_attr_init; > > + } > > + > > + /* enumerate all of this attribute */ > > + switch (attr_type) { > > + case ENUM: > > + retval = populate_enum_data(elements, instance_id, > > attr_name_kobj); > > + break; > > + case INT: > > + retval = populate_int_data(elements, instance_id, > > attr_name_kobj); > > + break; > > + case STR: > > + retval = populate_str_data(elements, instance_id, > > attr_name_kobj); > > + break; > > + case PO: > > + retval = populate_po_data(elements, instance_id, > > attr_name_kobj); > > + break; > > + default: > > + break; > > + } > > + > > + if (retval) { > > + dev_dbg(&platform_device->dev, "failed to populate %s", > > + elements[ATTR_NAME].string.pointer); > > + goto err_attr_init; > > + } > > + > > +nextobj: > > + kfree(obj); > > + instance_id++; > > + obj = get_wmiobj_pointer(instance_id, guid); > > + elements = obj ? obj->package.elements : NULL; > > + } > > + > > + goto out; > > + > > +err_attr_init: > > + release_attributes_data(); > > + kfree(obj); > > +out: > > + return retval; > > +} > > + > > +static int __init init_dell_bios_attrib_wmi(void) > > +{ > > + int ret = 0; > > + > > + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && > > + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { > > + pr_err("Unable to run on non-Dell system\n"); > > + return -ENODEV; > > + } > > + > > + ret = platform_driver_register(&platform_driver); > > + if (ret) > > + goto fail_platform_driver; > > + > > + platform_device = platform_device_alloc(DRIVER_NAME, -1); > > + if (!platform_device) { > > + ret = -ENOMEM; > > + goto fail_platform_device_alloc; > > + } > > + > > + ret = platform_device_add(platform_device); > > + if (ret) > > + goto fail_platform_device_add; > > + > > + main_dir_kset = kset_create_and_add("attributes", NULL, > > &platform_device->dev.kobj); > > + if (!main_dir_kset) { > > + ret = -ENOMEM; > > + goto fail_platform_device_add; > > + } > > + > > + ret = init_dell_wmi_bios_attr_set_interface(); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to initialize set > > interface"); > > + goto fail_kset; > > + } > > + > > + ret = init_dell_wmi_bios_attr_pass_interface(); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to initialize pass > > interface"); > > + goto fail_set_interface; > > + } > > + > > + ret = create_reset_bios(); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "could not create reset BIOS > > attribute"); > > + goto fail_pass_interface; > > + } > > + > > + ret = init_bios_attributes(ENUM, > > DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to populate enumeration type > > attributes"); > > + goto fail_create_group; > > + } > > + > > + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to populate integer type > > attributes"); > > + goto fail_create_group; > > + } > > + > > + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to populate string type > > attributes"); > > + goto fail_create_group; > > + } > > + > > + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); > > + if (ret) { > > + dev_dbg(&platform_device->dev, "failed to populate pass object type > > attributes"); > > + goto fail_create_group; > > + } > > + > > + return 0; > > + > > +fail_create_group: > > + release_attributes_data(); > > + platform_device_del(platform_device); > > + > > +fail_pass_interface: > > + exit_dell_wmi_bios_attr_pass_interface(); > > + > > +fail_set_interface: > > + exit_dell_wmi_bios_attr_set_interface(); > > + > > +fail_kset: > > + kset_unregister(main_dir_kset); > > + > > +fail_platform_device_add: > > + platform_device_put(platform_device); > > + > > +fail_platform_device_alloc: > > + platform_driver_unregister(&platform_driver); > > + > > +fail_platform_driver: > > + return ret; > > +} > > + > > +static void __exit exit_dell_bios_attrib_wmi(void) > > +{ > > + release_attributes_data(); > > + mutex_lock(&call_mutex); > > + exit_dell_wmi_bios_attr_set_interface(); > > + exit_dell_wmi_bios_attr_pass_interface(); > > + if (platform_device) { > > + platform_device_unregister(platform_device); > > + platform_driver_unregister(&platform_driver); > > + } > > + mutex_unlock(&call_mutex); > > +} > > + > > +module_init(init_dell_bios_attrib_wmi); > > +module_exit(exit_dell_bios_attrib_wmi); > > + > > +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); > > +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); > > +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); > > +MODULE_DESCRIPTION("Dell platform setting control interface"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.h > > b/drivers/platform/x86/dell-wmi-sysman-attributes.h > > new file mode 100644 > > index 000000000000..bb5053b91ec7 > > --- /dev/null > > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.h > > @@ -0,0 +1,125 @@ > > +/* SPDX-License-Identifier: GPL-2.0 > > + * Definitions for kernel modules using Dell WMI System Management Driver > > + * > > + * Copyright (c) 2020 Dell Inc. > > + */ > > + > > +#ifndef _DELL_WMI_BIOS_ATTR_H_ > > +#define _DELL_WMI_BIOS_ATTR_H_ > > + > > +#include <linux/wmi.h> > > +#include <linux/device.h> > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/platform_device.h> > > +#include <linux/capability.h> > > + > > +#define DRIVER_NAME "dell-wmi-sysman" > > +#define MAX_BUFF 512 > > + > > +#define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784- > > A11E-8A06684B9BF5" > > +#define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784- > > A11E-8A06684B9BFA" > > +#define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784- > > A11E-8A06684B9BF9" > > +#define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719- > > 97D7-6AD24108BFD4" > > +#define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C- > > 4784-A11E-8A06684B9BF4" > > +#define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214- > > A1C6-1F884B1A892A" > > + > > +enum { ENUM, INT, STR, PO }; > > + > > +enum { > > + ATTR_NAME, > > + DISPL_NAME_LANG_CODE, > > + DISPLAY_NAME, > > + DEFAULT_VAL, > > + CURRENT_VAL, > > + MODIFIER > > +}; > > + > > +#define get_instance_id(type) \ > > +int get_##type##_instance_id(struct kobject *kobj) \ > > +{ \ > > + int i; \ > > + for (i = 0; i <= type##_instances_count; i++) { \ > > + if (!(strcmp(kobj->name, type##_data[i].attribute_name))) \ > > + return i; \ > > + } \ > > + return -EIO; \ > > +} > > + > > +#define attribute_s_property_show(name, type) \ > > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, > > \ > > + char *buf) \ > > +{ \ > > + int i = get_##type##_instance_id(kobj); \ > > + if (i >= 0) \ > > + return sprintf(buf, "%s\n", type##_data[i].name); \ > > + return 0; \ > > +} > > + > > +#define attribute_n_property_show(name, type) \ > > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, > > \ > > + char *buf) \ > > +{ \ > > + int i = get_##type##_instance_id(kobj); \ > > + if (i >= 0) \ > > + return sprintf(buf, "%d\n", type##_data[i].name); \ > > + return 0; \ > > +} > > + > > +#define attribute_property_store(curr_val, type) \ > > +static ssize_t curr_val##_store(struct kobject *kobj, \ > > + struct kobj_attribute *attr, \ > > + const char *buf, size_t count) \ > > +{ \ > > + char *p = memchr(buf, '\n', count); \ > > + int ret = -EIO; \ > > + int i; \ > > + \ > > + if (p != NULL) \ > > + *p = '\0'; \ > > + i = get_##type##_instance_id(kobj); \ > > + if (i >= 0) \ > > + ret = validate_##type##_input(i, buf); \ > > + if (!ret) \ > > + ret = set_attribute(kobj->name, buf); \ > > + return ret ? ret : count; \ > > +} > > + > > +union acpi_object *get_wmiobj_pointer(int instance_id, const char > > *guid_string); > > +int get_instance_count(const char *guid_string); > > +void strncpy_attr(char *dest, char *src); > > + > > +int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, > > + struct kobject *attr_name_kobj); > > +int alloc_enum_data(void); > > +void exit_enum_attributes(struct kset *kset); > > + > > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > > + struct kobject *attr_name_kobj); > > +int alloc_int_data(void); > > +void exit_int_attributes(struct kset *kset); > > + > > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct > > kobject *attr_name_kobj); > > +int alloc_str_data(void); > > +void exit_str_attributes(struct kset *kset); > > + > > +int populate_po_data(union acpi_object *po_obj, int instance_id, struct > > kobject *attr_name_kobj); > > +int alloc_po_data(void); > > +void exit_po_attributes(struct kset *kset); > > + > > +int set_attribute(const char *a_name, const char *a_value); > > +int set_bios_defaults(u8 defType); > > + > > +void exit_dell_wmi_bios_attr_set_interface(void); > > +int init_dell_wmi_bios_attr_set_interface(void); > > +bool get_pending_changes(void); > > +int map_wmi_error(int error_code); > > +void populate_security_buffer(char *buffer, char *authentication); > > + > > +char *get_current_password(const char *password_type); > > +int set_current_password(const char *password_type, const char *cur); > > +int set_new_password(const char *password_type, const char *new); > > +int init_dell_wmi_bios_attr_pass_interface(void); > > +void exit_dell_wmi_bios_attr_pass_interface(void); > > + > > +#endif > > -- > > 2.25.1 >
Hi All, Going forward I will be helping Andy and Darren with maintaining the drivers/platform/x86/* drivers. So one of the first things which I'm doing with that hat on, is review this patch. On 7/30/20 4:31 PM, Divya Bharathi wrote: > From: Divya Bharathi <divya.bharathi@dell.com> > > 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. > > Co-developed-by: Mario Limonciello <mario.limonciello@dell.com> > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com> > Co-developed-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Divya Bharathi <divya.bharathi@dell.com> > --- > NOTE: This patch series is intended to go on top of > platform-drivers-x86 linux-next. > > .../testing/sysfs-platform-dell-wmi-sysman | 201 ++++++ > MAINTAINERS | 9 + > drivers/platform/x86/Kconfig | 11 + > drivers/platform/x86/Makefile | 8 + > .../x86/dell-wmi-biosattr-interface.c | 263 ++++++++ > .../platform/x86/dell-wmi-enum-attributes.c | 207 +++++++ > .../platform/x86/dell-wmi-int-attributes.c | 188 ++++++ > .../x86/dell-wmi-passobj-attributes.c | 161 +++++ > .../x86/dell-wmi-passwordattr-interface.c | 230 +++++++ > .../platform/x86/dell-wmi-string-attributes.c | 170 ++++++ > .../platform/x86/dell-wmi-sysman-attributes.c | 575 ++++++++++++++++++ > .../platform/x86/dell-wmi-sysman-attributes.h | 125 ++++ > 12 files changed, 2148 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/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > new file mode 100644 > index 000000000000..8ca3e502ed89 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > @@ -0,0 +1,201 @@ > +What: /sys/devices/platform/dell-wmi-sysman/attributes/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + The Dell WMI Systems Management Driver provides a sysfs interface > + for systems management software to enable BIOS configuration > + capability on certain Dell systems. This directory exposes > + interfaces for interacting with BIOS attributes > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This attribute can be used to reset the BIOS Configuration. > + Specifically, it tells which type of reset BIOS configuration is being > + requested on the host. > + > + Reading from it returns a list of supported options encoded as: > + > + 'builtinsafe' (Built in safe configuration profile) > + 'lastknowngood' (Last known good saved configuration profile) > + 'factory' (Default factory settings configuration profile) > + 'custom' (Custom saved configuration profile) > + > + The currently selected option is printed in square brackets as > + shown below: > + > + # echo "factory" > sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + > + # cat sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + # builtinsafe lastknowngood [factory] custom > + > + Note that any changes to this attribute requires a reboot > + for changes to take effect. > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/pending_reboot > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + A read-only attribute enumerating if a reboot is pending > + on any BIOS attribute change. > + > + 0: No pending reboot > + 1: Pending reboot > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/enumeration/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS enumeration attributes. > + > + Enumeration attributes are settings that accept set of > + pre-defined valid values. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of a <attr> > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > + possible_value: A file that can be read to obtain the possible > + value of the <attr> > + > + value_modifier: A file that can be read to obtain value-level > + dependency on a possible value which has to be met to configure <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/integer/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS integer attributes. > + > + Integer attributes are settings that accept a range of > + numerical values for inputs. Each BIOS integer has a > + lower bound and an upper bound on the values that it can take. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + lower_bound: A file that can be read to obtain the lower > + bound value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > + scalar_increment: A file that can be read to obtain the > + resolution of the incremental value this attribute accepts. > + > + upper_bound: A file that can be read to obtain the upper > + bound value of the <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/string/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS string attributes. > + > + String attributes are settings that accept a string for an input. > + Each BIOS string is characterized by the minimum length of the string, > + the maximum length of the string, and the type of the string. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + max_length: A file that can be read to obtain the maximum > + length value of the <attr> > + > + min_length: A file that can be read to obtain the minimum > + length value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> So first of all some comments on the userspace (sysfs) API for that. Getting this part right is the most important part of this patch, as that will be set in stone once merged. My first reaction to the suggest API is that I find the sorting by type thing really weird, so if I were to do: ls /sys/devices/platform/dell-wmi-sysman/attributes/ I would get the following as output: enumeration integer string And then to see the actual attributes I would need to do: ls /sys/devices/platform/dell-wmi-sysman/attributes/{enumeration,integer,string} This feels less then ideal both when interacting from a shell, but also when e.g. envisioning C-code enumerating attributes. IMHO it would be better to have: /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/type Which can be one of "enumeration,integer,string" and then have the other sysfs files (default_Value, current_value, max..., etc.) as: /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/default_value etc. Where which files exactly are present for a specific <attr> depends on the type. This will make e.g C-code enumerating all attributes be a single readdir, followed by reading the type for each dir entry; and if we add a new type the C-code can warn the user that it encountered an atribute with unknown type <new-type>, rather then not being aware that there is a fourth dir (for the new type) with attributes to check. Other then that the sysfs interface generally looks good to me, except for one other big thing (and one small thing, see below). This interface seems pretty generic (which is a good thing), but then having it live in the hardcoded /sys/devices/platform/dell-wmi-sysman/attributes name-space seems less then ideal. I also see in the code that you are creating a dummy platform device, just to have a place/parent to register the attributes dir with. Combining these 2 things I think that it would be better to make a new class for this, like how we e.g. have a backlight class under /sys/class/backlight we could have a /sys/class/firmware_attributes class and then we would get a dell_wmi entry under that (and drop the "attributes" dir), so we would get: /sys/class/firmware_attributes/dell_wmi/<attr>/type Etc. So instead of creating a dummy platform device, you would create a firmware_attributes class device. I think it is likely that other vendors may eventually also support modifying BIOS settings without going into the BIOS setup menu and I would like us to use one unified userspace API for this. Note this changes little for the Dell code / this patch (although eventually some code may be moved into shared helpers), but it does allow userspace to discover if the firmware-attr sysfs API is supported in a vendor agnostic API by doing a readdir on /sys/class/firmware_attributes There could even be multiple instances implementing this interface, e.g. if their is an add-on card with its own option-ROM, see for iscsi booting then the iscsi boot options could be available under: /sys/class/firmware_attributes/iscsi_boot_nic/<attr>/* While the main system firmware settings would be available under: /sys/class/firmware_attributes/dell_wmi/<attr>/* Since you have already designed a nice generic API for this it seems sensible to me to make it possible to use this outside the Dell WMI case. So as mentioned I also have one smaller issue with the API, how is a UI supposed to represent all these attributes? In the BIOS setup screen they are typically grouped together under e.g. CPU settings, power-management settings, etc. I wonder if it would be possible to add a "group" sysfs file to each attribute which represent the typical grouping. E.g. for pm related settings the group file would contain "Power Management" then an userspace Ui can enumerate the groups and have e.g. 1 tab per group, or a tree with the groups as parents oof the attributes for each group. This is just an idea I don't know if such grouping info is available in the WMI interface for this. > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/password/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS password attributes. > + > + BIOS Admin password and System Password can be set, reset or cleared > + using these attributes. > + > + An "Admin" password is used for preventing modification to the BIOS settings. > + A "System" password is required to boot a machine. > + > + is_password_set: A file that can be read > + to obtain flag to see if a password is set on <attr> > + > + max_password_length: A file that can be read to obtain the > + maximum length of the Password > + > + min_password_length: A file that can be read to obtain the > + minimum length of the Password > + > + current_password: A write only value used for privileged access > + such as setting attributes when a system or admin password is set > + or resetting to a new password > + > + new_password: A write only value that when used in tandem with > + current_password will reset a system or admin password. At first I was thinking that things like is_password_set would live directly under /sys/devices/platform/dell-wmi-sysman/attributes/password/ so we would have: /sys/devices/platform/dell-wmi-sysman/attributes/password/is_password_set But now I see that password really is just another type of attribute and we will have: /sys/devices/platform/dell-wmi-sysman/attributes/password/System/is_password_set and: /sys/devices/platform/dell-wmi-sysman/attributes/password/User/is_password_set That makes more sense, and will also work well with the changes I suggest above. I would like to split the overall discussion of the API, versus doing a detailed review of the code, so I will review the code in a separate email. Regards, Hans > diff --git a/MAINTAINERS b/MAINTAINERS > index e64cdde81851..176311d712db 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4879,6 +4879,15 @@ M: Mario Limonciello <mario.limonciello@dell.com> > S: Maintained > F: drivers/platform/x86/dell-wmi-descriptor.c > > +DELL WMI SYSMAN DRIVER > +M: Divya Bharathi <divya.bharathi@dell.com> > +M: Mario Limonciello <mario.limonciello@dell.com> > +M: Prasanth Ksr <prasanth.ksr@dell.com> > +L: platform-driver-x86@vger.kernel.org > +S: Maintained > +F: drivers/platform/x86/dell-wmi-*-attributes.* > +F: drivers/platform/x86/dell-wmi-*-interface.c > + > DELL WMI NOTIFICATIONS DRIVER > M: Matthew Garrett <mjg59@srcf.ucam.org> > M: Pali Rohár <pali@kernel.org> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > index 0581a54cf562..de66373554c2 100644 > --- a/drivers/platform/x86/Kconfig > +++ b/drivers/platform/x86/Kconfig > @@ -430,6 +430,17 @@ config DELL_WMI > To compile this driver as a module, choose M here: the module will > be called dell-wmi. > > +config DELL_WMI_SYSMAN > + tristate "Dell WMI SYSMAN" > + depends on ACPI_WMI > + depends on DMI > + help > + This driver allows changing BIOS settings on many Dell machines from > + 2018 and newer without the use of any additional software. > + > + To compile this driver as a module, choose M here: the module will > + be called dell-wmi-sysman. > + > config DELL_WMI_DESCRIPTOR > tristate > depends on ACPI_WMI > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile > index 2b85852a1a87..bee03f1af28f 100644 > --- a/drivers/platform/x86/Makefile > +++ b/drivers/platform/x86/Makefile > @@ -47,6 +47,14 @@ obj-$(CONFIG_DELL_WMI) += dell-wmi.o > obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o > obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o > obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o > +obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o > +dell-wmi-sysman-objs := dell-wmi-sysman-attributes.o \ > + dell-wmi-enum-attributes.o \ > + dell-wmi-int-attributes.o \ > + dell-wmi-string-attributes.o \ > + dell-wmi-passobj-attributes.o \ > + dell-wmi-passwordattr-interface.o \ > + dell-wmi-biosattr-interface.o > > # Fujitsu > obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o > diff --git a/drivers/platform/x86/dell-wmi-biosattr-interface.c b/drivers/platform/x86/dell-wmi-biosattr-interface.c > new file mode 100644 > index 000000000000..decf124d4388 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-biosattr-interface.c > @@ -0,0 +1,263 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET methods under BIOS attributes interface GUID for use > + * with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +#define SETDEFAULTVALUES_METHOD_ID 0x02 > +#define SETBIOSDEFAULTS_METHOD_ID 0x03 > +#define SETATTRIBUTE_METHOD_ID 0x04 > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + bool pending_changes; > +}; > +static LIST_HEAD(interface_list); > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, > + int method_id) > +{ > + 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, method_id, &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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_pending_changes() - Fetch if any changes are pending > + * > + * Checks if any changes have been written that will be changed > + * after a system reboot > + **/ > +bool get_pending_changes(void) > +{ > + struct wmi_interface_priv *priv; > + > + priv = get_first_interface_priv(); > + if (priv) > + return priv->pending_changes; > + return 0; > +} > + > +/** > + * set_attribute() - Update an attribute value > + * @a_name: The attribute name > + * @a_value: The attribute value > + * > + * Sets an attribute to new value > + **/ > +int set_attribute(const char *a_name, const char *a_value) > +{ > + int ret = -1; > + int i; > + u8 *name_len, *value_len; > + char *current_password, *attribute_name, *attribute_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + name_len = buffer + security_area_size; > + attribute_name = name_len + sizeof(u16); > + value_len = attribute_name + strlen(a_name)*2; > + attribute_value = value_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *name_len = strlen(a_name)*2; > + *value_len = strlen(a_value)*2; > + for (i = 0; i < strlen(a_name); i++) > + attribute_name[i*2] = a_name[i]; > + for (i = 0; i < strlen(a_value); i++) > + attribute_value[i*2] = a_value[i]; > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_set_attribute; > + } > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETATTRIBUTE_METHOD_ID); > + if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + > + priv->pending_changes = 1; > +out_set_attribute: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +/** > + * set_bios_defaults() - Resets BIOS defaults > + * @deftype: the type of BIOS value reset to issue. > + * > + * Resets BIOS defaults > + **/ > +int set_bios_defaults(u8 deftype) > +{ > + int ret = -1; > + u8 *defaultType; > + char *current_password, *buffer; > + size_t security_area_size; > + size_t integer_area_size = sizeof(u8); > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + > + buffer_size = security_area_size + integer_area_size; > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_bios_defaults; > + } > + > + defaultType = buffer + security_area_size; > + *defaultType = deftype; > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETBIOSDEFAULTS_METHOD_ID); > + if (ret) > + dev_err(&priv->wdev->dev, "reset BIOS defaults failed: %d", ret); > + priv->pending_changes = 1; > +out_bios_defaults: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_set_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_set_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_set_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-set" > + }, > + .probe = dell_wmi_bios_attr_set_interface_probe, > + .remove = dell_wmi_bios_attr_set_interface_remove, > + .id_table = dell_wmi_bios_attr_set_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_set_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_set_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_set_interface_id_table); > 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..a2d8ae291d5c > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-enum-attributes.c > @@ -0,0 +1,207 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct enumeration_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[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_value_count; > + char possible_value[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; > + > + 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) > +{ > + int ret = -EINVAL; > + char *options, *tmp, *p; > + > + options = tmp = kstrdup((enumeration_data[instance_id].possible_value), 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_value, enumeration); > +struct kobj_attribute poss_val = > + __ATTR_RO(possible_value); > + > +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, > + 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 i, next_obj; > + int retval = sysfs_create_group(attr_name_kobj, &enumeration_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_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_value_count = > + (uintptr_t) enumeration_obj[++next_obj].string.pointer; > + > + for (i = 0; i < enumeration_data[instance_id].possible_value_count; i++) { > + strcat(enumeration_data[instance_id].possible_value, > + enumeration_obj[++next_obj].string.pointer); > + strcat(enumeration_data[instance_id].possible_value, ";"); > + } > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_enum_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_enum_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &enumeration_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(enumeration_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-int-attributes.c b/drivers/platform/x86/dell-wmi-int-attributes.c > new file mode 100644 > index 000000000000..222971be2b48 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-int-attributes.c > @@ -0,0 +1,188 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to integer type attributes under BIOS Integer GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum int_properties {LOWER_BOUND = 6, UPPER_BOUND, SCALAR_INCR}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct integer_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + int default_value; > + int current_value; > + char modifier[MAX_BUFF]; > + int lower_bound; > + int upper_bound; > + int scalar_increment; > +}; > + > +static struct integer_data *integer_data; > +static int integer_instances_count; > +get_instance_id(integer); > + > +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_integer_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + integer_data[instance_id].current_value = > + (uintptr_t)obj->package.elements[CURRENT_VAL].string.pointer; > + kfree(obj); > + return sprintf(buf, "%d\n", integer_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_integer_input() - Validate input of current_value against lower and upper bound > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_integer_input(int instance_id, const char *buf) > +{ > + int in_val; > + int ret = -EINVAL; > + > + if (kstrtoint(buf, 0, &in_val)) > + return -EINVAL; > + if ((in_val >= integer_data[instance_id].lower_bound) && > + (in_val <= integer_data[instance_id].upper_bound)) > + ret = 0; > + > + return ret; > +} > + > +attribute_s_property_show(display_name_language_code, integer); > +static struct kobj_attribute integer_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, integer); > +struct kobj_attribute integer_displ_name = > + __ATTR_RO(display_name); > + > +attribute_n_property_show(default_value, integer); > +struct kobj_attribute integer_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, integer); > +struct kobj_attribute integer_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, integer); > +struct kobj_attribute integer_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(lower_bound, integer); > +struct kobj_attribute integer_lower_bound = > + __ATTR_RO(lower_bound); > + > +attribute_n_property_show(upper_bound, integer); > +struct kobj_attribute integer_upper_bound = > + __ATTR_RO(upper_bound); > + > +attribute_n_property_show(scalar_increment, integer); > +struct kobj_attribute integer_scalar_increment = > + __ATTR_RO(scalar_increment); > + > +static struct attribute *integer_attrs[] = { > + &integer_displ_langcode.attr, > + &integer_displ_name.attr, > + &integer_default_val.attr, > + &integer_current_val.attr, > + &integer_modifier.attr, > + &integer_lower_bound.attr, > + &integer_upper_bound.attr, > + &integer_scalar_increment.attr, > + NULL, > +}; > + > +static const struct attribute_group integer_attr_group = { > + .attrs = integer_attrs, > +}; > + > +int alloc_int_data(void) > +{ > + int ret = 0; > + > + integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + integer_data = kzalloc((sizeof(struct integer_data) * integer_instances_count), GFP_KERNEL); > + if (!integer_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under integer attribute > + * @integer_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &integer_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(integer_data[instance_id].attribute_name, > + integer_obj[ATTR_NAME].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name_language_code, > + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name, > + integer_obj[DISPLAY_NAME].string.pointer); > + integer_data[instance_id].default_value = > + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; > + integer_data[instance_id].current_value = > + (uintptr_t)integer_obj[CURRENT_VAL].string.pointer; > + strncpy_attr(integer_data[instance_id].modifier, integer_obj[MODIFIER].string.pointer); > + integer_data[instance_id].lower_bound = > + (uintptr_t)integer_obj[LOWER_BOUND].string.pointer; > + integer_data[instance_id].upper_bound = > + (uintptr_t)integer_obj[UPPER_BOUND].string.pointer; > + integer_data[instance_id].scalar_increment = > + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_int_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_int_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &integer_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(integer_data); > + mutex_unlock(&call_mutex); > +} > 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..d430ecf2387b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c > @@ -0,0 +1,161 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +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; > +}; > + > +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; > + > + 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; > + > + ret = set_current_password(kobj->name, buf); > + 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); > + > +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, > + 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(&call_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; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_po_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_po_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &po_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(po_data); > + mutex_unlock(&call_mutex); > +} > 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..9d7ac36e232f > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c > @@ -0,0 +1,230 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET password methods under BIOS attributes interface GUID > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + char current_admin_password[MAX_BUFF]; > + char current_system_password[MAX_BUFF]; > +}; > +static LIST_HEAD(interface_list); > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_current_password() - Get the current stored password value > + * @password_type: The type of password to store > + **/ > +char *get_current_password(const char *password_type) > +{ > + struct wmi_interface_priv *priv = get_first_interface_priv(); > + > + if (!priv) { > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + return NULL; > + } > + if (strcmp(password_type, "Admin") == 0) > + return priv->current_admin_password; > + if (strcmp(password_type, "System") == 0) > + return priv->current_system_password; > + dev_err(&priv->wdev->dev, "unknown password type %s", password_type); > + return NULL; > +} > + > +/** > + * set_current_password() - Store current password > + * @password_type: The type of password to store > + * @current: The current value > + * > + * Sets the current value of the password. > + * This is used for: > + * - Resetting password > + * - Basis for any functions requiring password to execute > + **/ > +int set_current_password(const char *password_type, const char *cur) > +{ > + char *current_password = get_current_password(password_type); > + > + if (!current_password) > + return -ENODEV; > + strncpy(current_password, cur, (strlen(cur) + 1)); > + return 0; > +} > + > +/** > + * set_new_password() - Sets a system admin password > + * @password_type: The type of password to set > + * @new: The new password > + * > + * Sets the password using plaintext interface > + **/ > +int set_new_password(const char *password_type, const char *new) > +{ > + int ret = -1; > + int i; > + u8 *type_len, *old_len, *new_len; > + char *current_password, *type_value, *old_value, *new_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_close_mutex; > + } > + current_password = get_current_password(password_type); > + if (!current_password) { > + ret = -ENODEV; > + goto out_close_mutex; > + } > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + string_area_size = (strlen(password_type) + strlen(current_password) + strlen(new))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 3; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + > + if (!buffer) { > + ret = -ENOMEM; > + goto out_close_mutex; > + } > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + type_len = buffer + security_area_size; > + type_value = type_len + sizeof(u16); > + old_len = type_value + strlen(password_type)*2; > + old_value = old_len + sizeof(u16); > + new_len = old_value + strlen(current_password)*2; > + new_value = new_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *type_len = strlen(password_type)*2; > + *old_len = strlen(current_password)*2; > + *new_len = strlen(new)*2; > + for (i = 0; i < strlen(password_type); i++) > + type_value[i*2] = password_type[i]; > + for (i = 0; i < strlen(current_password); i++) > + old_value[i*2] = current_password[i]; > + for (i = 0; i < strlen(new); i++) > + new_value[i*2] = new[i]; > + > + ret = call_password_interface(priv->wdev, buffer, buffer_size); > + /* update current password so commands work after reset */ > + if (!ret) > + ret = set_current_password(password_type, new); > + /* explain to user the detailed failure reason */ > + else if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + kfree(buffer); > + > +out_close_mutex: > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_pass_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_pass_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_pass_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-password" > + }, > + .probe = dell_wmi_bios_attr_pass_interface_probe, > + .remove = dell_wmi_bios_attr_pass_interface_remove, > + .id_table = dell_wmi_bios_attr_pass_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_pass_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_pass_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_pass_interface_id_table); > diff --git a/drivers/platform/x86/dell-wmi-string-attributes.c b/drivers/platform/x86/dell-wmi-string-attributes.c > new file mode 100644 > index 000000000000..562d09055dd1 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-string-attributes.c > @@ -0,0 +1,170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to string type attributes under BIOS String GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum string_properties {MIN_LEN = 6, MAX_LEN}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct str_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + char default_value[MAX_BUFF]; > + char current_value[MAX_BUFF]; > + char modifier[MAX_BUFF]; > + int min_length; > + int max_length; > +}; > + > +static struct str_data *str_data; > +static int str_instances_count; > +get_instance_id(str); > + > +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_str_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + strncpy_attr(str_data[instance_id].current_value, > + obj->package.elements[CURRENT_VAL].string.pointer); > + kfree(obj); > + return sprintf(buf, "%s\n", str_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_str_input() - Validate input of current_value against min and max lengths > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_str_input(int instance_id, const char *buf) > +{ > + int in_len = strlen(buf); > + > + if ((in_len >= str_data[instance_id].min_length) && > + (in_len <= str_data[instance_id].max_length)) > + return 0; > + > + return -EINVAL; > +} > + > +attribute_s_property_show(display_name_language_code, str); > +static struct kobj_attribute str_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, str); > +struct kobj_attribute str_displ_name = > + __ATTR_RO(display_name); > + > +attribute_s_property_show(default_value, str); > +struct kobj_attribute str_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, str); > +struct kobj_attribute str_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, str); > +struct kobj_attribute str_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(min_length, str); > +struct kobj_attribute str_min_length = > + __ATTR_RO(min_length); > + > +attribute_n_property_show(max_length, str); > +struct kobj_attribute str_max_length = > + __ATTR_RO(max_length); > + > +static struct attribute *str_attrs[] = { > + &str_displ_langcode.attr, > + &str_displ_name.attr, > + &str_default_val.attr, > + &str_current_val.attr, > + &str_modifier.attr, > + &str_min_length.attr, > + &str_max_length.attr, > + NULL, > +}; > + > +static const struct attribute_group str_attr_group = { > + .attrs = str_attrs, > +}; > + > +int alloc_str_data(void) > +{ > + int ret = 0; > + > + str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + str_data = kzalloc((sizeof(struct str_data) * str_instances_count), GFP_KERNEL); > + if (!str_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under string attribute > + * @str_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &str_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(str_data[instance_id].attribute_name, str_obj[ATTR_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].display_name_language_code, > + str_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(str_data[instance_id].display_name, str_obj[DISPLAY_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].default_value, str_obj[DEFAULT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].current_value, str_obj[CURRENT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].modifier, str_obj[MODIFIER].string.pointer); > + str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer; > + str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_str_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_str_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &str_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(str_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.c b/drivers/platform/x86/dell-wmi-sysman-attributes.c > new file mode 100644 > index 000000000000..485545ab6c8b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.c > @@ -0,0 +1,575 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Common methods for use with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/dmi.h> > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +#define MAX_TYPES 4 > + > +static struct platform_device *platform_device; > + > +static struct platform_driver platform_driver = { > + .driver = { > + .name = DRIVER_NAME, > + }, > +}; > + > +/* attribute directory under platform dev */ > +struct kset *main_dir_kset; > +/* subtypes of attributes */ > +struct kset *type_dir_kset[MAX_TYPES]; > + > +/* reset bios to defaults */ > +static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; > +static int reset_option = -1; > + > +/** > + * populate_security_buffer() - builds a security buffer for authentication scheme > + * @buffer: the buffer to populate > + * @authentication: the authentication content > + * > + * Currently only supported type is PLAIN TEXT > + **/ > +void populate_security_buffer(char *buffer, char *authentication) > +{ > + u32 *sectype = (u32 *) buffer; > + u32 *seclen = sectype + 1; > + char *auth = buffer + sizeof(u32)*2; > + /* plain text */ > + *sectype = 1; > + *seclen = strlen(authentication); > + strncpy(auth, authentication, *seclen); > +} > + > +/** > + * map_wmi_error() - map errors from WMI methods to kernel error codes > + **/ > +int map_wmi_error(int error_code) > +{ > + switch (error_code) { > + case 0: > + /* success */ > + return 0; > + case 1: > + /* failed */ > + return -EIO; > + case 2: > + /* invalid parameter */ > + return -EINVAL; > + case 3: > + /* access denied */ > + return -EACCES; > + case 4: > + /* not supported */ > + return -EOPNOTSUPP; > + case 5: > + /* memory error */ > + return -ENOMEM; > + case 6: > + /* protocol error */ > + return -EPROTO; > + } > + /* unspecified error */ > + return -EIO; > +} > + > +/** > + * reset_bios_show() - sysfs implementaton for read reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + **/ > +static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) > +{ > + int i; > + char *start = buf; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (i == reset_option) > + buf += sprintf(buf, "[%s] ", reset_types[i]); > + else > + buf += sprintf(buf, "%s ", reset_types[i]); > + } > + buf += sprintf(buf, "\n"); > + return buf-start; > +} > + > +/** > + * reset_bios_store() - sysfs implementaton for write reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer from userspace > + * @count: the size of the buffer from userspace > + **/ > +static ssize_t reset_bios_store(struct kobject *kobj, > + struct kobj_attribute *attr, const char *buf, size_t count) > +{ > + int len, ret, i; > + int type = -1; > + char *p; > + > + p = memchr(buf, '\n', count); > + if (p != NULL) > + *p = '\0'; > + len = p ? p - buf : count; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (len == strlen(reset_types[i]) > + && !strncmp(buf, reset_types[i], len)) { > + type = i; > + break; > + } > + } > + > + if (type < 0 || type >= MAX_TYPES) { > + ret = -EINVAL; > + goto out; > + } > + > + ret = set_bios_defaults(type); > + dev_dbg(&platform_device->dev, "reset all attributes request type %d: %d", type, ret); > + if (ret) { > + ret = -EINVAL; > + } else { > + reset_option = type; > + ret = count; > + } > + > +out: > + return ret; > +} > + > +/** > + * pending_reboot_show() - sysfs implementaton for read pending_reboot > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + * > + * Stores default value as 0 > + * When current_value is changed this attribute is set to 1 to notify reboot may be required > + **/ > +static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%d\n", get_pending_changes()); > +} > + > +static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); > +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); > + > + > +/** > + * create_reset_bios() - Creates reset_bios and pending_reboot attributes > + **/ > +static int create_reset_bios(void) > +{ > + int ret = sysfs_create_file(&main_dir_kset->kobj, &reset_bios.attr); > + > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset_bios file"); > + return ret; > + } > + > + ret = sysfs_create_file(&main_dir_kset->kobj, &pending_reboot.attr); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create changing_pending_reboot file"); > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + } > + return ret; > +} > + > +static void release_reset_bios_data(void) > +{ > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + sysfs_remove_file(&main_dir_kset->kobj, &pending_reboot.attr); > +} > + > +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, > + char *buf) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->show) > + ret = kattr->show(kobj, kattr, buf); > + return ret; > +} > + > +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, > + const char *buf, size_t count) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->store) > + ret = kattr->store(kobj, kattr, buf, count); > + return ret; > +} > + > +const struct sysfs_ops kobj_sysfs_ops = { > + .show = kobj_attr_show, > + .store = kobj_attr_store, > +}; > + > +static void attr_name_release(struct kobject *kobj) > +{ > + kfree(kobj); > +} > + > +static struct kobj_type attr_name_ktype = { > + .release = attr_name_release, > + .sysfs_ops = &kobj_sysfs_ops, > +}; > + > +/** > + * strncpy_attr - Copy a length-limited, NULL-terminated string with bound checks > + * @dest: Where to copy the string to > + * @src: Where to copy the string from > + **/ > +void strncpy_attr(char *dest, char *src) > +{ > + size_t len = strlen(src) + 1; > + > + if (len > 1 && len < MAX_BUFF) > + strncpy(dest, src, len); > +} > + > +/** > + * get_wmiobj_pointer() - Get Content of WMI block for particular instance > + * @instance_id: WMI instance ID > + * @guid_string: WMI GUID (in str form) > + * > + * Fetches the content for WMI block (instance_id) under GUID (guid_string) > + * Caller must kfree the return > + **/ > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) > +{ > + acpi_status status; > + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; > + > + status = wmi_query_block(guid_string, instance_id, &out); > + > + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; > +} > + > +/** > + * get_instance_count() - Compute total number of instances under guid_string > + * @guid_string: WMI GUID (in string form) > + **/ > +int get_instance_count(const char *guid_string) > +{ > + int i = 0; > + union acpi_object *wmi_obj = NULL; > + > + do { > + kfree(wmi_obj); > + wmi_obj = get_wmiobj_pointer(i, guid_string); > + i++; > + } while (wmi_obj); > + > + return (i-1); > +} > + > +/** > + * alloc_attributes_data() - Allocate attributes data for a particular type > + * @attr_type: Attribute type to allocate > + **/ > +static int alloc_attributes_data(int attr_type) > +{ > + int retval = 0; > + char *type[MAX_TYPES] = {"enumeration", "integer", "string", "password"}; > + > + type_dir_kset[attr_type] = kset_create_and_add(type[attr_type], NULL, &main_dir_kset->kobj); > + if (!type_dir_kset[attr_type]) > + goto err_bios_attr; > + > + switch (attr_type) { > + case ENUM: > + retval = alloc_enum_data(); > + break; > + case INT: > + retval = alloc_int_data(); > + break; > + case STR: > + retval = alloc_str_data(); > + break; > + case PO: > + retval = alloc_po_data(); > + break; > + default: > + break; > + } > + > + goto out; > + > +err_bios_attr: > + kset_unregister(main_dir_kset); > + > +out: > + return retval; > +} > + > +/** > + * destroy_attribute_objs() - Free a kset of kobjects > + * @kset: The kset to destroy > + * > + * Fress kobjects created for each attribute_name under attribute type kset > + **/ > +static void destroy_attribute_objs(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + kobject_put(pos); > + } > + mutex_unlock(&list_mutex); > +} > + > +/** > + * release_attributes_data() - Clean-up all sysfs directories and files created > + **/ > +static void release_attributes_data(void) > +{ > + release_reset_bios_data(); > + > + exit_enum_attributes(type_dir_kset[ENUM]); > + exit_int_attributes(type_dir_kset[INT]); > + exit_str_attributes(type_dir_kset[STR]); > + exit_po_attributes(type_dir_kset[PO]); > + > + mutex_lock(&call_mutex); > + if (main_dir_kset) { > + int i; > + > + /* unregister all 4 types ENUM,INT,STR and PO */ > + for (i = ENUM; i <= PO; i++) { > + destroy_attribute_objs(type_dir_kset[i]); > + kset_unregister(type_dir_kset[i]); > + } > + kset_unregister(main_dir_kset); > + } > + mutex_unlock(&call_mutex); > +} > + > +/** > + * init_bios_attributes() - Initialize all attributes for a type > + * @attr_type: The attribute type to initialize > + * @guid: The WMI GUID associated with this type to initialize > + * > + * Initialiaze all 4 types of attributes enumeration, integer, string and password object. > + * Populates each attrbute typ's respective properties under sysfs files > + **/ > +static int init_bios_attributes(int attr_type, const char *guid) > +{ > + union acpi_object *obj = NULL; > + union acpi_object *elements; > + int retval = 0; > + int instance_id = 0; > + struct kobject *attr_name_kobj; //individual attribute names > + > + retval = alloc_attributes_data(attr_type); > + if (retval) > + return retval; > + obj = get_wmiobj_pointer(instance_id, guid); > + if (!obj) > + return -ENODEV; > + elements = obj->package.elements; > + > + while (elements) { > + /* sanity checking */ > + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { > + dev_dbg(&platform_device->dev, "empty attribute found"); > + goto nextobj; > + } > + if (kset_find_obj(type_dir_kset[attr_type], elements[ATTR_NAME].string.pointer)) { > + dev_dbg(&platform_device->dev, "duplicate attribute name found - %s", > + elements[ATTR_NAME].string.pointer); > + goto nextobj; > + } > + > + /* build attribute */ > + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); > + if (!attr_name_kobj) > + goto err_attr_init; > + > + attr_name_kobj->kset = type_dir_kset[attr_type]; > + > + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", > + elements[ATTR_NAME].string.pointer); > + if (retval) { > + kobject_put(attr_name_kobj); > + goto err_attr_init; > + } > + > + /* enumerate all of this attribute */ > + switch (attr_type) { > + case ENUM: > + retval = populate_enum_data(elements, instance_id, attr_name_kobj); > + break; > + case INT: > + retval = populate_int_data(elements, instance_id, attr_name_kobj); > + break; > + case STR: > + retval = populate_str_data(elements, instance_id, attr_name_kobj); > + break; > + case PO: > + retval = populate_po_data(elements, instance_id, attr_name_kobj); > + break; > + default: > + break; > + } > + > + if (retval) { > + dev_dbg(&platform_device->dev, "failed to populate %s", > + elements[ATTR_NAME].string.pointer); > + goto err_attr_init; > + } > + > +nextobj: > + kfree(obj); > + instance_id++; > + obj = get_wmiobj_pointer(instance_id, guid); > + elements = obj ? obj->package.elements : NULL; > + } > + > + goto out; > + > +err_attr_init: > + release_attributes_data(); > + kfree(obj); > +out: > + return retval; > +} > + > +static int __init init_dell_bios_attrib_wmi(void) > +{ > + int ret = 0; > + > + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && > + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { > + pr_err("Unable to run on non-Dell system\n"); > + return -ENODEV; > + } > + > + ret = platform_driver_register(&platform_driver); > + if (ret) > + goto fail_platform_driver; > + > + platform_device = platform_device_alloc(DRIVER_NAME, -1); > + if (!platform_device) { > + ret = -ENOMEM; > + goto fail_platform_device_alloc; > + } > + > + ret = platform_device_add(platform_device); > + if (ret) > + goto fail_platform_device_add; > + > + main_dir_kset = kset_create_and_add("attributes", NULL, &platform_device->dev.kobj); > + if (!main_dir_kset) { > + ret = -ENOMEM; > + goto fail_platform_device_add; > + } > + > + ret = init_dell_wmi_bios_attr_set_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize set interface"); > + goto fail_kset; > + } > + > + ret = init_dell_wmi_bios_attr_pass_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize pass interface"); > + goto fail_set_interface; > + } > + > + ret = create_reset_bios(); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset BIOS attribute"); > + goto fail_pass_interface; > + } > + > + ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate enumeration type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate integer type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate string type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate pass object type attributes"); > + goto fail_create_group; > + } > + > + return 0; > + > +fail_create_group: > + release_attributes_data(); > + platform_device_del(platform_device); > + > +fail_pass_interface: > + exit_dell_wmi_bios_attr_pass_interface(); > + > +fail_set_interface: > + exit_dell_wmi_bios_attr_set_interface(); > + > +fail_kset: > + kset_unregister(main_dir_kset); > + > +fail_platform_device_add: > + platform_device_put(platform_device); > + > +fail_platform_device_alloc: > + platform_driver_unregister(&platform_driver); > + > +fail_platform_driver: > + return ret; > +} > + > +static void __exit exit_dell_bios_attrib_wmi(void) > +{ > + release_attributes_data(); > + mutex_lock(&call_mutex); > + exit_dell_wmi_bios_attr_set_interface(); > + exit_dell_wmi_bios_attr_pass_interface(); > + if (platform_device) { > + platform_device_unregister(platform_device); > + platform_driver_unregister(&platform_driver); > + } > + mutex_unlock(&call_mutex); > +} > + > +module_init(init_dell_bios_attrib_wmi); > +module_exit(exit_dell_bios_attrib_wmi); > + > +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); > +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); > +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); > +MODULE_DESCRIPTION("Dell platform setting control interface"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.h b/drivers/platform/x86/dell-wmi-sysman-attributes.h > new file mode 100644 > index 000000000000..bb5053b91ec7 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.h > @@ -0,0 +1,125 @@ > +/* SPDX-License-Identifier: GPL-2.0 > + * Definitions for kernel modules using Dell WMI System Management Driver > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#ifndef _DELL_WMI_BIOS_ATTR_H_ > +#define _DELL_WMI_BIOS_ATTR_H_ > + > +#include <linux/wmi.h> > +#include <linux/device.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/capability.h> > + > +#define DRIVER_NAME "dell-wmi-sysman" > +#define MAX_BUFF 512 > + > +#define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF5" > +#define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BFA" > +#define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF9" > +#define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719-97D7-6AD24108BFD4" > +#define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF4" > +#define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214-A1C6-1F884B1A892A" > + > +enum { ENUM, INT, STR, PO }; > + > +enum { > + ATTR_NAME, > + DISPL_NAME_LANG_CODE, > + DISPLAY_NAME, > + DEFAULT_VAL, > + CURRENT_VAL, > + MODIFIER > +}; > + > +#define get_instance_id(type) \ > +int get_##type##_instance_id(struct kobject *kobj) \ > +{ \ > + int i; \ > + for (i = 0; i <= type##_instances_count; i++) { \ > + if (!(strcmp(kobj->name, type##_data[i].attribute_name))) \ > + return i; \ > + } \ > + return -EIO; \ > +} > + > +#define attribute_s_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%s\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_n_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%d\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_property_store(curr_val, type) \ > +static ssize_t curr_val##_store(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + const char *buf, size_t count) \ > +{ \ > + char *p = memchr(buf, '\n', count); \ > + int ret = -EIO; \ > + int i; \ > + \ > + if (p != NULL) \ > + *p = '\0'; \ > + i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + ret = validate_##type##_input(i, buf); \ > + if (!ret) \ > + ret = set_attribute(kobj->name, buf); \ > + return ret ? ret : count; \ > +} > + > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); > +int get_instance_count(const char *guid_string); > +void strncpy_attr(char *dest, char *src); > + > +int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_enum_data(void); > +void exit_enum_attributes(struct kset *kset); > + > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_int_data(void); > +void exit_int_attributes(struct kset *kset); > + > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_str_data(void); > +void exit_str_attributes(struct kset *kset); > + > +int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_po_data(void); > +void exit_po_attributes(struct kset *kset); > + > +int set_attribute(const char *a_name, const char *a_value); > +int set_bios_defaults(u8 defType); > + > +void exit_dell_wmi_bios_attr_set_interface(void); > +int init_dell_wmi_bios_attr_set_interface(void); > +bool get_pending_changes(void); > +int map_wmi_error(int error_code); > +void populate_security_buffer(char *buffer, char *authentication); > + > +char *get_current_password(const char *password_type); > +int set_current_password(const char *password_type, const char *cur); > +int set_new_password(const char *password_type, const char *new); > +int init_dell_wmi_bios_attr_pass_interface(void); > +void exit_dell_wmi_bios_attr_pass_interface(void); > + > +#endif >
Hi, I promised a second review focussed on the code, but first a bit more detailed review of the sysfs-files for the different attribute types... On 7/30/20 4:31 PM, Divya Bharathi wrote: > From: Divya Bharathi <divya.bharathi@dell.com> > > 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. > > Co-developed-by: Mario Limonciello <mario.limonciello@dell.com> > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com> > Co-developed-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Divya Bharathi <divya.bharathi@dell.com> > --- > NOTE: This patch series is intended to go on top of > platform-drivers-x86 linux-next. > > .../testing/sysfs-platform-dell-wmi-sysman | 201 ++++++ > MAINTAINERS | 9 + > drivers/platform/x86/Kconfig | 11 + > drivers/platform/x86/Makefile | 8 + > .../x86/dell-wmi-biosattr-interface.c | 263 ++++++++ > .../platform/x86/dell-wmi-enum-attributes.c | 207 +++++++ > .../platform/x86/dell-wmi-int-attributes.c | 188 ++++++ > .../x86/dell-wmi-passobj-attributes.c | 161 +++++ > .../x86/dell-wmi-passwordattr-interface.c | 230 +++++++ > .../platform/x86/dell-wmi-string-attributes.c | 170 ++++++ > .../platform/x86/dell-wmi-sysman-attributes.c | 575 ++++++++++++++++++ > .../platform/x86/dell-wmi-sysman-attributes.h | 125 ++++ > 12 files changed, 2148 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/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > new file mode 100644 > index 000000000000..8ca3e502ed89 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > @@ -0,0 +1,201 @@ > +What: /sys/devices/platform/dell-wmi-sysman/attributes/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + The Dell WMI Systems Management Driver provides a sysfs interface > + for systems management software to enable BIOS configuration > + capability on certain Dell systems. This directory exposes > + interfaces for interacting with BIOS attributes > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This attribute can be used to reset the BIOS Configuration. > + Specifically, it tells which type of reset BIOS configuration is being > + requested on the host. > + > + Reading from it returns a list of supported options encoded as: > + > + 'builtinsafe' (Built in safe configuration profile) > + 'lastknowngood' (Last known good saved configuration profile) > + 'factory' (Default factory settings configuration profile) > + 'custom' (Custom saved configuration profile) > + > + The currently selected option is printed in square brackets as > + shown below: > + > + # echo "factory" > sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + > + # cat sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + # builtinsafe lastknowngood [factory] custom > + > + Note that any changes to this attribute requires a reboot > + for changes to take effect. > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/pending_reboot > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + A read-only attribute enumerating if a reboot is pending > + on any BIOS attribute change. > + > + 0: No pending reboot > + 1: Pending reboot "A read-only attribute enumerating if a reboot is pending on any BIOS attribute change." does not really seem to make much sense. I guess what this is trying to say is: "This read-only attribute reads 1 if a reboot is necessary to apply pending BIOS attribute changes"? 0: All BIOS attributes setting are current 1: A reboot is necessary to get pending pending BIOS attribute changes applied Or some such. I'm not really happy with my own text either, but I think it better explains what this attribute is about then the original text, right ? > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/enumeration/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS enumeration attributes. > + > + Enumeration attributes are settings that accept set of > + pre-defined valid values. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of a <attr> > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> This needs to be specified better, e.g. this needs to say that this is an ISO 639‑1 language code (or some other language-code specification) > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> What is the difference between modifier and value_modifier ? Also this need to be specified in more detail. > + > + possible_value: A file that can be read to obtain the possible > + value of the <attr> This is an enum, so possible value_s_ ? I assume that for a enum this will list all possible values, this also needs to specify how the possible values will be separated (e.g. using semi-colons or newlines or ...). > + > + value_modifier: A file that can be read to obtain value-level > + dependency on a possible value which has to be met to configure <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/integer/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS integer attributes. > + > + Integer attributes are settings that accept a range of > + numerical values for inputs. Each BIOS integer has a > + lower bound and an upper bound on the values that it can take. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + lower_bound: A file that can be read to obtain the lower > + bound value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > + scalar_increment: A file that can be read to obtain the > + resolution of the incremental value this attribute accepts. > + > + upper_bound: A file that can be read to obtain the upper > + bound value of the <attr> Are these integers or also possibly floats? I guess possibly also floats, right? Then at a minimum this should specify which decimal-separator is used (I assume we will go with the usual '.' as decimal separator). > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/string/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS string attributes. > + > + String attributes are settings that accept a string for an input. > + Each BIOS string is characterized by the minimum length of the string, > + the maximum length of the string, and the type of the string. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + max_length: A file that can be read to obtain the maximum > + length value of the <attr> > + > + min_length: A file that can be read to obtain the minimum > + length value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/password/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS password attributes. > + > + BIOS Admin password and System Password can be set, reset or cleared > + using these attributes. > + > + An "Admin" password is used for preventing modification to the BIOS settings. > + A "System" password is required to boot a machine. > + > + is_password_set: A file that can be read > + to obtain flag to see if a password is set on <attr> > + > + max_password_length: A file that can be read to obtain the > + maximum length of the Password > + > + min_password_length: A file that can be read to obtain the > + minimum length of the Password > + > + current_password: A write only value used for privileged access > + such as setting attributes when a system or admin password is set > + or resetting to a new password > + > + new_password: A write only value that when used in tandem with > + current_password will reset a system or admin password. I'll send a third email for a review of the actual code for this. Regards, Hans > diff --git a/MAINTAINERS b/MAINTAINERS > index e64cdde81851..176311d712db 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4879,6 +4879,15 @@ M: Mario Limonciello <mario.limonciello@dell.com> > S: Maintained > F: drivers/platform/x86/dell-wmi-descriptor.c > > +DELL WMI SYSMAN DRIVER > +M: Divya Bharathi <divya.bharathi@dell.com> > +M: Mario Limonciello <mario.limonciello@dell.com> > +M: Prasanth Ksr <prasanth.ksr@dell.com> > +L: platform-driver-x86@vger.kernel.org > +S: Maintained > +F: drivers/platform/x86/dell-wmi-*-attributes.* > +F: drivers/platform/x86/dell-wmi-*-interface.c > + > DELL WMI NOTIFICATIONS DRIVER > M: Matthew Garrett <mjg59@srcf.ucam.org> > M: Pali Rohár <pali@kernel.org> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > index 0581a54cf562..de66373554c2 100644 > --- a/drivers/platform/x86/Kconfig > +++ b/drivers/platform/x86/Kconfig > @@ -430,6 +430,17 @@ config DELL_WMI > To compile this driver as a module, choose M here: the module will > be called dell-wmi. > > +config DELL_WMI_SYSMAN > + tristate "Dell WMI SYSMAN" > + depends on ACPI_WMI > + depends on DMI > + help > + This driver allows changing BIOS settings on many Dell machines from > + 2018 and newer without the use of any additional software. > + > + To compile this driver as a module, choose M here: the module will > + be called dell-wmi-sysman. > + > config DELL_WMI_DESCRIPTOR > tristate > depends on ACPI_WMI > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile > index 2b85852a1a87..bee03f1af28f 100644 > --- a/drivers/platform/x86/Makefile > +++ b/drivers/platform/x86/Makefile > @@ -47,6 +47,14 @@ obj-$(CONFIG_DELL_WMI) += dell-wmi.o > obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o > obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o > obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o > +obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o > +dell-wmi-sysman-objs := dell-wmi-sysman-attributes.o \ > + dell-wmi-enum-attributes.o \ > + dell-wmi-int-attributes.o \ > + dell-wmi-string-attributes.o \ > + dell-wmi-passobj-attributes.o \ > + dell-wmi-passwordattr-interface.o \ > + dell-wmi-biosattr-interface.o > > # Fujitsu > obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o > diff --git a/drivers/platform/x86/dell-wmi-biosattr-interface.c b/drivers/platform/x86/dell-wmi-biosattr-interface.c > new file mode 100644 > index 000000000000..decf124d4388 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-biosattr-interface.c > @@ -0,0 +1,263 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET methods under BIOS attributes interface GUID for use > + * with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +#define SETDEFAULTVALUES_METHOD_ID 0x02 > +#define SETBIOSDEFAULTS_METHOD_ID 0x03 > +#define SETATTRIBUTE_METHOD_ID 0x04 > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + bool pending_changes; > +}; > +static LIST_HEAD(interface_list); > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, > + int method_id) > +{ > + 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, method_id, &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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_pending_changes() - Fetch if any changes are pending > + * > + * Checks if any changes have been written that will be changed > + * after a system reboot > + **/ > +bool get_pending_changes(void) > +{ > + struct wmi_interface_priv *priv; > + > + priv = get_first_interface_priv(); > + if (priv) > + return priv->pending_changes; > + return 0; > +} > + > +/** > + * set_attribute() - Update an attribute value > + * @a_name: The attribute name > + * @a_value: The attribute value > + * > + * Sets an attribute to new value > + **/ > +int set_attribute(const char *a_name, const char *a_value) > +{ > + int ret = -1; > + int i; > + u8 *name_len, *value_len; > + char *current_password, *attribute_name, *attribute_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + name_len = buffer + security_area_size; > + attribute_name = name_len + sizeof(u16); > + value_len = attribute_name + strlen(a_name)*2; > + attribute_value = value_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *name_len = strlen(a_name)*2; > + *value_len = strlen(a_value)*2; > + for (i = 0; i < strlen(a_name); i++) > + attribute_name[i*2] = a_name[i]; > + for (i = 0; i < strlen(a_value); i++) > + attribute_value[i*2] = a_value[i]; > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_set_attribute; > + } > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETATTRIBUTE_METHOD_ID); > + if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + > + priv->pending_changes = 1; > +out_set_attribute: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +/** > + * set_bios_defaults() - Resets BIOS defaults > + * @deftype: the type of BIOS value reset to issue. > + * > + * Resets BIOS defaults > + **/ > +int set_bios_defaults(u8 deftype) > +{ > + int ret = -1; > + u8 *defaultType; > + char *current_password, *buffer; > + size_t security_area_size; > + size_t integer_area_size = sizeof(u8); > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + > + buffer_size = security_area_size + integer_area_size; > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_bios_defaults; > + } > + > + defaultType = buffer + security_area_size; > + *defaultType = deftype; > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETBIOSDEFAULTS_METHOD_ID); > + if (ret) > + dev_err(&priv->wdev->dev, "reset BIOS defaults failed: %d", ret); > + priv->pending_changes = 1; > +out_bios_defaults: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_set_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_set_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_set_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-set" > + }, > + .probe = dell_wmi_bios_attr_set_interface_probe, > + .remove = dell_wmi_bios_attr_set_interface_remove, > + .id_table = dell_wmi_bios_attr_set_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_set_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_set_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_set_interface_id_table); > 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..a2d8ae291d5c > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-enum-attributes.c > @@ -0,0 +1,207 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct enumeration_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[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_value_count; > + char possible_value[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; > + > + 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) > +{ > + int ret = -EINVAL; > + char *options, *tmp, *p; > + > + options = tmp = kstrdup((enumeration_data[instance_id].possible_value), 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_value, enumeration); > +struct kobj_attribute poss_val = > + __ATTR_RO(possible_value); > + > +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, > + 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 i, next_obj; > + int retval = sysfs_create_group(attr_name_kobj, &enumeration_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_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_value_count = > + (uintptr_t) enumeration_obj[++next_obj].string.pointer; > + > + for (i = 0; i < enumeration_data[instance_id].possible_value_count; i++) { > + strcat(enumeration_data[instance_id].possible_value, > + enumeration_obj[++next_obj].string.pointer); > + strcat(enumeration_data[instance_id].possible_value, ";"); > + } > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_enum_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_enum_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &enumeration_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(enumeration_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-int-attributes.c b/drivers/platform/x86/dell-wmi-int-attributes.c > new file mode 100644 > index 000000000000..222971be2b48 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-int-attributes.c > @@ -0,0 +1,188 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to integer type attributes under BIOS Integer GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum int_properties {LOWER_BOUND = 6, UPPER_BOUND, SCALAR_INCR}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct integer_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + int default_value; > + int current_value; > + char modifier[MAX_BUFF]; > + int lower_bound; > + int upper_bound; > + int scalar_increment; > +}; > + > +static struct integer_data *integer_data; > +static int integer_instances_count; > +get_instance_id(integer); > + > +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_integer_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + integer_data[instance_id].current_value = > + (uintptr_t)obj->package.elements[CURRENT_VAL].string.pointer; > + kfree(obj); > + return sprintf(buf, "%d\n", integer_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_integer_input() - Validate input of current_value against lower and upper bound > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_integer_input(int instance_id, const char *buf) > +{ > + int in_val; > + int ret = -EINVAL; > + > + if (kstrtoint(buf, 0, &in_val)) > + return -EINVAL; > + if ((in_val >= integer_data[instance_id].lower_bound) && > + (in_val <= integer_data[instance_id].upper_bound)) > + ret = 0; > + > + return ret; > +} > + > +attribute_s_property_show(display_name_language_code, integer); > +static struct kobj_attribute integer_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, integer); > +struct kobj_attribute integer_displ_name = > + __ATTR_RO(display_name); > + > +attribute_n_property_show(default_value, integer); > +struct kobj_attribute integer_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, integer); > +struct kobj_attribute integer_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, integer); > +struct kobj_attribute integer_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(lower_bound, integer); > +struct kobj_attribute integer_lower_bound = > + __ATTR_RO(lower_bound); > + > +attribute_n_property_show(upper_bound, integer); > +struct kobj_attribute integer_upper_bound = > + __ATTR_RO(upper_bound); > + > +attribute_n_property_show(scalar_increment, integer); > +struct kobj_attribute integer_scalar_increment = > + __ATTR_RO(scalar_increment); > + > +static struct attribute *integer_attrs[] = { > + &integer_displ_langcode.attr, > + &integer_displ_name.attr, > + &integer_default_val.attr, > + &integer_current_val.attr, > + &integer_modifier.attr, > + &integer_lower_bound.attr, > + &integer_upper_bound.attr, > + &integer_scalar_increment.attr, > + NULL, > +}; > + > +static const struct attribute_group integer_attr_group = { > + .attrs = integer_attrs, > +}; > + > +int alloc_int_data(void) > +{ > + int ret = 0; > + > + integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + integer_data = kzalloc((sizeof(struct integer_data) * integer_instances_count), GFP_KERNEL); > + if (!integer_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under integer attribute > + * @integer_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &integer_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(integer_data[instance_id].attribute_name, > + integer_obj[ATTR_NAME].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name_language_code, > + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name, > + integer_obj[DISPLAY_NAME].string.pointer); > + integer_data[instance_id].default_value = > + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; > + integer_data[instance_id].current_value = > + (uintptr_t)integer_obj[CURRENT_VAL].string.pointer; > + strncpy_attr(integer_data[instance_id].modifier, integer_obj[MODIFIER].string.pointer); > + integer_data[instance_id].lower_bound = > + (uintptr_t)integer_obj[LOWER_BOUND].string.pointer; > + integer_data[instance_id].upper_bound = > + (uintptr_t)integer_obj[UPPER_BOUND].string.pointer; > + integer_data[instance_id].scalar_increment = > + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_int_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_int_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &integer_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(integer_data); > + mutex_unlock(&call_mutex); > +} > 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..d430ecf2387b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c > @@ -0,0 +1,161 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +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; > +}; > + > +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; > + > + 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; > + > + ret = set_current_password(kobj->name, buf); > + 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); > + > +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, > + 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(&call_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; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_po_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_po_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &po_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(po_data); > + mutex_unlock(&call_mutex); > +} > 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..9d7ac36e232f > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c > @@ -0,0 +1,230 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET password methods under BIOS attributes interface GUID > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + char current_admin_password[MAX_BUFF]; > + char current_system_password[MAX_BUFF]; > +}; > +static LIST_HEAD(interface_list); > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_current_password() - Get the current stored password value > + * @password_type: The type of password to store > + **/ > +char *get_current_password(const char *password_type) > +{ > + struct wmi_interface_priv *priv = get_first_interface_priv(); > + > + if (!priv) { > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + return NULL; > + } > + if (strcmp(password_type, "Admin") == 0) > + return priv->current_admin_password; > + if (strcmp(password_type, "System") == 0) > + return priv->current_system_password; > + dev_err(&priv->wdev->dev, "unknown password type %s", password_type); > + return NULL; > +} > + > +/** > + * set_current_password() - Store current password > + * @password_type: The type of password to store > + * @current: The current value > + * > + * Sets the current value of the password. > + * This is used for: > + * - Resetting password > + * - Basis for any functions requiring password to execute > + **/ > +int set_current_password(const char *password_type, const char *cur) > +{ > + char *current_password = get_current_password(password_type); > + > + if (!current_password) > + return -ENODEV; > + strncpy(current_password, cur, (strlen(cur) + 1)); > + return 0; > +} > + > +/** > + * set_new_password() - Sets a system admin password > + * @password_type: The type of password to set > + * @new: The new password > + * > + * Sets the password using plaintext interface > + **/ > +int set_new_password(const char *password_type, const char *new) > +{ > + int ret = -1; > + int i; > + u8 *type_len, *old_len, *new_len; > + char *current_password, *type_value, *old_value, *new_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_close_mutex; > + } > + current_password = get_current_password(password_type); > + if (!current_password) { > + ret = -ENODEV; > + goto out_close_mutex; > + } > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + string_area_size = (strlen(password_type) + strlen(current_password) + strlen(new))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 3; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + > + if (!buffer) { > + ret = -ENOMEM; > + goto out_close_mutex; > + } > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + type_len = buffer + security_area_size; > + type_value = type_len + sizeof(u16); > + old_len = type_value + strlen(password_type)*2; > + old_value = old_len + sizeof(u16); > + new_len = old_value + strlen(current_password)*2; > + new_value = new_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *type_len = strlen(password_type)*2; > + *old_len = strlen(current_password)*2; > + *new_len = strlen(new)*2; > + for (i = 0; i < strlen(password_type); i++) > + type_value[i*2] = password_type[i]; > + for (i = 0; i < strlen(current_password); i++) > + old_value[i*2] = current_password[i]; > + for (i = 0; i < strlen(new); i++) > + new_value[i*2] = new[i]; > + > + ret = call_password_interface(priv->wdev, buffer, buffer_size); > + /* update current password so commands work after reset */ > + if (!ret) > + ret = set_current_password(password_type, new); > + /* explain to user the detailed failure reason */ > + else if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + kfree(buffer); > + > +out_close_mutex: > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_pass_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_pass_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_pass_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-password" > + }, > + .probe = dell_wmi_bios_attr_pass_interface_probe, > + .remove = dell_wmi_bios_attr_pass_interface_remove, > + .id_table = dell_wmi_bios_attr_pass_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_pass_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_pass_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_pass_interface_id_table); > diff --git a/drivers/platform/x86/dell-wmi-string-attributes.c b/drivers/platform/x86/dell-wmi-string-attributes.c > new file mode 100644 > index 000000000000..562d09055dd1 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-string-attributes.c > @@ -0,0 +1,170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to string type attributes under BIOS String GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum string_properties {MIN_LEN = 6, MAX_LEN}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct str_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + char default_value[MAX_BUFF]; > + char current_value[MAX_BUFF]; > + char modifier[MAX_BUFF]; > + int min_length; > + int max_length; > +}; > + > +static struct str_data *str_data; > +static int str_instances_count; > +get_instance_id(str); > + > +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_str_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + strncpy_attr(str_data[instance_id].current_value, > + obj->package.elements[CURRENT_VAL].string.pointer); > + kfree(obj); > + return sprintf(buf, "%s\n", str_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_str_input() - Validate input of current_value against min and max lengths > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_str_input(int instance_id, const char *buf) > +{ > + int in_len = strlen(buf); > + > + if ((in_len >= str_data[instance_id].min_length) && > + (in_len <= str_data[instance_id].max_length)) > + return 0; > + > + return -EINVAL; > +} > + > +attribute_s_property_show(display_name_language_code, str); > +static struct kobj_attribute str_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, str); > +struct kobj_attribute str_displ_name = > + __ATTR_RO(display_name); > + > +attribute_s_property_show(default_value, str); > +struct kobj_attribute str_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, str); > +struct kobj_attribute str_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, str); > +struct kobj_attribute str_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(min_length, str); > +struct kobj_attribute str_min_length = > + __ATTR_RO(min_length); > + > +attribute_n_property_show(max_length, str); > +struct kobj_attribute str_max_length = > + __ATTR_RO(max_length); > + > +static struct attribute *str_attrs[] = { > + &str_displ_langcode.attr, > + &str_displ_name.attr, > + &str_default_val.attr, > + &str_current_val.attr, > + &str_modifier.attr, > + &str_min_length.attr, > + &str_max_length.attr, > + NULL, > +}; > + > +static const struct attribute_group str_attr_group = { > + .attrs = str_attrs, > +}; > + > +int alloc_str_data(void) > +{ > + int ret = 0; > + > + str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + str_data = kzalloc((sizeof(struct str_data) * str_instances_count), GFP_KERNEL); > + if (!str_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under string attribute > + * @str_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &str_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(str_data[instance_id].attribute_name, str_obj[ATTR_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].display_name_language_code, > + str_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(str_data[instance_id].display_name, str_obj[DISPLAY_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].default_value, str_obj[DEFAULT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].current_value, str_obj[CURRENT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].modifier, str_obj[MODIFIER].string.pointer); > + str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer; > + str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_str_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_str_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &str_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(str_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.c b/drivers/platform/x86/dell-wmi-sysman-attributes.c > new file mode 100644 > index 000000000000..485545ab6c8b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.c > @@ -0,0 +1,575 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Common methods for use with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/dmi.h> > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +#define MAX_TYPES 4 > + > +static struct platform_device *platform_device; > + > +static struct platform_driver platform_driver = { > + .driver = { > + .name = DRIVER_NAME, > + }, > +}; > + > +/* attribute directory under platform dev */ > +struct kset *main_dir_kset; > +/* subtypes of attributes */ > +struct kset *type_dir_kset[MAX_TYPES]; > + > +/* reset bios to defaults */ > +static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; > +static int reset_option = -1; > + > +/** > + * populate_security_buffer() - builds a security buffer for authentication scheme > + * @buffer: the buffer to populate > + * @authentication: the authentication content > + * > + * Currently only supported type is PLAIN TEXT > + **/ > +void populate_security_buffer(char *buffer, char *authentication) > +{ > + u32 *sectype = (u32 *) buffer; > + u32 *seclen = sectype + 1; > + char *auth = buffer + sizeof(u32)*2; > + /* plain text */ > + *sectype = 1; > + *seclen = strlen(authentication); > + strncpy(auth, authentication, *seclen); > +} > + > +/** > + * map_wmi_error() - map errors from WMI methods to kernel error codes > + **/ > +int map_wmi_error(int error_code) > +{ > + switch (error_code) { > + case 0: > + /* success */ > + return 0; > + case 1: > + /* failed */ > + return -EIO; > + case 2: > + /* invalid parameter */ > + return -EINVAL; > + case 3: > + /* access denied */ > + return -EACCES; > + case 4: > + /* not supported */ > + return -EOPNOTSUPP; > + case 5: > + /* memory error */ > + return -ENOMEM; > + case 6: > + /* protocol error */ > + return -EPROTO; > + } > + /* unspecified error */ > + return -EIO; > +} > + > +/** > + * reset_bios_show() - sysfs implementaton for read reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + **/ > +static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) > +{ > + int i; > + char *start = buf; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (i == reset_option) > + buf += sprintf(buf, "[%s] ", reset_types[i]); > + else > + buf += sprintf(buf, "%s ", reset_types[i]); > + } > + buf += sprintf(buf, "\n"); > + return buf-start; > +} > + > +/** > + * reset_bios_store() - sysfs implementaton for write reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer from userspace > + * @count: the size of the buffer from userspace > + **/ > +static ssize_t reset_bios_store(struct kobject *kobj, > + struct kobj_attribute *attr, const char *buf, size_t count) > +{ > + int len, ret, i; > + int type = -1; > + char *p; > + > + p = memchr(buf, '\n', count); > + if (p != NULL) > + *p = '\0'; > + len = p ? p - buf : count; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (len == strlen(reset_types[i]) > + && !strncmp(buf, reset_types[i], len)) { > + type = i; > + break; > + } > + } > + > + if (type < 0 || type >= MAX_TYPES) { > + ret = -EINVAL; > + goto out; > + } > + > + ret = set_bios_defaults(type); > + dev_dbg(&platform_device->dev, "reset all attributes request type %d: %d", type, ret); > + if (ret) { > + ret = -EINVAL; > + } else { > + reset_option = type; > + ret = count; > + } > + > +out: > + return ret; > +} > + > +/** > + * pending_reboot_show() - sysfs implementaton for read pending_reboot > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + * > + * Stores default value as 0 > + * When current_value is changed this attribute is set to 1 to notify reboot may be required > + **/ > +static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%d\n", get_pending_changes()); > +} > + > +static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); > +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); > + > + > +/** > + * create_reset_bios() - Creates reset_bios and pending_reboot attributes > + **/ > +static int create_reset_bios(void) > +{ > + int ret = sysfs_create_file(&main_dir_kset->kobj, &reset_bios.attr); > + > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset_bios file"); > + return ret; > + } > + > + ret = sysfs_create_file(&main_dir_kset->kobj, &pending_reboot.attr); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create changing_pending_reboot file"); > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + } > + return ret; > +} > + > +static void release_reset_bios_data(void) > +{ > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + sysfs_remove_file(&main_dir_kset->kobj, &pending_reboot.attr); > +} > + > +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, > + char *buf) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->show) > + ret = kattr->show(kobj, kattr, buf); > + return ret; > +} > + > +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, > + const char *buf, size_t count) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->store) > + ret = kattr->store(kobj, kattr, buf, count); > + return ret; > +} > + > +const struct sysfs_ops kobj_sysfs_ops = { > + .show = kobj_attr_show, > + .store = kobj_attr_store, > +}; > + > +static void attr_name_release(struct kobject *kobj) > +{ > + kfree(kobj); > +} > + > +static struct kobj_type attr_name_ktype = { > + .release = attr_name_release, > + .sysfs_ops = &kobj_sysfs_ops, > +}; > + > +/** > + * strncpy_attr - Copy a length-limited, NULL-terminated string with bound checks > + * @dest: Where to copy the string to > + * @src: Where to copy the string from > + **/ > +void strncpy_attr(char *dest, char *src) > +{ > + size_t len = strlen(src) + 1; > + > + if (len > 1 && len < MAX_BUFF) > + strncpy(dest, src, len); > +} > + > +/** > + * get_wmiobj_pointer() - Get Content of WMI block for particular instance > + * @instance_id: WMI instance ID > + * @guid_string: WMI GUID (in str form) > + * > + * Fetches the content for WMI block (instance_id) under GUID (guid_string) > + * Caller must kfree the return > + **/ > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) > +{ > + acpi_status status; > + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; > + > + status = wmi_query_block(guid_string, instance_id, &out); > + > + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; > +} > + > +/** > + * get_instance_count() - Compute total number of instances under guid_string > + * @guid_string: WMI GUID (in string form) > + **/ > +int get_instance_count(const char *guid_string) > +{ > + int i = 0; > + union acpi_object *wmi_obj = NULL; > + > + do { > + kfree(wmi_obj); > + wmi_obj = get_wmiobj_pointer(i, guid_string); > + i++; > + } while (wmi_obj); > + > + return (i-1); > +} > + > +/** > + * alloc_attributes_data() - Allocate attributes data for a particular type > + * @attr_type: Attribute type to allocate > + **/ > +static int alloc_attributes_data(int attr_type) > +{ > + int retval = 0; > + char *type[MAX_TYPES] = {"enumeration", "integer", "string", "password"}; > + > + type_dir_kset[attr_type] = kset_create_and_add(type[attr_type], NULL, &main_dir_kset->kobj); > + if (!type_dir_kset[attr_type]) > + goto err_bios_attr; > + > + switch (attr_type) { > + case ENUM: > + retval = alloc_enum_data(); > + break; > + case INT: > + retval = alloc_int_data(); > + break; > + case STR: > + retval = alloc_str_data(); > + break; > + case PO: > + retval = alloc_po_data(); > + break; > + default: > + break; > + } > + > + goto out; > + > +err_bios_attr: > + kset_unregister(main_dir_kset); > + > +out: > + return retval; > +} > + > +/** > + * destroy_attribute_objs() - Free a kset of kobjects > + * @kset: The kset to destroy > + * > + * Fress kobjects created for each attribute_name under attribute type kset > + **/ > +static void destroy_attribute_objs(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + kobject_put(pos); > + } > + mutex_unlock(&list_mutex); > +} > + > +/** > + * release_attributes_data() - Clean-up all sysfs directories and files created > + **/ > +static void release_attributes_data(void) > +{ > + release_reset_bios_data(); > + > + exit_enum_attributes(type_dir_kset[ENUM]); > + exit_int_attributes(type_dir_kset[INT]); > + exit_str_attributes(type_dir_kset[STR]); > + exit_po_attributes(type_dir_kset[PO]); > + > + mutex_lock(&call_mutex); > + if (main_dir_kset) { > + int i; > + > + /* unregister all 4 types ENUM,INT,STR and PO */ > + for (i = ENUM; i <= PO; i++) { > + destroy_attribute_objs(type_dir_kset[i]); > + kset_unregister(type_dir_kset[i]); > + } > + kset_unregister(main_dir_kset); > + } > + mutex_unlock(&call_mutex); > +} > + > +/** > + * init_bios_attributes() - Initialize all attributes for a type > + * @attr_type: The attribute type to initialize > + * @guid: The WMI GUID associated with this type to initialize > + * > + * Initialiaze all 4 types of attributes enumeration, integer, string and password object. > + * Populates each attrbute typ's respective properties under sysfs files > + **/ > +static int init_bios_attributes(int attr_type, const char *guid) > +{ > + union acpi_object *obj = NULL; > + union acpi_object *elements; > + int retval = 0; > + int instance_id = 0; > + struct kobject *attr_name_kobj; //individual attribute names > + > + retval = alloc_attributes_data(attr_type); > + if (retval) > + return retval; > + obj = get_wmiobj_pointer(instance_id, guid); > + if (!obj) > + return -ENODEV; > + elements = obj->package.elements; > + > + while (elements) { > + /* sanity checking */ > + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { > + dev_dbg(&platform_device->dev, "empty attribute found"); > + goto nextobj; > + } > + if (kset_find_obj(type_dir_kset[attr_type], elements[ATTR_NAME].string.pointer)) { > + dev_dbg(&platform_device->dev, "duplicate attribute name found - %s", > + elements[ATTR_NAME].string.pointer); > + goto nextobj; > + } > + > + /* build attribute */ > + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); > + if (!attr_name_kobj) > + goto err_attr_init; > + > + attr_name_kobj->kset = type_dir_kset[attr_type]; > + > + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", > + elements[ATTR_NAME].string.pointer); > + if (retval) { > + kobject_put(attr_name_kobj); > + goto err_attr_init; > + } > + > + /* enumerate all of this attribute */ > + switch (attr_type) { > + case ENUM: > + retval = populate_enum_data(elements, instance_id, attr_name_kobj); > + break; > + case INT: > + retval = populate_int_data(elements, instance_id, attr_name_kobj); > + break; > + case STR: > + retval = populate_str_data(elements, instance_id, attr_name_kobj); > + break; > + case PO: > + retval = populate_po_data(elements, instance_id, attr_name_kobj); > + break; > + default: > + break; > + } > + > + if (retval) { > + dev_dbg(&platform_device->dev, "failed to populate %s", > + elements[ATTR_NAME].string.pointer); > + goto err_attr_init; > + } > + > +nextobj: > + kfree(obj); > + instance_id++; > + obj = get_wmiobj_pointer(instance_id, guid); > + elements = obj ? obj->package.elements : NULL; > + } > + > + goto out; > + > +err_attr_init: > + release_attributes_data(); > + kfree(obj); > +out: > + return retval; > +} > + > +static int __init init_dell_bios_attrib_wmi(void) > +{ > + int ret = 0; > + > + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && > + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { > + pr_err("Unable to run on non-Dell system\n"); > + return -ENODEV; > + } > + > + ret = platform_driver_register(&platform_driver); > + if (ret) > + goto fail_platform_driver; > + > + platform_device = platform_device_alloc(DRIVER_NAME, -1); > + if (!platform_device) { > + ret = -ENOMEM; > + goto fail_platform_device_alloc; > + } > + > + ret = platform_device_add(platform_device); > + if (ret) > + goto fail_platform_device_add; > + > + main_dir_kset = kset_create_and_add("attributes", NULL, &platform_device->dev.kobj); > + if (!main_dir_kset) { > + ret = -ENOMEM; > + goto fail_platform_device_add; > + } > + > + ret = init_dell_wmi_bios_attr_set_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize set interface"); > + goto fail_kset; > + } > + > + ret = init_dell_wmi_bios_attr_pass_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize pass interface"); > + goto fail_set_interface; > + } > + > + ret = create_reset_bios(); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset BIOS attribute"); > + goto fail_pass_interface; > + } > + > + ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate enumeration type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate integer type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate string type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate pass object type attributes"); > + goto fail_create_group; > + } > + > + return 0; > + > +fail_create_group: > + release_attributes_data(); > + platform_device_del(platform_device); > + > +fail_pass_interface: > + exit_dell_wmi_bios_attr_pass_interface(); > + > +fail_set_interface: > + exit_dell_wmi_bios_attr_set_interface(); > + > +fail_kset: > + kset_unregister(main_dir_kset); > + > +fail_platform_device_add: > + platform_device_put(platform_device); > + > +fail_platform_device_alloc: > + platform_driver_unregister(&platform_driver); > + > +fail_platform_driver: > + return ret; > +} > + > +static void __exit exit_dell_bios_attrib_wmi(void) > +{ > + release_attributes_data(); > + mutex_lock(&call_mutex); > + exit_dell_wmi_bios_attr_set_interface(); > + exit_dell_wmi_bios_attr_pass_interface(); > + if (platform_device) { > + platform_device_unregister(platform_device); > + platform_driver_unregister(&platform_driver); > + } > + mutex_unlock(&call_mutex); > +} > + > +module_init(init_dell_bios_attrib_wmi); > +module_exit(exit_dell_bios_attrib_wmi); > + > +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); > +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); > +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); > +MODULE_DESCRIPTION("Dell platform setting control interface"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.h b/drivers/platform/x86/dell-wmi-sysman-attributes.h > new file mode 100644 > index 000000000000..bb5053b91ec7 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.h > @@ -0,0 +1,125 @@ > +/* SPDX-License-Identifier: GPL-2.0 > + * Definitions for kernel modules using Dell WMI System Management Driver > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#ifndef _DELL_WMI_BIOS_ATTR_H_ > +#define _DELL_WMI_BIOS_ATTR_H_ > + > +#include <linux/wmi.h> > +#include <linux/device.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/capability.h> > + > +#define DRIVER_NAME "dell-wmi-sysman" > +#define MAX_BUFF 512 > + > +#define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF5" > +#define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BFA" > +#define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF9" > +#define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719-97D7-6AD24108BFD4" > +#define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF4" > +#define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214-A1C6-1F884B1A892A" > + > +enum { ENUM, INT, STR, PO }; > + > +enum { > + ATTR_NAME, > + DISPL_NAME_LANG_CODE, > + DISPLAY_NAME, > + DEFAULT_VAL, > + CURRENT_VAL, > + MODIFIER > +}; > + > +#define get_instance_id(type) \ > +int get_##type##_instance_id(struct kobject *kobj) \ > +{ \ > + int i; \ > + for (i = 0; i <= type##_instances_count; i++) { \ > + if (!(strcmp(kobj->name, type##_data[i].attribute_name))) \ > + return i; \ > + } \ > + return -EIO; \ > +} > + > +#define attribute_s_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%s\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_n_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%d\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_property_store(curr_val, type) \ > +static ssize_t curr_val##_store(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + const char *buf, size_t count) \ > +{ \ > + char *p = memchr(buf, '\n', count); \ > + int ret = -EIO; \ > + int i; \ > + \ > + if (p != NULL) \ > + *p = '\0'; \ > + i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + ret = validate_##type##_input(i, buf); \ > + if (!ret) \ > + ret = set_attribute(kobj->name, buf); \ > + return ret ? ret : count; \ > +} > + > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); > +int get_instance_count(const char *guid_string); > +void strncpy_attr(char *dest, char *src); > + > +int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_enum_data(void); > +void exit_enum_attributes(struct kset *kset); > + > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_int_data(void); > +void exit_int_attributes(struct kset *kset); > + > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_str_data(void); > +void exit_str_attributes(struct kset *kset); > + > +int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_po_data(void); > +void exit_po_attributes(struct kset *kset); > + > +int set_attribute(const char *a_name, const char *a_value); > +int set_bios_defaults(u8 defType); > + > +void exit_dell_wmi_bios_attr_set_interface(void); > +int init_dell_wmi_bios_attr_set_interface(void); > +bool get_pending_changes(void); > +int map_wmi_error(int error_code); > +void populate_security_buffer(char *buffer, char *authentication); > + > +char *get_current_password(const char *password_type); > +int set_current_password(const char *password_type, const char *cur); > +int set_new_password(const char *password_type, const char *new); > +int init_dell_wmi_bios_attr_pass_interface(void); > +void exit_dell_wmi_bios_attr_pass_interface(void); > + > +#endif >
Hi, So as promised a third mail with a review focused some more on the code. On 7/30/20 4:31 PM, Divya Bharathi wrote: > From: Divya Bharathi <divya.bharathi@dell.com> > > 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. > > Co-developed-by: Mario Limonciello <mario.limonciello@dell.com> > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com> > Co-developed-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Prasanth KSR <prasanth.ksr@dell.com> > Signed-off-by: Divya Bharathi <divya.bharathi@dell.com> > --- > NOTE: This patch series is intended to go on top of > platform-drivers-x86 linux-next. > > .../testing/sysfs-platform-dell-wmi-sysman | 201 ++++++ > MAINTAINERS | 9 + > drivers/platform/x86/Kconfig | 11 + > drivers/platform/x86/Makefile | 8 + > .../x86/dell-wmi-biosattr-interface.c | 263 ++++++++ > .../platform/x86/dell-wmi-enum-attributes.c | 207 +++++++ > .../platform/x86/dell-wmi-int-attributes.c | 188 ++++++ > .../x86/dell-wmi-passobj-attributes.c | 161 +++++ > .../x86/dell-wmi-passwordattr-interface.c | 230 +++++++ > .../platform/x86/dell-wmi-string-attributes.c | 170 ++++++ > .../platform/x86/dell-wmi-sysman-attributes.c | 575 ++++++++++++++++++ > .../platform/x86/dell-wmi-sysman-attributes.h | 125 ++++ > 12 files changed, 2148 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/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > new file mode 100644 > index 000000000000..8ca3e502ed89 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman > @@ -0,0 +1,201 @@ > +What: /sys/devices/platform/dell-wmi-sysman/attributes/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + The Dell WMI Systems Management Driver provides a sysfs interface > + for systems management software to enable BIOS configuration > + capability on certain Dell systems. This directory exposes > + interfaces for interacting with BIOS attributes > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This attribute can be used to reset the BIOS Configuration. > + Specifically, it tells which type of reset BIOS configuration is being > + requested on the host. > + > + Reading from it returns a list of supported options encoded as: > + > + 'builtinsafe' (Built in safe configuration profile) > + 'lastknowngood' (Last known good saved configuration profile) > + 'factory' (Default factory settings configuration profile) > + 'custom' (Custom saved configuration profile) > + > + The currently selected option is printed in square brackets as > + shown below: > + > + # echo "factory" > sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + > + # cat sys/devices/platform/dell-wmi-sysman/attributes/reset_bios > + # builtinsafe lastknowngood [factory] custom > + > + Note that any changes to this attribute requires a reboot > + for changes to take effect. > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/pending_reboot > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + A read-only attribute enumerating if a reboot is pending > + on any BIOS attribute change. > + > + 0: No pending reboot > + 1: Pending reboot > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/enumeration/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS enumeration attributes. > + > + Enumeration attributes are settings that accept set of > + pre-defined valid values. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of a <attr> > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > + possible_value: A file that can be read to obtain the possible > + value of the <attr> > + > + value_modifier: A file that can be read to obtain value-level > + dependency on a possible value which has to be met to configure <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/integer/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS integer attributes. > + > + Integer attributes are settings that accept a range of > + numerical values for inputs. Each BIOS integer has a > + lower bound and an upper bound on the values that it can take. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + lower_bound: A file that can be read to obtain the lower > + bound value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > + scalar_increment: A file that can be read to obtain the > + resolution of the incremental value this attribute accepts. > + > + upper_bound: A file that can be read to obtain the upper > + bound value of the <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/string/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS string attributes. > + > + String attributes are settings that accept a string for an input. > + Each BIOS string is characterized by the minimum length of the string, > + the maximum length of the string, and the type of the string. > + > + current_value: A file that can be read to obtain the current > + value of the <attr> > + > + This file can also be written to in order to update > + the value of an <attr>. > + > + default_value: A file that can be read to obtain the default > + value of the <attr> > + > + display_name: A file that can be read to obtain a user friendly > + description of the at <attr> > + > + display_name_language_code: A file that can be read to obtain > + the language code corresponding to the "display_name" of the <attr> > + > + max_length: A file that can be read to obtain the maximum > + length value of the <attr> > + > + min_length: A file that can be read to obtain the minimum > + length value of the <attr> > + > + modifier: A file that can be read to obtain attribute-level > + dependency rule which has to be met to configure <attr> > + > +What: /sys/devices/platform/dell-wmi-sysman/attributes/password/<attr>/ > +Date: October 2020 > +KernelVersion: 5.9 > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > + Mario Limonciello <mario.limonciello@dell.com>, > + Prasanth KSR <prasanth.ksr@dell.com> > +Description: > + This directory exposes interfaces for interaction with > + BIOS password attributes. > + > + BIOS Admin password and System Password can be set, reset or cleared > + using these attributes. > + > + An "Admin" password is used for preventing modification to the BIOS settings. > + A "System" password is required to boot a machine. > + > + is_password_set: A file that can be read > + to obtain flag to see if a password is set on <attr> > + > + max_password_length: A file that can be read to obtain the > + maximum length of the Password > + > + min_password_length: A file that can be read to obtain the > + minimum length of the Password > + > + current_password: A write only value used for privileged access > + such as setting attributes when a system or admin password is set > + or resetting to a new password > + > + new_password: A write only value that when used in tandem with > + current_password will reset a system or admin password. > diff --git a/MAINTAINERS b/MAINTAINERS > index e64cdde81851..176311d712db 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4879,6 +4879,15 @@ M: Mario Limonciello <mario.limonciello@dell.com> > S: Maintained > F: drivers/platform/x86/dell-wmi-descriptor.c > > +DELL WMI SYSMAN DRIVER > +M: Divya Bharathi <divya.bharathi@dell.com> > +M: Mario Limonciello <mario.limonciello@dell.com> > +M: Prasanth Ksr <prasanth.ksr@dell.com> > +L: platform-driver-x86@vger.kernel.org > +S: Maintained > +F: drivers/platform/x86/dell-wmi-*-attributes.* > +F: drivers/platform/x86/dell-wmi-*-interface.c > + > DELL WMI NOTIFICATIONS DRIVER > M: Matthew Garrett <mjg59@srcf.ucam.org> > M: Pali Rohár <pali@kernel.org> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > index 0581a54cf562..de66373554c2 100644 > --- a/drivers/platform/x86/Kconfig > +++ b/drivers/platform/x86/Kconfig > @@ -430,6 +430,17 @@ config DELL_WMI > To compile this driver as a module, choose M here: the module will > be called dell-wmi. > > +config DELL_WMI_SYSMAN > + tristate "Dell WMI SYSMAN" > + depends on ACPI_WMI > + depends on DMI > + help > + This driver allows changing BIOS settings on many Dell machines from > + 2018 and newer without the use of any additional software. > + > + To compile this driver as a module, choose M here: the module will > + be called dell-wmi-sysman. > + > config DELL_WMI_DESCRIPTOR > tristate > depends on ACPI_WMI > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile > index 2b85852a1a87..bee03f1af28f 100644 > --- a/drivers/platform/x86/Makefile > +++ b/drivers/platform/x86/Makefile > @@ -47,6 +47,14 @@ obj-$(CONFIG_DELL_WMI) += dell-wmi.o > obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o > obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o > obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o > +obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o > +dell-wmi-sysman-objs := dell-wmi-sysman-attributes.o \ > + dell-wmi-enum-attributes.o \ > + dell-wmi-int-attributes.o \ > + dell-wmi-string-attributes.o \ > + dell-wmi-passobj-attributes.o \ > + dell-wmi-passwordattr-interface.o \ > + dell-wmi-biosattr-interface.o > > # Fujitsu > obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o > diff --git a/drivers/platform/x86/dell-wmi-biosattr-interface.c b/drivers/platform/x86/dell-wmi-biosattr-interface.c > new file mode 100644 > index 000000000000..decf124d4388 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-biosattr-interface.c > @@ -0,0 +1,263 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET methods under BIOS attributes interface GUID for use > + * with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +#define SETDEFAULTVALUES_METHOD_ID 0x02 > +#define SETBIOSDEFAULTS_METHOD_ID 0x03 > +#define SETATTRIBUTE_METHOD_ID 0x04 > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); These are defined multiple times in almost all of the files, if the purpose is to allow only 1 WMI call at a time then their should be only 1 define of the call_mutex. > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + bool pending_changes; > +}; > +static LIST_HEAD(interface_list); You use a list here; and also in drivers/platform/x86/dell-wmi-passwordattr-interface.c but you only every use the first entry of the list. Looking at the structure of the driver I think that it would be best to do the following: 1) Use a single global struct which contains the data from the struct wmi_interface_priv from both dell-wmi-biosattr-interface.c and dell-wmi-passwordattr-interface.c including separate wdev pointers for both and add a single shared call_mutex to this struct 2) Have the dell_wmi_bios_attr_set_interface_probe() functions each set their own wdev pointer in the global struct 3) Have drivers/platform/x86/dell-wmi-sysman-attributes.c 's init_dell_bios_attrib_wmi function first register the 2 wmi drivers and then check that both wdev pointers are set in the global struct to make sure both GUID's were found and the driver did successfully bound and move the platform_device (to be replaced with a class-dev) registration to the very end of init_dell_bios_attrib_wmi, so that sysfs files do not become available before the wdev pointers are both set (which I think is currently possinble). This refactoring will allow the removing of all the list_mutex-s and the 2 LIST_HEAD(interface_list) declarations; and it will also replace all the separate call_mutex locks with a single shared lock in the global shared data struct. I believe that this + the suggested API changes will result in enough refactoring (assuming that you agree) that a further detailed code review does not make a whole lot of sense right now. So first please address my current review remarks (or explain why you believe that things should be kept as is); and then we will see from there. Regards, Hans > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, > + int method_id) > +{ > + 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, method_id, &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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_pending_changes() - Fetch if any changes are pending > + * > + * Checks if any changes have been written that will be changed > + * after a system reboot > + **/ > +bool get_pending_changes(void) > +{ > + struct wmi_interface_priv *priv; > + > + priv = get_first_interface_priv(); > + if (priv) > + return priv->pending_changes; > + return 0; > +} > + > +/** > + * set_attribute() - Update an attribute value > + * @a_name: The attribute name > + * @a_value: The attribute value > + * > + * Sets an attribute to new value > + **/ > +int set_attribute(const char *a_name, const char *a_value) > +{ > + int ret = -1; > + int i; > + u8 *name_len, *value_len; > + char *current_password, *attribute_name, *attribute_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; Can we instead of passing "Admin" and "System" to this function just have 2 separate get_current_admin_password and get_current_system_password helpers and then drop the error handling ? > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; Since you are using more then 1 line here please use {} around the state-ments, also please put the /* password not set */ after the else: ... } else { /* password not set */ ... > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + name_len = buffer + security_area_size; > + attribute_name = name_len + sizeof(u16); > + value_len = attribute_name + strlen(a_name)*2; > + attribute_value = value_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *name_len = strlen(a_name)*2; > + *value_len = strlen(a_value)*2; > + for (i = 0; i < strlen(a_name); i++) > + attribute_name[i*2] = a_name[i]; > + for (i = 0; i < strlen(a_value); i++) > + attribute_value[i*2] = a_value[i]; This assumes the incoming string is ASCII only and won't work when the incoming string is UTF-8. It is probably better to use the utf8s_to_utf16s() helper from the nls code, this will mean adding a dependency on CONFIG_NLS which typically is used for filesystem code, but I think that that will be fine. > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_set_attribute; > + } > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETATTRIBUTE_METHOD_ID); > + if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + > + priv->pending_changes = 1; > +out_set_attribute: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +/** > + * set_bios_defaults() - Resets BIOS defaults > + * @deftype: the type of BIOS value reset to issue. > + * > + * Resets BIOS defaults > + **/ > +int set_bios_defaults(u8 deftype) > +{ > + int ret = -1; > + u8 *defaultType; > + char *current_password, *buffer; > + size_t security_area_size; > + size_t integer_area_size = sizeof(u8); > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + > + /* look up if user set a password for the requests */ > + current_password = get_current_password("Admin"); > + if (!current_password) > + return -ENODEV; > + > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + > + buffer_size = security_area_size + integer_area_size; > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_bios_defaults; > + } > + > + defaultType = buffer + security_area_size; > + *defaultType = deftype; > + > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > + SETBIOSDEFAULTS_METHOD_ID); > + if (ret) > + dev_err(&priv->wdev->dev, "reset BIOS defaults failed: %d", ret); > + priv->pending_changes = 1; > +out_bios_defaults: > + kfree(buffer); > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_set_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_set_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_set_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-set" > + }, > + .probe = dell_wmi_bios_attr_set_interface_probe, > + .remove = dell_wmi_bios_attr_set_interface_remove, > + .id_table = dell_wmi_bios_attr_set_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_set_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_set_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_set_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_set_interface_id_table); > 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..a2d8ae291d5c > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-enum-attributes.c > @@ -0,0 +1,207 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct enumeration_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[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_value_count; > + char possible_value[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; > + > + 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) > +{ > + int ret = -EINVAL; > + char *options, *tmp, *p; > + > + options = tmp = kstrdup((enumeration_data[instance_id].possible_value), 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_value, enumeration); > +struct kobj_attribute poss_val = > + __ATTR_RO(possible_value); > + > +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, > + 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 i, next_obj; > + int retval = sysfs_create_group(attr_name_kobj, &enumeration_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_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_value_count = > + (uintptr_t) enumeration_obj[++next_obj].string.pointer; > + > + for (i = 0; i < enumeration_data[instance_id].possible_value_count; i++) { > + strcat(enumeration_data[instance_id].possible_value, > + enumeration_obj[++next_obj].string.pointer); > + strcat(enumeration_data[instance_id].possible_value, ";"); > + } > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_enum_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_enum_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &enumeration_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(enumeration_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-int-attributes.c b/drivers/platform/x86/dell-wmi-int-attributes.c > new file mode 100644 > index 000000000000..222971be2b48 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-int-attributes.c > @@ -0,0 +1,188 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to integer type attributes under BIOS Integer GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum int_properties {LOWER_BOUND = 6, UPPER_BOUND, SCALAR_INCR}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct integer_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + int default_value; > + int current_value; > + char modifier[MAX_BUFF]; > + int lower_bound; > + int upper_bound; > + int scalar_increment; > +}; > + > +static struct integer_data *integer_data; > +static int integer_instances_count; > +get_instance_id(integer); > + > +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_integer_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + integer_data[instance_id].current_value = > + (uintptr_t)obj->package.elements[CURRENT_VAL].string.pointer; > + kfree(obj); > + return sprintf(buf, "%d\n", integer_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_integer_input() - Validate input of current_value against lower and upper bound > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_integer_input(int instance_id, const char *buf) > +{ > + int in_val; > + int ret = -EINVAL; > + > + if (kstrtoint(buf, 0, &in_val)) > + return -EINVAL; > + if ((in_val >= integer_data[instance_id].lower_bound) && > + (in_val <= integer_data[instance_id].upper_bound)) > + ret = 0; > + > + return ret; > +} > + > +attribute_s_property_show(display_name_language_code, integer); > +static struct kobj_attribute integer_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, integer); > +struct kobj_attribute integer_displ_name = > + __ATTR_RO(display_name); > + > +attribute_n_property_show(default_value, integer); > +struct kobj_attribute integer_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, integer); > +struct kobj_attribute integer_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, integer); > +struct kobj_attribute integer_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(lower_bound, integer); > +struct kobj_attribute integer_lower_bound = > + __ATTR_RO(lower_bound); > + > +attribute_n_property_show(upper_bound, integer); > +struct kobj_attribute integer_upper_bound = > + __ATTR_RO(upper_bound); > + > +attribute_n_property_show(scalar_increment, integer); > +struct kobj_attribute integer_scalar_increment = > + __ATTR_RO(scalar_increment); > + > +static struct attribute *integer_attrs[] = { > + &integer_displ_langcode.attr, > + &integer_displ_name.attr, > + &integer_default_val.attr, > + &integer_current_val.attr, > + &integer_modifier.attr, > + &integer_lower_bound.attr, > + &integer_upper_bound.attr, > + &integer_scalar_increment.attr, > + NULL, > +}; > + > +static const struct attribute_group integer_attr_group = { > + .attrs = integer_attrs, > +}; > + > +int alloc_int_data(void) > +{ > + int ret = 0; > + > + integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + integer_data = kzalloc((sizeof(struct integer_data) * integer_instances_count), GFP_KERNEL); > + if (!integer_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under integer attribute > + * @integer_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &integer_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(integer_data[instance_id].attribute_name, > + integer_obj[ATTR_NAME].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name_language_code, > + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(integer_data[instance_id].display_name, > + integer_obj[DISPLAY_NAME].string.pointer); > + integer_data[instance_id].default_value = > + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; > + integer_data[instance_id].current_value = > + (uintptr_t)integer_obj[CURRENT_VAL].string.pointer; > + strncpy_attr(integer_data[instance_id].modifier, integer_obj[MODIFIER].string.pointer); > + integer_data[instance_id].lower_bound = > + (uintptr_t)integer_obj[LOWER_BOUND].string.pointer; > + integer_data[instance_id].upper_bound = > + (uintptr_t)integer_obj[UPPER_BOUND].string.pointer; > + integer_data[instance_id].scalar_increment = > + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_int_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_int_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &integer_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(integer_data); > + mutex_unlock(&call_mutex); > +} > 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..d430ecf2387b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c > @@ -0,0 +1,161 @@ > +// 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" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +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; > +}; > + > +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; > + > + 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; > + > + ret = set_current_password(kobj->name, buf); > + 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); > + > +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, > + 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(&call_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; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_po_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_po_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &po_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(po_data); > + mutex_unlock(&call_mutex); > +} > 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..9d7ac36e232f > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c > @@ -0,0 +1,230 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to SET password methods under BIOS attributes interface GUID > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +struct wmi_interface_priv { > + struct list_head list; > + struct wmi_device *wdev; > + struct device *child; > + char current_admin_password[MAX_BUFF]; > + char current_system_password[MAX_BUFF]; > +}; > +static LIST_HEAD(interface_list); > + > +static inline struct wmi_interface_priv *get_first_interface_priv(void) > +{ > + return list_first_entry_or_null(&interface_list, > + struct wmi_interface_priv, > + list); > +} > + > +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); > + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); > + return map_wmi_error(ret); > +} > + > +/** > + * get_current_password() - Get the current stored password value > + * @password_type: The type of password to store > + **/ > +char *get_current_password(const char *password_type) > +{ > + struct wmi_interface_priv *priv = get_first_interface_priv(); > + > + if (!priv) { > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + return NULL; > + } > + if (strcmp(password_type, "Admin") == 0) > + return priv->current_admin_password; > + if (strcmp(password_type, "System") == 0) > + return priv->current_system_password; > + dev_err(&priv->wdev->dev, "unknown password type %s", password_type); > + return NULL; > +} > + > +/** > + * set_current_password() - Store current password > + * @password_type: The type of password to store > + * @current: The current value > + * > + * Sets the current value of the password. > + * This is used for: > + * - Resetting password > + * - Basis for any functions requiring password to execute > + **/ > +int set_current_password(const char *password_type, const char *cur) > +{ > + char *current_password = get_current_password(password_type); > + > + if (!current_password) > + return -ENODEV; > + strncpy(current_password, cur, (strlen(cur) + 1)); > + return 0; > +} > + > +/** > + * set_new_password() - Sets a system admin password > + * @password_type: The type of password to set > + * @new: The new password > + * > + * Sets the password using plaintext interface > + **/ > +int set_new_password(const char *password_type, const char *new) > +{ > + int ret = -1; > + int i; > + u8 *type_len, *old_len, *new_len; > + char *current_password, *type_value, *old_value, *new_value; > + size_t security_area_size; > + size_t string_area_size; > + size_t buffer_size; > + struct wmi_interface_priv *priv; > + char *buffer; > + > + mutex_lock(&call_mutex); > + priv = get_first_interface_priv(); > + if (!priv) { > + ret = -ENODEV; > + pr_err(DRIVER_NAME ": no WMI backend bound"); > + goto out_close_mutex; > + } > + current_password = get_current_password(password_type); > + if (!current_password) { > + ret = -ENODEV; > + goto out_close_mutex; > + } > + /* password is set */ > + if (strlen(current_password) > 0) > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > + strlen(current_password) % 2; > + /* password not set */ > + else > + security_area_size = sizeof(u32) * 2; > + string_area_size = (strlen(password_type) + strlen(current_password) + strlen(new))*2; > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 3; > + > + buffer = kzalloc(buffer_size, GFP_KERNEL); > + > + if (!buffer) { > + ret = -ENOMEM; > + goto out_close_mutex; > + } > + > + /* build security area */ > + if (strlen(current_password) > 0) > + populate_security_buffer(buffer, current_password); > + > + type_len = buffer + security_area_size; > + type_value = type_len + sizeof(u16); > + old_len = type_value + strlen(password_type)*2; > + old_value = old_len + sizeof(u16); > + new_len = old_value + strlen(current_password)*2; > + new_value = new_len + sizeof(u16); > + > + /* turn into UTF16 strings, no NULL terminator */ > + *type_len = strlen(password_type)*2; > + *old_len = strlen(current_password)*2; > + *new_len = strlen(new)*2; > + for (i = 0; i < strlen(password_type); i++) > + type_value[i*2] = password_type[i]; > + for (i = 0; i < strlen(current_password); i++) > + old_value[i*2] = current_password[i]; > + for (i = 0; i < strlen(new); i++) > + new_value[i*2] = new[i]; > + > + ret = call_password_interface(priv->wdev, buffer, buffer_size); > + /* update current password so commands work after reset */ > + if (!ret) > + ret = set_current_password(password_type, new); > + /* explain to user the detailed failure reason */ > + else if (ret == -EOPNOTSUPP) > + dev_err(&priv->wdev->dev, "admin password must be configured"); > + else if (ret == -EACCES) > + dev_err(&priv->wdev->dev, "invalid password"); > + kfree(buffer); > + > +out_close_mutex: > + mutex_unlock(&call_mutex); > + > + return ret; > +} > + > +static int dell_wmi_bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) > +{ > + struct wmi_interface_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), > + GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + mutex_lock(&list_mutex); > + list_add_tail(&priv->list, &interface_list); > + mutex_unlock(&list_mutex); > + return 0; > +} > + > +static int dell_wmi_bios_attr_pass_interface_remove(struct wmi_device *wdev) > +{ > + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); > + > + mutex_lock(&call_mutex); > + mutex_lock(&list_mutex); > + list_del(&priv->list); > + mutex_unlock(&list_mutex); > + mutex_unlock(&call_mutex); > + return 0; > +} > + > +static const struct wmi_device_id dell_wmi_bios_attr_pass_interface_id_table[] = { > + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, > + { }, > +}; > +static struct wmi_driver dell_wmi_bios_attr_pass_interface_driver = { > + .driver = { > + .name = DRIVER_NAME"-password" > + }, > + .probe = dell_wmi_bios_attr_pass_interface_probe, > + .remove = dell_wmi_bios_attr_pass_interface_remove, > + .id_table = dell_wmi_bios_attr_pass_interface_id_table, > +}; > + > +int init_dell_wmi_bios_attr_pass_interface(void) > +{ > + return wmi_driver_register(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +void exit_dell_wmi_bios_attr_pass_interface(void) > +{ > + wmi_driver_unregister(&dell_wmi_bios_attr_pass_interface_driver); > +} > + > +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_pass_interface_id_table); > diff --git a/drivers/platform/x86/dell-wmi-string-attributes.c b/drivers/platform/x86/dell-wmi-string-attributes.c > new file mode 100644 > index 000000000000..562d09055dd1 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-string-attributes.c > @@ -0,0 +1,170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Functions corresponding to string type attributes under BIOS String GUID for use with > + * dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +enum string_properties {MIN_LEN = 6, MAX_LEN}; > + > +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ > +struct str_data { > + char attribute_name[MAX_BUFF]; > + char display_name_language_code[MAX_BUFF]; > + char display_name[MAX_BUFF]; > + char default_value[MAX_BUFF]; > + char current_value[MAX_BUFF]; > + char modifier[MAX_BUFF]; > + int min_length; > + int max_length; > +}; > + > +static struct str_data *str_data; > +static int str_instances_count; > +get_instance_id(str); > + > +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_str_instance_id(kobj); > + if (instance_id >= 0) { > + union acpi_object *obj; > + > + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (!obj) > + return -AE_ERROR; > + strncpy_attr(str_data[instance_id].current_value, > + obj->package.elements[CURRENT_VAL].string.pointer); > + kfree(obj); > + return sprintf(buf, "%s\n", str_data[instance_id].current_value); > + } > + return -EIO; > +} > + > +/** > + * validate_str_input() - Validate input of current_value against min and max lengths > + * @instance_id: The instance on which input is validated > + * @buf: Input value > + **/ > +int validate_str_input(int instance_id, const char *buf) > +{ > + int in_len = strlen(buf); > + > + if ((in_len >= str_data[instance_id].min_length) && > + (in_len <= str_data[instance_id].max_length)) > + return 0; > + > + return -EINVAL; > +} > + > +attribute_s_property_show(display_name_language_code, str); > +static struct kobj_attribute str_displ_langcode = > + __ATTR_RO(display_name_language_code); > + > +attribute_s_property_show(display_name, str); > +struct kobj_attribute str_displ_name = > + __ATTR_RO(display_name); > + > +attribute_s_property_show(default_value, str); > +struct kobj_attribute str_default_val = > + __ATTR_RO(default_value); > + > +attribute_property_store(current_value, str); > +struct kobj_attribute str_current_val = > + __ATTR_RW(current_value); > + > +attribute_s_property_show(modifier, str); > +struct kobj_attribute str_modifier = > + __ATTR_RO(modifier); > + > +attribute_n_property_show(min_length, str); > +struct kobj_attribute str_min_length = > + __ATTR_RO(min_length); > + > +attribute_n_property_show(max_length, str); > +struct kobj_attribute str_max_length = > + __ATTR_RO(max_length); > + > +static struct attribute *str_attrs[] = { > + &str_displ_langcode.attr, > + &str_displ_name.attr, > + &str_default_val.attr, > + &str_current_val.attr, > + &str_modifier.attr, > + &str_min_length.attr, > + &str_max_length.attr, > + NULL, > +}; > + > +static const struct attribute_group str_attr_group = { > + .attrs = str_attrs, > +}; > + > +int alloc_str_data(void) > +{ > + int ret = 0; > + > + str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + str_data = kzalloc((sizeof(struct str_data) * str_instances_count), GFP_KERNEL); > + if (!str_data) > + ret = -ENOMEM; > + return ret; > +} > + > +/** > + * populate_enum_data() - Populate all properties of an instance under string attribute > + * @str_obj: ACPI object with integer data > + * @instance_id: The instance to enumerate > + * @attr_name_kobj: The parent kernel object > + **/ > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) > +{ > + int retval = sysfs_create_group(attr_name_kobj, &str_attr_group); > + > + if (retval) > + goto out; > + > + mutex_lock(&call_mutex); > + strncpy_attr(str_data[instance_id].attribute_name, str_obj[ATTR_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].display_name_language_code, > + str_obj[DISPL_NAME_LANG_CODE].string.pointer); > + strncpy_attr(str_data[instance_id].display_name, str_obj[DISPLAY_NAME].string.pointer); > + strncpy_attr(str_data[instance_id].default_value, str_obj[DEFAULT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].current_value, str_obj[CURRENT_VAL].string.pointer); > + strncpy_attr(str_data[instance_id].modifier, str_obj[MODIFIER].string.pointer); > + str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer; > + str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; > + mutex_unlock(&call_mutex); > + > +out: > + return retval; > +} > + > +/** > + * exit_str_attributes() - Clear all attribute data > + * @kset: The kset to free > + * > + * Clears all data allocated for this group of attributes > + **/ > +void exit_str_attributes(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + sysfs_remove_group(pos, &str_attr_group); > + } > + mutex_unlock(&list_mutex); > + mutex_lock(&call_mutex); > + kfree(str_data); > + mutex_unlock(&call_mutex); > +} > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.c b/drivers/platform/x86/dell-wmi-sysman-attributes.c > new file mode 100644 > index 000000000000..485545ab6c8b > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.c > @@ -0,0 +1,575 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Common methods for use with dell-wmi-sysman > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/dmi.h> > +#include <linux/wmi.h> > +#include "dell-wmi-sysman-attributes.h" > + > +static DEFINE_MUTEX(call_mutex); > +static DEFINE_MUTEX(list_mutex); > + > +#define MAX_TYPES 4 > + > +static struct platform_device *platform_device; > + > +static struct platform_driver platform_driver = { > + .driver = { > + .name = DRIVER_NAME, > + }, > +}; > + > +/* attribute directory under platform dev */ > +struct kset *main_dir_kset; > +/* subtypes of attributes */ > +struct kset *type_dir_kset[MAX_TYPES]; > + > +/* reset bios to defaults */ > +static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; > +static int reset_option = -1; > + > +/** > + * populate_security_buffer() - builds a security buffer for authentication scheme > + * @buffer: the buffer to populate > + * @authentication: the authentication content > + * > + * Currently only supported type is PLAIN TEXT > + **/ > +void populate_security_buffer(char *buffer, char *authentication) > +{ > + u32 *sectype = (u32 *) buffer; > + u32 *seclen = sectype + 1; > + char *auth = buffer + sizeof(u32)*2; > + /* plain text */ > + *sectype = 1; > + *seclen = strlen(authentication); > + strncpy(auth, authentication, *seclen); > +} > + > +/** > + * map_wmi_error() - map errors from WMI methods to kernel error codes > + **/ > +int map_wmi_error(int error_code) > +{ > + switch (error_code) { > + case 0: > + /* success */ > + return 0; > + case 1: > + /* failed */ > + return -EIO; > + case 2: > + /* invalid parameter */ > + return -EINVAL; > + case 3: > + /* access denied */ > + return -EACCES; > + case 4: > + /* not supported */ > + return -EOPNOTSUPP; > + case 5: > + /* memory error */ > + return -ENOMEM; > + case 6: > + /* protocol error */ > + return -EPROTO; > + } > + /* unspecified error */ > + return -EIO; > +} > + > +/** > + * reset_bios_show() - sysfs implementaton for read reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + **/ > +static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) > +{ > + int i; > + char *start = buf; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (i == reset_option) > + buf += sprintf(buf, "[%s] ", reset_types[i]); > + else > + buf += sprintf(buf, "%s ", reset_types[i]); > + } > + buf += sprintf(buf, "\n"); > + return buf-start; > +} > + > +/** > + * reset_bios_store() - sysfs implementaton for write reset_bios > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer from userspace > + * @count: the size of the buffer from userspace > + **/ > +static ssize_t reset_bios_store(struct kobject *kobj, > + struct kobj_attribute *attr, const char *buf, size_t count) > +{ > + int len, ret, i; > + int type = -1; > + char *p; > + > + p = memchr(buf, '\n', count); > + if (p != NULL) > + *p = '\0'; > + len = p ? p - buf : count; > + > + for (i = 0; i < MAX_TYPES; i++) { > + if (len == strlen(reset_types[i]) > + && !strncmp(buf, reset_types[i], len)) { > + type = i; > + break; > + } > + } > + > + if (type < 0 || type >= MAX_TYPES) { > + ret = -EINVAL; > + goto out; > + } > + > + ret = set_bios_defaults(type); > + dev_dbg(&platform_device->dev, "reset all attributes request type %d: %d", type, ret); > + if (ret) { > + ret = -EINVAL; > + } else { > + reset_option = type; > + ret = count; > + } > + > +out: > + return ret; > +} > + > +/** > + * pending_reboot_show() - sysfs implementaton for read pending_reboot > + * @kobj: Kernel object for this attribute > + * @attr: Kernel object attribute > + * @buf: The buffer to display to userspace > + * > + * Stores default value as 0 > + * When current_value is changed this attribute is set to 1 to notify reboot may be required > + **/ > +static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%d\n", get_pending_changes()); > +} > + > +static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); > +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); > + > + > +/** > + * create_reset_bios() - Creates reset_bios and pending_reboot attributes > + **/ > +static int create_reset_bios(void) > +{ > + int ret = sysfs_create_file(&main_dir_kset->kobj, &reset_bios.attr); > + > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset_bios file"); > + return ret; > + } > + > + ret = sysfs_create_file(&main_dir_kset->kobj, &pending_reboot.attr); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create changing_pending_reboot file"); > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + } > + return ret; > +} > + > +static void release_reset_bios_data(void) > +{ > + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); > + sysfs_remove_file(&main_dir_kset->kobj, &pending_reboot.attr); > +} > + > +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, > + char *buf) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->show) > + ret = kattr->show(kobj, kattr, buf); > + return ret; > +} > + > +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, > + const char *buf, size_t count) > +{ > + struct kobj_attribute *kattr; > + ssize_t ret = -EIO; > + > + kattr = container_of(attr, struct kobj_attribute, attr); > + if (kattr->store) > + ret = kattr->store(kobj, kattr, buf, count); > + return ret; > +} > + > +const struct sysfs_ops kobj_sysfs_ops = { > + .show = kobj_attr_show, > + .store = kobj_attr_store, > +}; > + > +static void attr_name_release(struct kobject *kobj) > +{ > + kfree(kobj); > +} > + > +static struct kobj_type attr_name_ktype = { > + .release = attr_name_release, > + .sysfs_ops = &kobj_sysfs_ops, > +}; > + > +/** > + * strncpy_attr - Copy a length-limited, NULL-terminated string with bound checks > + * @dest: Where to copy the string to > + * @src: Where to copy the string from > + **/ > +void strncpy_attr(char *dest, char *src) > +{ > + size_t len = strlen(src) + 1; > + > + if (len > 1 && len < MAX_BUFF) > + strncpy(dest, src, len); > +} > + > +/** > + * get_wmiobj_pointer() - Get Content of WMI block for particular instance > + * @instance_id: WMI instance ID > + * @guid_string: WMI GUID (in str form) > + * > + * Fetches the content for WMI block (instance_id) under GUID (guid_string) > + * Caller must kfree the return > + **/ > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) > +{ > + acpi_status status; > + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; > + > + status = wmi_query_block(guid_string, instance_id, &out); > + > + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; > +} > + > +/** > + * get_instance_count() - Compute total number of instances under guid_string > + * @guid_string: WMI GUID (in string form) > + **/ > +int get_instance_count(const char *guid_string) > +{ > + int i = 0; > + union acpi_object *wmi_obj = NULL; > + > + do { > + kfree(wmi_obj); > + wmi_obj = get_wmiobj_pointer(i, guid_string); > + i++; > + } while (wmi_obj); > + > + return (i-1); > +} > + > +/** > + * alloc_attributes_data() - Allocate attributes data for a particular type > + * @attr_type: Attribute type to allocate > + **/ > +static int alloc_attributes_data(int attr_type) > +{ > + int retval = 0; > + char *type[MAX_TYPES] = {"enumeration", "integer", "string", "password"}; > + > + type_dir_kset[attr_type] = kset_create_and_add(type[attr_type], NULL, &main_dir_kset->kobj); > + if (!type_dir_kset[attr_type]) > + goto err_bios_attr; > + > + switch (attr_type) { > + case ENUM: > + retval = alloc_enum_data(); > + break; > + case INT: > + retval = alloc_int_data(); > + break; > + case STR: > + retval = alloc_str_data(); > + break; > + case PO: > + retval = alloc_po_data(); > + break; > + default: > + break; > + } > + > + goto out; > + > +err_bios_attr: > + kset_unregister(main_dir_kset); > + > +out: > + return retval; > +} > + > +/** > + * destroy_attribute_objs() - Free a kset of kobjects > + * @kset: The kset to destroy > + * > + * Fress kobjects created for each attribute_name under attribute type kset > + **/ > +static void destroy_attribute_objs(struct kset *kset) > +{ > + struct kobject *pos, *next; > + > + mutex_lock(&list_mutex); > + list_for_each_entry_safe(pos, next, &kset->list, entry) { > + kobject_put(pos); > + } > + mutex_unlock(&list_mutex); > +} > + > +/** > + * release_attributes_data() - Clean-up all sysfs directories and files created > + **/ > +static void release_attributes_data(void) > +{ > + release_reset_bios_data(); > + > + exit_enum_attributes(type_dir_kset[ENUM]); > + exit_int_attributes(type_dir_kset[INT]); > + exit_str_attributes(type_dir_kset[STR]); > + exit_po_attributes(type_dir_kset[PO]); > + > + mutex_lock(&call_mutex); > + if (main_dir_kset) { > + int i; > + > + /* unregister all 4 types ENUM,INT,STR and PO */ > + for (i = ENUM; i <= PO; i++) { > + destroy_attribute_objs(type_dir_kset[i]); > + kset_unregister(type_dir_kset[i]); > + } > + kset_unregister(main_dir_kset); > + } > + mutex_unlock(&call_mutex); > +} > + > +/** > + * init_bios_attributes() - Initialize all attributes for a type > + * @attr_type: The attribute type to initialize > + * @guid: The WMI GUID associated with this type to initialize > + * > + * Initialiaze all 4 types of attributes enumeration, integer, string and password object. > + * Populates each attrbute typ's respective properties under sysfs files > + **/ > +static int init_bios_attributes(int attr_type, const char *guid) > +{ > + union acpi_object *obj = NULL; > + union acpi_object *elements; > + int retval = 0; > + int instance_id = 0; > + struct kobject *attr_name_kobj; //individual attribute names > + > + retval = alloc_attributes_data(attr_type); > + if (retval) > + return retval; > + obj = get_wmiobj_pointer(instance_id, guid); > + if (!obj) > + return -ENODEV; > + elements = obj->package.elements; > + > + while (elements) { > + /* sanity checking */ > + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { > + dev_dbg(&platform_device->dev, "empty attribute found"); > + goto nextobj; > + } > + if (kset_find_obj(type_dir_kset[attr_type], elements[ATTR_NAME].string.pointer)) { > + dev_dbg(&platform_device->dev, "duplicate attribute name found - %s", > + elements[ATTR_NAME].string.pointer); > + goto nextobj; > + } > + > + /* build attribute */ > + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); > + if (!attr_name_kobj) > + goto err_attr_init; > + > + attr_name_kobj->kset = type_dir_kset[attr_type]; > + > + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", > + elements[ATTR_NAME].string.pointer); > + if (retval) { > + kobject_put(attr_name_kobj); > + goto err_attr_init; > + } > + > + /* enumerate all of this attribute */ > + switch (attr_type) { > + case ENUM: > + retval = populate_enum_data(elements, instance_id, attr_name_kobj); > + break; > + case INT: > + retval = populate_int_data(elements, instance_id, attr_name_kobj); > + break; > + case STR: > + retval = populate_str_data(elements, instance_id, attr_name_kobj); > + break; > + case PO: > + retval = populate_po_data(elements, instance_id, attr_name_kobj); > + break; > + default: > + break; > + } > + > + if (retval) { > + dev_dbg(&platform_device->dev, "failed to populate %s", > + elements[ATTR_NAME].string.pointer); > + goto err_attr_init; > + } > + > +nextobj: > + kfree(obj); > + instance_id++; > + obj = get_wmiobj_pointer(instance_id, guid); > + elements = obj ? obj->package.elements : NULL; > + } > + > + goto out; > + > +err_attr_init: > + release_attributes_data(); > + kfree(obj); > +out: > + return retval; > +} > + > +static int __init init_dell_bios_attrib_wmi(void) > +{ > + int ret = 0; > + > + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && > + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { > + pr_err("Unable to run on non-Dell system\n"); > + return -ENODEV; > + } > + > + ret = platform_driver_register(&platform_driver); > + if (ret) > + goto fail_platform_driver; > + > + platform_device = platform_device_alloc(DRIVER_NAME, -1); > + if (!platform_device) { > + ret = -ENOMEM; > + goto fail_platform_device_alloc; > + } > + > + ret = platform_device_add(platform_device); > + if (ret) > + goto fail_platform_device_add; > + > + main_dir_kset = kset_create_and_add("attributes", NULL, &platform_device->dev.kobj); > + if (!main_dir_kset) { > + ret = -ENOMEM; > + goto fail_platform_device_add; > + } > + > + ret = init_dell_wmi_bios_attr_set_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize set interface"); > + goto fail_kset; > + } > + > + ret = init_dell_wmi_bios_attr_pass_interface(); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to initialize pass interface"); > + goto fail_set_interface; > + } > + > + ret = create_reset_bios(); > + if (ret) { > + dev_dbg(&platform_device->dev, "could not create reset BIOS attribute"); > + goto fail_pass_interface; > + } > + > + ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate enumeration type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate integer type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate string type attributes"); > + goto fail_create_group; > + } > + > + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); > + if (ret) { > + dev_dbg(&platform_device->dev, "failed to populate pass object type attributes"); > + goto fail_create_group; > + } > + > + return 0; > + > +fail_create_group: > + release_attributes_data(); > + platform_device_del(platform_device); > + > +fail_pass_interface: > + exit_dell_wmi_bios_attr_pass_interface(); > + > +fail_set_interface: > + exit_dell_wmi_bios_attr_set_interface(); > + > +fail_kset: > + kset_unregister(main_dir_kset); > + > +fail_platform_device_add: > + platform_device_put(platform_device); > + > +fail_platform_device_alloc: > + platform_driver_unregister(&platform_driver); > + > +fail_platform_driver: > + return ret; > +} > + > +static void __exit exit_dell_bios_attrib_wmi(void) > +{ > + release_attributes_data(); > + mutex_lock(&call_mutex); > + exit_dell_wmi_bios_attr_set_interface(); > + exit_dell_wmi_bios_attr_pass_interface(); > + if (platform_device) { > + platform_device_unregister(platform_device); > + platform_driver_unregister(&platform_driver); > + } > + mutex_unlock(&call_mutex); > +} > + > +module_init(init_dell_bios_attrib_wmi); > +module_exit(exit_dell_bios_attrib_wmi); > + > +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); > +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); > +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); > +MODULE_DESCRIPTION("Dell platform setting control interface"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.h b/drivers/platform/x86/dell-wmi-sysman-attributes.h > new file mode 100644 > index 000000000000..bb5053b91ec7 > --- /dev/null > +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.h > @@ -0,0 +1,125 @@ > +/* SPDX-License-Identifier: GPL-2.0 > + * Definitions for kernel modules using Dell WMI System Management Driver > + * > + * Copyright (c) 2020 Dell Inc. > + */ > + > +#ifndef _DELL_WMI_BIOS_ATTR_H_ > +#define _DELL_WMI_BIOS_ATTR_H_ > + > +#include <linux/wmi.h> > +#include <linux/device.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/capability.h> > + > +#define DRIVER_NAME "dell-wmi-sysman" > +#define MAX_BUFF 512 > + > +#define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF5" > +#define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BFA" > +#define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF9" > +#define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719-97D7-6AD24108BFD4" > +#define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF4" > +#define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214-A1C6-1F884B1A892A" > + > +enum { ENUM, INT, STR, PO }; > + > +enum { > + ATTR_NAME, > + DISPL_NAME_LANG_CODE, > + DISPLAY_NAME, > + DEFAULT_VAL, > + CURRENT_VAL, > + MODIFIER > +}; > + > +#define get_instance_id(type) \ > +int get_##type##_instance_id(struct kobject *kobj) \ > +{ \ > + int i; \ > + for (i = 0; i <= type##_instances_count; i++) { \ > + if (!(strcmp(kobj->name, type##_data[i].attribute_name))) \ > + return i; \ > + } \ > + return -EIO; \ > +} > + > +#define attribute_s_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%s\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_n_property_show(name, type) \ > +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ > + char *buf) \ > +{ \ > + int i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + return sprintf(buf, "%d\n", type##_data[i].name); \ > + return 0; \ > +} > + > +#define attribute_property_store(curr_val, type) \ > +static ssize_t curr_val##_store(struct kobject *kobj, \ > + struct kobj_attribute *attr, \ > + const char *buf, size_t count) \ > +{ \ > + char *p = memchr(buf, '\n', count); \ > + int ret = -EIO; \ > + int i; \ > + \ > + if (p != NULL) \ > + *p = '\0'; \ > + i = get_##type##_instance_id(kobj); \ > + if (i >= 0) \ > + ret = validate_##type##_input(i, buf); \ > + if (!ret) \ > + ret = set_attribute(kobj->name, buf); \ > + return ret ? ret : count; \ > +} > + > +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); > +int get_instance_count(const char *guid_string); > +void strncpy_attr(char *dest, char *src); > + > +int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_enum_data(void); > +void exit_enum_attributes(struct kset *kset); > + > +int populate_int_data(union acpi_object *integer_obj, int instance_id, > + struct kobject *attr_name_kobj); > +int alloc_int_data(void); > +void exit_int_attributes(struct kset *kset); > + > +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_str_data(void); > +void exit_str_attributes(struct kset *kset); > + > +int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj); > +int alloc_po_data(void); > +void exit_po_attributes(struct kset *kset); > + > +int set_attribute(const char *a_name, const char *a_value); > +int set_bios_defaults(u8 defType); > + > +void exit_dell_wmi_bios_attr_set_interface(void); > +int init_dell_wmi_bios_attr_set_interface(void); > +bool get_pending_changes(void); > +int map_wmi_error(int error_code); > +void populate_security_buffer(char *buffer, char *authentication); > + > +char *get_current_password(const char *password_type); > +int set_current_password(const char *password_type, const char *cur); > +int set_new_password(const char *password_type, const char *new); > +int init_dell_wmi_bios_attr_pass_interface(void); > +void exit_dell_wmi_bios_attr_pass_interface(void); > + > +#endif >
> Going forward I will be helping Andy and Darren with maintaining the > drivers/platform/x86/* drivers. > > So one of the first things which I'm doing with that hat on, > is review this patch. Congrats on the new responsibilities :) <snip> > > So first of all some comments on the userspace (sysfs) API for that. Getting > this > part right is the most important part of this patch, as that will be set in > stone > once merged. > > My first reaction to the suggest API is that I find the sorting by type thing > really weird, > so if I were to do: > > ls /sys/devices/platform/dell-wmi-sysman/attributes/ > > I would get the following as output: > > enumeration > integer > string > > And then to see the actual attributes I would need to do: > > ls /sys/devices/platform/dell-wmi-sysman/attributes/{enumeration,integer,string} > > This feels less then ideal both when interacting from a shell, but also when > e.g. envisioning C-code enumerating attributes. > > IMHO it would be better to have: > > /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/type > > Which can be one of "enumeration,integer,string" > > and then have the other sysfs files (default_Value, current_value, max..., etc.) > as: > > /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/default_value > etc. > > Where which files exactly are present for a specific <attr> depends on the type. > > This will make e.g C-code enumerating all attributes be a single readdir, > followed > by reading the type for each dir entry; and if we add a new type the C-code can > warn the user that it encountered an atribute with unknown type <new-type>, > rather then not being aware that there is a fourth dir (for the new type) with > attributes to check. I agree this is the most important part to get correct. This proposal seems pretty good to me. > > Other then that the sysfs interface generally looks good to me, except for > one other big thing (and one small thing, see below). > > This interface seems pretty generic (which is a good thing), but then having > it live in the hardcoded /sys/devices/platform/dell-wmi-sysman/attributes > name-space seems less then ideal. I also see in the code that you are creating > a dummy platform device, just to have a place/parent to register the attributes > dir with. > > Combining these 2 things I think that it would be better to make a new class > for this, like how we e.g. have a backlight class under /sys/class/backlight > we could have a /sys/class/firmware_attributes class and then we would get > a dell_wmi entry under that (and drop the "attributes" dir), so we would get: > > /sys/class/firmware_attributes/dell_wmi/<attr>/type > > Etc. > > So instead of creating a dummy platform device, you would create a > firmware_attributes > class device. > > I think it is likely that other vendors may eventually also support modifying > BIOS settings without going into the BIOS setup menu and I would like us to > use one unified userspace API for this. Note this changes little for the Dell > code / > this patch (although eventually some code may be moved into shared helpers), but > it does allow userspace to discover if the firmware-attr sysfs API is supported > in > a vendor agnostic API by doing a readdir on /sys/class/firmware_attributes > This area I'm not sure I'm aligned. Two reasons come to mind: 1) The interface that Dell offers isn't guaranteed to work the same as any other Vendors. Do we want to force them to use the same interface as Dell? For example what if another vendor doesn't offer an interface from their firmware to enumerate possible options for any attribute but you have to know them in advance? Would those possible options be hardcoded in their kernel driver? Dell sets precedent here by having the first driver. 2) Dell has some extensions planned for other authentication mechanisms than password. That is *definitely* going to be Dell specific, so should it be done in this vendor agnostic directory? > There could even be multiple instances implementing this interface, e.g. if > their > is an add-on card with its own option-ROM, see for iscsi booting then the iscsi > boot > options could be available under: > > /sys/class/firmware_attributes/iscsi_boot_nic/<attr>/* > > While the main system firmware settings would be available under: > > /sys/class/firmware_attributes/dell_wmi/<attr>/* > > Since you have already designed a nice generic API for this it seems > sensible to me to make it possible to use this outside the Dell WMI case. > > > So as mentioned I also have one smaller issue with the API, how is a > UI supposed to represent all these attributes? In the BIOS setup screen > they are typically grouped together under e.g. CPU settings, power-management > settings, > etc. I wonder if it would be possible to add a "group" sysfs file to each > attribute > which represent the typical grouping. E.g. for pm related settings the group > file > would contain "Power Management" then an userspace Ui can enumerate the groups > and > have e.g. 1 tab per group, or a tree with the groups as parents oof the > attributes > for each group. This is just an idea I don't know if such grouping info is > available > in the WMI interface for this. This information isn't available in the ACPI-WMI interface unfortunately. <snip> > > At first I was thinking that things like is_password_set would live directly > under > /sys/devices/platform/dell-wmi-sysman/attributes/password/ so we would have: > > /sys/devices/platform/dell-wmi-sysman/attributes/password/is_password_set > > But now I see that password really is just another type of attribute and we will > have: > > /sys/devices/platform/dell-wmi-sysman/attributes/password/System/is_password_set > > and: > > /sys/devices/platform/dell-wmi-sysman/attributes/password/User/is_password_set > > That makes more sense, and will also work well with the changes I suggest above. > Correct. > > I would like to split the overall discussion of the API, versus doing a > detailed review of the code, so I will review the code in a separate email. Thanks, I think this is a productive way to review it. <snip>
> > "A read-only attribute enumerating if a reboot is pending on any BIOS attribute > change." > does not really seem to make much sense. I guess what this is trying to say is: > > "This read-only attribute reads 1 if a reboot is necessary to apply pending BIOS > attribute changes"? > > 0: All BIOS attributes setting are current > 1: A reboot is necessary to get pending pending BIOS attribute > changes applied > > Or some such. I'm not really happy with my own text either, but I think it > better explains > what this attribute is about then the original text, right ? I think that text does read better, Divya and team will reword it. <snip> > > + display_name_language_code: A file that can be read to obtain > > + the language code corresponding to the "display_name" of the <attr> > > This needs to be specified better, e.g. this needs to say that this is an > ISO 639‑1 language code (or some other language-code specification) Ack. > > > > + > > + modifier: A file that can be read to obtain attribute-level > > + dependency rule which has to be met to configure <attr> > > What is the difference between modifier and value_modifier ? Also this need to > be specified in more detail. Ack. > > > + > > + possible_value: A file that can be read to obtain the possible > > + value of the <attr> > > This is an enum, so possible value_s_ ? I assume that for a enum this will list > all possible values, this also needs to specify how the possible values will be > separated (e.g. using semi-colons or newlines or ...). Yes correct. > > > > + > > + value_modifier: A file that can be read to obtain value-level > > + dependency on a possible value which has to be met to configure > <attr> > > + > > +What: /sys/devices/platform/dell-wmi- > sysman/attributes/integer/<attr>/ > > +Date: October 2020 > > +KernelVersion: 5.9 > > +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, > > + Mario Limonciello <mario.limonciello@dell.com>, > > + Prasanth KSR <prasanth.ksr@dell.com> > > +Description: > > + This directory exposes interfaces for interaction with > > + BIOS integer attributes. > > + > > + Integer attributes are settings that accept a range of > > + numerical values for inputs. Each BIOS integer has a > > + lower bound and an upper bound on the values that it can take. > > + > > + current_value: A file that can be read to obtain the current > > + value of the <attr> > > + > > + This file can also be written to in order to update > > + the value of an <attr>. > > + > > + default_value: A file that can be read to obtain the default > > + value of the <attr> > > + > > + display_name: A file that can be read to obtain a user friendly > > + description of the at <attr> > > + > > + display_name_language_code: A file that can be read to obtain > > + the language code corresponding to the "display_name" of the <attr> > > + > > + lower_bound: A file that can be read to obtain the lower > > + bound value of the <attr> > > + > > + modifier: A file that can be read to obtain attribute-level > > + dependency rule which has to be met to configure <attr> > > + > > + scalar_increment: A file that can be read to obtain the > > + resolution of the incremental value this attribute accepts. > > + > > + upper_bound: A file that can be read to obtain the upper > > + bound value of the <attr> > > Are these integers or also possibly floats? I guess possibly also floats, right? > Then at a minimum this should specify which decimal-separator is used (I assume > we will go with the usual '.' as decimal separator). In practice they're integers, but I don't see why they couldn't be floats.
On Tue, Sep 1, 2020 at 5:58 PM Hans de Goede <hdegoede@redhat.com> wrote: > On 7/30/20 4:31 PM, Divya Bharathi wrote: ... > > +bool get_pending_changes(void) > > +{ > > + struct wmi_interface_priv *priv; > > + > > + priv = get_first_interface_priv(); > > + if (priv) > > + return priv->pending_changes; > > + return 0; 0 is not boolean. > > +} ... > > +int set_attribute(const char *a_name, const char *a_value) > > +{ > > + int ret = -1; > > + int i; > > + u8 *name_len, *value_len; > > + char *current_password, *attribute_name, *attribute_value; > > + size_t security_area_size; > > + size_t string_area_size; > > + size_t buffer_size; > > + struct wmi_interface_priv *priv; > > + char *buffer; Consider to use reversed xmas tree order. And what -1 means? > > + /* look up if user set a password for the requests */ > > + current_password = get_current_password("Admin"); > > + if (!current_password) > > + return -ENODEV; > > Can we instead of passing "Admin" and "System" to this function > just have 2 separate get_current_admin_password and get_current_system_password > helpers and then drop the error handling ? > > > + > > + /* password is set */ > > + if (strlen(current_password) > 0) > > + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + > > + strlen(current_password) % 2; > > + /* password not set */ > > + else > > + security_area_size = sizeof(u32) * 2; > > Since you are using more then 1 line here please use {} around the state-ments, > also please put the /* password not set */ after the else: > > ... > } else { /* password not set */ > ... > > > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > > + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; > > + > > + buffer = kzalloc(buffer_size, GFP_KERNEL); Actually above looks like home grown kasprintf() implementation. > > + if (!buffer) > > + return -ENOMEM; > > + > > + /* build security area */ > > + if (strlen(current_password) > 0) > > + populate_security_buffer(buffer, current_password); > > + name_len = buffer + security_area_size; > > + attribute_name = name_len + sizeof(u16); > > + value_len = attribute_name + strlen(a_name)*2; > > + attribute_value = value_len + sizeof(u16); > > + > > + /* turn into UTF16 strings, no NULL terminator */ > > + *name_len = strlen(a_name)*2; > > + *value_len = strlen(a_value)*2; > > + for (i = 0; i < strlen(a_name); i++) > > + attribute_name[i*2] = a_name[i]; > > + for (i = 0; i < strlen(a_value); i++) > > + attribute_value[i*2] = a_value[i]; > > This assumes the incoming string is ASCII only and won't > work when the incoming string is UTF-8. It is probably > better to use the utf8s_to_utf16s() helper from the nls > code, this will mean adding a dependency on CONFIG_NLS > which typically is used for filesystem code, but I think > that that will be fine. +1. Also my thought. > > + mutex_lock(&call_mutex); > > + priv = get_first_interface_priv(); > > + if (!priv) { > > + ret = -ENODEV; > > + pr_err(DRIVER_NAME ": no WMI backend bound"); If you wish, define pr_fmt() rather than putting this DRIVER_NAME to each of the pr_*() call. > > + goto out_set_attribute; > > + } > > + > > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > > + SETATTRIBUTE_METHOD_ID); > > + if (ret == -EOPNOTSUPP) > > + dev_err(&priv->wdev->dev, "admin password must be configured"); > > + else if (ret == -EACCES) > > + dev_err(&priv->wdev->dev, "invalid password"); > > + > > + priv->pending_changes = 1; > > +out_set_attribute: > > + kfree(buffer); > > + mutex_unlock(&call_mutex); > > + > > + return ret; > > +} Above comments, as a rule of thumb, should be considered against entire code (where appropriate and applicable).
Andy, Thanks for your feedback. > > > > +bool get_pending_changes(void) > > > +{ > > > + struct wmi_interface_priv *priv; > > > + > > > + priv = get_first_interface_priv(); > > > + if (priv) > > > + return priv->pending_changes; > > > > + return 0; > > 0 is not boolean. Ack > > > > +} > > ... > > > > +int set_attribute(const char *a_name, const char *a_value) > > > +{ > > > > + int ret = -1; > > > + int i; > > > + u8 *name_len, *value_len; > > > + char *current_password, *attribute_name, *attribute_value; > > > + size_t security_area_size; > > > + size_t string_area_size; > > > + size_t buffer_size; > > > + struct wmi_interface_priv *priv; > > > + char *buffer; > > Consider to use reversed xmas tree order. And what -1 means? Ack > > > > + /* look up if user set a password for the requests */ > > > + current_password = get_current_password("Admin"); > > > + if (!current_password) > > > + return -ENODEV; > > > > Can we instead of passing "Admin" and "System" to this function > > just have 2 separate get_current_admin_password and > get_current_system_password > > helpers and then drop the error handling ? The error handling for -ENODEV is actually important in case a WMI driver was unbound. > > > > > + > > > + /* password is set */ > > > + if (strlen(current_password) > 0) > > > + security_area_size = (sizeof(u32) * 2) + > strlen(current_password) + > > > + strlen(current_password) % 2; > > > + /* password not set */ > > > + else > > > + security_area_size = sizeof(u32) * 2; > > > > Since you are using more then 1 line here please use {} around the state- > ments, > > also please put the /* password not set */ after the else: > > > > ... > > } else { /* password not set */ > > ... > > > > > + string_area_size = (strlen(a_name) + strlen(a_value))*2; > > > + buffer_size = security_area_size + string_area_size + sizeof(u16) * > 2; > > > + > > > + buffer = kzalloc(buffer_size, GFP_KERNEL); > > Actually above looks like home grown kasprintf() implementation. I don't think so, sprintf isn't used at all here. It's a calculation to determine the size of the buffer to use. > > > > + if (!buffer) > > > + return -ENOMEM; > > > + > > > + /* build security area */ > > > + if (strlen(current_password) > 0) > > > + populate_security_buffer(buffer, current_password); > > > > + name_len = buffer + security_area_size; > > > + attribute_name = name_len + sizeof(u16); > > > + value_len = attribute_name + strlen(a_name)*2; > > > + attribute_value = value_len + sizeof(u16); > > > + > > > + /* turn into UTF16 strings, no NULL terminator */ > > > + *name_len = strlen(a_name)*2; > > > + *value_len = strlen(a_value)*2; > > > + for (i = 0; i < strlen(a_name); i++) > > > + attribute_name[i*2] = a_name[i]; > > > + for (i = 0; i < strlen(a_value); i++) > > > + attribute_value[i*2] = a_value[i]; > > > > This assumes the incoming string is ASCII only and won't > > work when the incoming string is UTF-8. It is probably > > better to use the utf8s_to_utf16s() helper from the nls > > code, this will mean adding a dependency on CONFIG_NLS > > which typically is used for filesystem code, but I think > > that that will be fine. > > +1. Also my thought. Ack > > > > + mutex_lock(&call_mutex); > > > + priv = get_first_interface_priv(); > > > + if (!priv) { > > > + ret = -ENODEV; > > > > + pr_err(DRIVER_NAME ": no WMI backend bound"); > > If you wish, define pr_fmt() rather than putting this DRIVER_NAME to > each of the pr_*() call. Ack, we'll add pr_fmt. > > > > + goto out_set_attribute; > > > + } > > > + > > > + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, > > > + SETATTRIBUTE_METHOD_ID); > > > + if (ret == -EOPNOTSUPP) > > > + dev_err(&priv->wdev->dev, "admin password must be > configured"); > > > + else if (ret == -EACCES) > > > + dev_err(&priv->wdev->dev, "invalid password"); > > > + > > > + priv->pending_changes = 1; > > > +out_set_attribute: > > > + kfree(buffer); > > > + mutex_unlock(&call_mutex); > > > + > > > + return ret; > > > +} > > Above comments, as a rule of thumb, should be considered against > entire code (where appropriate and applicable). Thanks, the team will adjust against all the code for the next patch.
Hi, Sorry for being slow with getting back to you on this. On 9/1/20 4:22 PM, Limonciello, Mario wrote: >> >> "A read-only attribute enumerating if a reboot is pending on any BIOS attribute >> change." >> does not really seem to make much sense. I guess what this is trying to say is: >> >> "This read-only attribute reads 1 if a reboot is necessary to apply pending BIOS >> attribute changes"? >> >> 0: All BIOS attributes setting are current >> 1: A reboot is necessary to get pending pending BIOS attribute >> changes applied >> >> Or some such. I'm not really happy with my own text either, but I think it >> better explains >> what this attribute is about then the original text, right ? > > I think that text does read better, Divya and team will reword it. > > <snip> > >>> + display_name_language_code: A file that can be read to obtain >>> + the language code corresponding to the "display_name" of the <attr> >> >> This needs to be specified better, e.g. this needs to say that this is an >> ISO 639‑1 language code (or some other language-code specification) > > Ack. > >> >> >>> + >>> + modifier: A file that can be read to obtain attribute-level >>> + dependency rule which has to be met to configure <attr> >> >> What is the difference between modifier and value_modifier ? Also this need to >> be specified in more detail. > > Ack. > >> >>> + >>> + possible_value: A file that can be read to obtain the possible >>> + value of the <attr> >> >> This is an enum, so possible value_s_ ? I assume that for a enum this will list >> all possible values, this also needs to specify how the possible values will be >> separated (e.g. using semi-colons or newlines or ...). > > Yes correct. > >> >> >>> + >>> + value_modifier: A file that can be read to obtain value-level >>> + dependency on a possible value which has to be met to configure >> <attr> >>> + >>> +What: /sys/devices/platform/dell-wmi- >> sysman/attributes/integer/<attr>/ >>> +Date: October 2020 >>> +KernelVersion: 5.9 >>> +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, >>> + Mario Limonciello <mario.limonciello@dell.com>, >>> + Prasanth KSR <prasanth.ksr@dell.com> >>> +Description: >>> + This directory exposes interfaces for interaction with >>> + BIOS integer attributes. >>> + >>> + Integer attributes are settings that accept a range of >>> + numerical values for inputs. Each BIOS integer has a >>> + lower bound and an upper bound on the values that it can take. >>> + >>> + current_value: A file that can be read to obtain the current >>> + value of the <attr> >>> + >>> + This file can also be written to in order to update >>> + the value of an <attr>. >>> + >>> + default_value: A file that can be read to obtain the default >>> + value of the <attr> >>> + >>> + display_name: A file that can be read to obtain a user friendly >>> + description of the at <attr> >>> + >>> + display_name_language_code: A file that can be read to obtain >>> + the language code corresponding to the "display_name" of the <attr> >>> + >>> + lower_bound: A file that can be read to obtain the lower >>> + bound value of the <attr> >>> + >>> + modifier: A file that can be read to obtain attribute-level >>> + dependency rule which has to be met to configure <attr> >>> + >>> + scalar_increment: A file that can be read to obtain the >>> + resolution of the incremental value this attribute accepts. >>> + >>> + upper_bound: A file that can be read to obtain the upper >>> + bound value of the <attr> >> >> Are these integers or also possibly floats? I guess possibly also floats, right? >> Then at a minimum this should specify which decimal-separator is used (I assume >> we will go with the usual '.' as decimal separator). > > In practice they're integers, but I don't see why they couldn't be floats. Hmm, that is a bit hand-wavy, for an userspace ABI we really need to define this clearly. Either it is integers (which is fine), or it is floats and we need to define a decimal-separator as part of the ABI. Note the reason why I started wondering about this in the first place is the scalar_increment attribute. I think that can use some clarification too. Regards, Hans
Hi, On 9/14/20 10:45 AM, Hans de Goede wrote: <snip> >>>> + lower_bound: A file that can be read to obtain the lower >>>> + bound value of the <attr> >>>> + >>>> + modifier: A file that can be read to obtain attribute-level >>>> + dependency rule which has to be met to configure <attr> >>>> + >>>> + scalar_increment: A file that can be read to obtain the >>>> + resolution of the incremental value this attribute accepts. >>>> + >>>> + upper_bound: A file that can be read to obtain the upper >>>> + bound value of the <attr> >>> >>> Are these integers or also possibly floats? I guess possibly also floats, right? >>> Then at a minimum this should specify which decimal-separator is used (I assume >>> we will go with the usual '.' as decimal separator). >> >> In practice they're integers, but I don't see why they couldn't be floats. > > Hmm, that is a bit hand-wavy, for an userspace ABI we really need to define > this clearly. Either it is integers (which is fine), or it is floats and we need > to define a decimal-separator as part of the ABI. > > Note the reason why I started wondering about this in the first place is the > scalar_increment attribute. I think that can use some clarification too. p.s. I just realized that the lower_ / upper_bound attributes would be better if they were renamed to min_value and max_value then everyone will immediately understand what they are without even needing to consult the docs. Regards, Hans
Hi, On 9/1/20 4:17 PM, Limonciello, Mario wrote: <snip> >> So first of all some comments on the userspace (sysfs) API for that. Getting >> this >> part right is the most important part of this patch, as that will be set in >> stone >> once merged. >> >> My first reaction to the suggest API is that I find the sorting by type thing >> really weird, >> so if I were to do: >> >> ls /sys/devices/platform/dell-wmi-sysman/attributes/ >> >> I would get the following as output: >> >> enumeration >> integer >> string >> >> And then to see the actual attributes I would need to do: >> >> ls /sys/devices/platform/dell-wmi-sysman/attributes/{enumeration,integer,string} >> >> This feels less then ideal both when interacting from a shell, but also when >> e.g. envisioning C-code enumerating attributes. >> >> IMHO it would be better to have: >> >> /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/type >> >> Which can be one of "enumeration,integer,string" >> >> and then have the other sysfs files (default_Value, current_value, max..., etc.) >> as: >> >> /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/default_value >> etc. >> >> Where which files exactly are present for a specific <attr> depends on the type. >> >> This will make e.g C-code enumerating all attributes be a single readdir, >> followed >> by reading the type for each dir entry; and if we add a new type the C-code can >> warn the user that it encountered an atribute with unknown type <new-type>, >> rather then not being aware that there is a fourth dir (for the new type) with >> attributes to check. > > I agree this is the most important part to get correct. This proposal seems pretty > good to me. > >> >> Other then that the sysfs interface generally looks good to me, except for >> one other big thing (and one small thing, see below). >> >> This interface seems pretty generic (which is a good thing), but then having >> it live in the hardcoded /sys/devices/platform/dell-wmi-sysman/attributes >> name-space seems less then ideal. I also see in the code that you are creating >> a dummy platform device, just to have a place/parent to register the attributes >> dir with. >> >> Combining these 2 things I think that it would be better to make a new class >> for this, like how we e.g. have a backlight class under /sys/class/backlight >> we could have a /sys/class/firmware_attributes class and then we would get >> a dell_wmi entry under that (and drop the "attributes" dir), so we would get: >> >> /sys/class/firmware_attributes/dell_wmi/<attr>/type >> >> Etc. >> >> So instead of creating a dummy platform device, you would create a >> firmware_attributes >> class device. >> >> I think it is likely that other vendors may eventually also support modifying >> BIOS settings without going into the BIOS setup menu and I would like us to >> use one unified userspace API for this. Note this changes little for the Dell >> code / >> this patch (although eventually some code may be moved into shared helpers), but >> it does allow userspace to discover if the firmware-attr sysfs API is supported >> in >> a vendor agnostic API by doing a readdir on /sys/class/firmware_attributes >> > > This area I'm not sure I'm aligned. Two reasons come to mind: > > 1) The interface that Dell offers isn't guaranteed to work the same as any other > Vendors. Do we want to force them to use the same interface as Dell? For example what > if another vendor doesn't offer an interface from their firmware to enumerate possible > options for any attribute but you have to know them in advance? So my thinking here is as follows: 1. AFAIK other vendors may want to do something similar in the near future 2. The interface you (Dell) have come up with looks pretty generic / complete to me > Would those possible options > be hardcoded in their kernel driver? Maybe, so the firmware implementation of an enum type, can take 2 forms: a) An integer in the range of 0-# where 0-# is like the integer value backing an enum in C b) Reading the current value as a string and when writing only a value from a fixed list of strings is valid Now in both cases, either not knowing what the numbers actually mean, or not knowing which values are valid for writing would make the interface pretty hard to use, close to useless. So yes in that case the driver may need to hardcode to values (assuming that scales for that vendor and they don't have a gazillion different enums). Also note that sysfs attributes can be marked as optional, so we could mark things like possible enum-values, min/max/scalar_inc as optal right from the start. We could for now mark everything optional except for type, current_value and display-name. That should make it easy for other vendors implementations to adhere to / implement the API. > Dell sets precedent here by having the first driver. Right and normally I may have wanted to wait until a second vendor implements a similar mechanism under Linux so that we can find common ground between the 2 implementations for the generic userspace API for this. The problem with that approach is that because we do not break userspace, we then get to carry the "temporary" vendor-specific implementation of the userspace API around for ever, since it may very well have existing users before we replace it with the generic API. This scenario would mean that after some point in time (when the generic API gets added to the kernel) Dell needs to support 2 userspace APIs the one which is being introduced by this patch + the generic one going forward. Since to me the current API/ABI Dell is proposing is pretty generic I'm trying to avoid this having 2 maintain 2 different userspace APIs problem by making this the generic API from the get go. > 2) Dell has some extensions planned for other authentication mechanisms than password. > That is *definitely* going to be Dell specific, so should it be done in this vendor > agnostic directory? Well each property: /sys/class/firmware-properties/dell-bios/<property-name> Will have a type attribute: /sys/class/firmware-properties/dell-bios/<property-name>/type You can use dell-special-auth-mechanism as type for this and then it is clear it is dell specific. As mentioned above I fully expect new types to get added over this and userspace tools will be expected to just skip properties with unknown types (possibly with a warning). We could even do something like we have for .desktop files fields, where we add something to the sysfs ABI documentation that vendors may add vendor specific types prefixed with X-<vendorname>. So all in all I believe that we can make using the proposed sysfs ABI a generic one work, and that this will be worth it to avoid having the issue of eventually having both a couple of vendor specific APIs + one grant unified generic API replacing those vendor-APIs (where we can never drop the vendor specific APIs because of backward compat. guarantees). >> There could even be multiple instances implementing this interface, e.g. if >> their >> is an add-on card with its own option-ROM, see for iscsi booting then the iscsi >> boot >> options could be available under: >> >> /sys/class/firmware_attributes/iscsi_boot_nic/<attr>/* >> >> While the main system firmware settings would be available under: >> >> /sys/class/firmware_attributes/dell_wmi/<attr>/* >> >> Since you have already designed a nice generic API for this it seems >> sensible to me to make it possible to use this outside the Dell WMI case. >> >> >> So as mentioned I also have one smaller issue with the API, how is a >> UI supposed to represent all these attributes? In the BIOS setup screen >> they are typically grouped together under e.g. CPU settings, power-management >> settings, >> etc. I wonder if it would be possible to add a "group" sysfs file to each >> attribute >> which represent the typical grouping. E.g. for pm related settings the group >> file >> would contain "Power Management" then an userspace Ui can enumerate the groups >> and >> have e.g. 1 tab per group, or a tree with the groups as parents oof the >> attributes >> for each group. This is just an idea I don't know if such grouping info is >> available >> in the WMI interface for this. > > This information isn't available in the ACPI-WMI interface unfortunately. Ok, too bad, but understandable. Regards, Hans
Hi, On 9/1/20 4:17 PM, Limonciello, Mario wrote: <snip> >> So first of all some comments on the userspace (sysfs) API for that. Getting >> this >> part right is the most important part of this patch, as that will be set in >> stone >> once merged. >> >> My first reaction to the suggest API is that I find the sorting by type thing >> really weird, >> so if I were to do: >> >> ls /sys/devices/platform/dell-wmi-sysman/attributes/ >> >> I would get the following as output: >> >> enumeration >> integer >> string >> >> And then to see the actual attributes I would need to do: >> >> ls /sys/devices/platform/dell-wmi-sysman/attributes/{enumeration,integer,string} >> >> This feels less then ideal both when interacting from a shell, but also when >> e.g. envisioning C-code enumerating attributes. >> >> IMHO it would be better to have: >> >> /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/type >> >> Which can be one of "enumeration,integer,string" >> >> and then have the other sysfs files (default_Value, current_value, max..., etc.) >> as: >> >> /sys/devices/platform/dell-wmi-sysman/attributes/<attr>/default_value >> etc. >> >> Where which files exactly are present for a specific <attr> depends on the type. >> >> This will make e.g C-code enumerating all attributes be a single readdir, >> followed >> by reading the type for each dir entry; and if we add a new type the C-code can >> warn the user that it encountered an atribute with unknown type <new-type>, >> rather then not being aware that there is a fourth dir (for the new type) with >> attributes to check. > > I agree this is the most important part to get correct. This proposal seems pretty > good to me. > >> >> Other then that the sysfs interface generally looks good to me, except for >> one other big thing (and one small thing, see below). >> >> This interface seems pretty generic (which is a good thing), but then having >> it live in the hardcoded /sys/devices/platform/dell-wmi-sysman/attributes >> name-space seems less then ideal. I also see in the code that you are creating >> a dummy platform device, just to have a place/parent to register the attributes >> dir with. >> >> Combining these 2 things I think that it would be better to make a new class >> for this, like how we e.g. have a backlight class under /sys/class/backlight >> we could have a /sys/class/firmware_attributes class and then we would get >> a dell_wmi entry under that (and drop the "attributes" dir), so we would get: >> >> /sys/class/firmware_attributes/dell_wmi/<attr>/type >> >> Etc. >> >> So instead of creating a dummy platform device, you would create a >> firmware_attributes >> class device. >> >> I think it is likely that other vendors may eventually also support modifying >> BIOS settings without going into the BIOS setup menu and I would like us to >> use one unified userspace API for this. Note this changes little for the Dell >> code / >> this patch (although eventually some code may be moved into shared helpers), but >> it does allow userspace to discover if the firmware-attr sysfs API is supported >> in >> a vendor agnostic API by doing a readdir on /sys/class/firmware_attributes >> > > This area I'm not sure I'm aligned. Two reasons come to mind: > > 1) The interface that Dell offers isn't guaranteed to work the same as any other > Vendors. Do we want to force them to use the same interface as Dell? For example what > if another vendor doesn't offer an interface from their firmware to enumerate possible > options for any attribute but you have to know them in advance? So my thinking here is as follows: 1. AFAIK other vendors may want to do something similar in the near future 2. The interface you (Dell) have come up with looks pretty generic / complete to me > Would those possible options > be hardcoded in their kernel driver? Maybe, so the firmware implementation of an enum type, can take 2 forms: a) An integer in the range of 0-# where 0-# is like the integer value backing an enum in C b) Reading the current value as a string and when writing only a value from a fixed list of strings is valid Now in both cases, either not knowing what the numbers actually mean, or not knowing which values are valid for writing would make the interface pretty hard to use, close to useless. So yes in that case the driver may need to hardcode to values (assuming that scales for that vendor and they don't have a gazillion different enums). Also note that sysfs attributes can be marked as optional, so we could mark things like possible enum-values, min/max/scalar_inc as optal right from the start. We could for now mark everything optional except for type, current_value and display-name. That should make it easy for other vendors implementations to adhere to / implement the API. > Dell sets precedent here by having the first driver. Right and normally I may have wanted to wait until a second vendor implements a similar mechanism under Linux so that we can find common ground between the 2 implementations for the generic userspace API for this. The problem with that approach is that because we do not break userspace, we then get to carry the "temporary" vendor-specific implementation of the userspace API around for ever, since it may very well have existing users before we replace it with the generic API. This scenario would mean that after some point in time (when the generic API gets added to the kernel) Dell needs to support 2 userspace APIs the one which is being introduced by this patch + the generic one going forward. Since to me the current API/ABI Dell is proposing is pretty generic I'm trying to avoid this having 2 maintain 2 different userspace APIs problem by making this the generic API from the get go. > 2) Dell has some extensions planned for other authentication mechanisms than password. > That is *definitely* going to be Dell specific, so should it be done in this vendor > agnostic directory? Well each property: /sys/class/firmware-properties/dell-bios/<property-name> Will have a type attribute: /sys/class/firmware-properties/dell-bios/<property-name>/type You can use dell-special-auth-mechanism as type for this and then it is clear it is dell specific. As mentioned above I fully expect new types to get added over this and userspace tools will be expected to just skip properties with unknown types (possibly with a warning). We could even do something like we have for .desktop files fields, where we add something to the sysfs ABI documentation that vendors may add vendor specific types prefixed with X-<vendorname>. So all in all I believe that we can make using the proposed sysfs ABI a generic one work, and that this will be worth it to avoid having the issue of eventually having both a couple of vendor specific APIs + one grant unified generic API replacing those vendor-APIs (where we can never drop the vendor specific APIs because of backward compat. guarantees). >> There could even be multiple instances implementing this interface, e.g. if >> their >> is an add-on card with its own option-ROM, see for iscsi booting then the iscsi >> boot >> options could be available under: >> >> /sys/class/firmware_attributes/iscsi_boot_nic/<attr>/* >> >> While the main system firmware settings would be available under: >> >> /sys/class/firmware_attributes/dell_wmi/<attr>/* >> >> Since you have already designed a nice generic API for this it seems >> sensible to me to make it possible to use this outside the Dell WMI case. >> >> >> So as mentioned I also have one smaller issue with the API, how is a >> UI supposed to represent all these attributes? In the BIOS setup screen >> they are typically grouped together under e.g. CPU settings, power-management >> settings, >> etc. I wonder if it would be possible to add a "group" sysfs file to each >> attribute >> which represent the typical grouping. E.g. for pm related settings the group >> file >> would contain "Power Management" then an userspace Ui can enumerate the groups >> and >> have e.g. 1 tab per group, or a tree with the groups as parents oof the >> attributes >> for each group. This is just an idea I don't know if such grouping info is >> available >> in the WMI interface for this. > > This information isn't available in the ACPI-WMI interface unfortunately. Ok, too bad, but understandable. Regards, Hans
Hi, On 9/3/20 4:27 PM, Limonciello, Mario wrote: <snip> >>>> + /* look up if user set a password for the requests */ >>>> + current_password = get_current_password("Admin"); >>>> + if (!current_password) >>>> + return -ENODEV; >>> >>> Can we instead of passing "Admin" and "System" to this function >>> just have 2 separate get_current_admin_password and >> get_current_system_password >>> helpers and then drop the error handling ? > > The error handling for -ENODEV is actually important in case a WMI driver > was unbound. I see and checking for that is good, but then please make it explicit, rather then hiding it like this. As is the code suggests to someone reading the code that the problem is a missing password not the driver being unbound. As I mentioned before, since the code clearly assume that only 1 instance of each WMI GUID is present, it should move to storing all its data into a shared global struct. Protected by a single shared global mutex. And then functions exposed through sysfs attributes can do: mutex_lock(&dell_wmi_bios_attr_mutex); if (!dell_wmi_bios_attr_data.bios_attr_wdev || !dell_wmi_bios_attr_data.password_attr_wdev) { mutex_unlock(&call_mutex); return -ENODEV; } And the password data can simply be directly read from dell_wmi_bios_attr_data without needing a helper for it at all. >>>> + >>>> + /* password is set */ >>>> + if (strlen(current_password) > 0) >>>> + security_area_size = (sizeof(u32) * 2) + >> strlen(current_password) + >>>> + strlen(current_password) % 2; >>>> + /* password not set */ >>>> + else >>>> + security_area_size = sizeof(u32) * 2; >>> >>> Since you are using more then 1 line here please use {} around the state- >> ments, >>> also please put the /* password not set */ after the else: >>> >>> ... >>> } else { /* password not set */ >>> ... >>> >>>> + string_area_size = (strlen(a_name) + strlen(a_value))*2; >>>> + buffer_size = security_area_size + string_area_size + sizeof(u16) * >> 2; >>>> + >>>> + buffer = kzalloc(buffer_size, GFP_KERNEL); >> >> Actually above looks like home grown kasprintf() implementation. > > I don't think so, sprintf isn't used at all here. It's a calculation to determine > the size of the buffer to use. Ack, this is different because it concats a UTF16 string together with some other data into the new buf. Regards, Hans
> So my thinking here is as follows: > > 1. AFAIK other vendors may want to do something similar in the near future > 2. The interface you (Dell) have come up with looks pretty generic / complete > to me > > > Would those possible options > > be hardcoded in their kernel driver? > > Maybe, so the firmware implementation of an enum type, can take 2 forms: > a) An integer in the range of 0-# where 0-# is like the integer value backing > an enum in C > b) Reading the current value as a string and when writing only a value from a > fixed list of strings is valid > > Now in both cases, either not knowing what the numbers actually mean, or not > knowing > which values are valid for writing would make the interface pretty hard to > use, > close to useless. So yes in that case the driver may need to hardcode to > values > (assuming that scales for that vendor and they don't have a gazillion > different > enums). > > Also note that sysfs attributes can be marked as optional, so we could mark > things like possible enum-values, min/max/scalar_inc as optal right from the > start. > We could for now mark everything optional except for type, current_value and > display-name. That should make it easy for other vendors implementations to > adhere to / implement the API. > > > Dell sets precedent here by having the first driver. > > Right and normally I may have wanted to wait until a second vendor implements > a similar mechanism under Linux so that we can find common ground between the > 2 implementations for the generic userspace API for this. > I think in terms of the basic sysfs files and their contents, a generic API makes fine sense, but I'm hung specifically up on the flow when the firmware interface is locked down. > The problem with that approach is that because we do not break userspace, > we then get to carry the "temporary" vendor-specific implementation of the > userspace API around for ever, since it may very well have existing users > before we replace it with the generic API. > > This scenario would mean that after some point in time (when the generic API > gets > added to the kernel) Dell needs to support 2 userspace APIs the one which is > being introduced by this patch + the generic one going forward. > > Since to me the current API/ABI Dell is proposing is pretty generic I'm > trying to avoid this having 2 maintain 2 different userspace APIs problem > by making this the generic API from the get go. I'm worried that we actually end up in a situation that the "generic" one supports these basic features. This is very similar to the Dell one, but misses certain enhancements that are not in the generic one so you have to use the Dell one to get those features. And then how do you know which one to select from the kernel config? It gets messy quickly. > > > 2) Dell has some extensions planned for other authentication mechanisms than > password. > > That is *definitely* going to be Dell specific, so should it be done in this > vendor > > agnostic directory? > > Well each property: > > /sys/class/firmware-properties/dell-bios/<property-name> > > Will have a type attribute: > > /sys/class/firmware-properties/dell-bios/<property-name>/type > > You can use dell-special-auth-mechanism as type for this and > then it is clear it is dell specific. As mentioned above I > fully expect new types to get added over this and userspace tools > will be expected to just skip properties with unknown types > (possibly with a warning). So I think the nuance that is missed here is the actual flow for interacting with an attribute when password security is enabled in today's patch set (both v1 and v2). Userspace would perform like this: 1) Check "is_password_set" attribute to determine if admin password required 2) If yes write password into the current_password attribute (location changed in 2 patches) 3) write new attribute value(s) 4) If necessary clear current_password attribute This works like a "session" today with admin password. So if you have a generic interface representing things as attributes you need to also have a generic attribute indicating authentication required. That would mean ALL attributes need to have a "authentication_required" type of attribute. And then that comes back to the point that authentication flow is definitely not generic. Dell requires you to write the password in every WMI call today, but the sysfs interface actually behaves like a session and caches the password in memory for the next call. As a completely hypothetical idea what if another vendor also supports an admin password but decides for their threat model it's actually a password hashed appended with a nonce and hashed and hence needs to be set every time from sysfs? Their flow might look something like this: 1) Check auth_required attribute 2) Write hash(password|nonce) to current_password 3) Write attribute 4) Write hash(password|nonce) to current_password 5) Not necessary to clear current_password Those are very different flows to get to and change the same "types" of data. By Dell's interface being Dell specific we can guarantee that the documented flow works how it should. > > We could even do something like we have for .desktop files > fields, where we add something to the sysfs ABI documentation > that vendors may add vendor specific types prefixed with X-<vendorname>. > > So all in all I believe that we can make using the proposed sysfs ABI > a generic one work, and that this will be worth it to avoid having the > issue of eventually having both a couple of vendor specific APIs + > one grant unified generic API replacing those vendor-APIs > (where we can never drop the vendor specific APIs because of > backward compat. guarantees). I'm personally leaning on the right place to have a vendor agnostic view is "outside" of the kernel in an userland library. All the vendor drivers that change settings can behave very similarly for the most part, but differences between vendor implementations can be better expressed there.
Hi, On 9/14/20 6:06 PM, Limonciello, Mario wrote: >> So my thinking here is as follows: >> >> 1. AFAIK other vendors may want to do something similar in the near future >> 2. The interface you (Dell) have come up with looks pretty generic / complete >> to me <snip> >>> Dell sets precedent here by having the first driver. >> >> Right and normally I may have wanted to wait until a second vendor implements >> a similar mechanism under Linux so that we can find common ground between the >> 2 implementations for the generic userspace API for this. >> > > I think in terms of the basic sysfs files and their contents, a generic API makes > fine sense, but I'm hung specifically up on the flow when the firmware interface is > locked down. Ok. >> The problem with that approach is that because we do not break userspace, >> we then get to carry the "temporary" vendor-specific implementation of the >> userspace API around for ever, since it may very well have existing users >> before we replace it with the generic API. >> >> This scenario would mean that after some point in time (when the generic API >> gets >> added to the kernel) Dell needs to support 2 userspace APIs the one which is >> being introduced by this patch + the generic one going forward. >> >> Since to me the current API/ABI Dell is proposing is pretty generic I'm >> trying to avoid this having 2 maintain 2 different userspace APIs problem >> by making this the generic API from the get go. > > I'm worried that we actually end up in a situation that the "generic" one supports > these basic features. This is very similar to the Dell one, but misses certain > enhancements that are not in the generic one so you have to use the Dell one to get > those features. And then how do you know which one to select from the kernel config? > > It gets messy quickly. If there are 2 interfaces then yes it can get messy, but having 2 interfaces is exactly what I'm trying to avoid here. >>> 2) Dell has some extensions planned for other authentication mechanisms than >> password. >>> That is *definitely* going to be Dell specific, so should it be done in this >> vendor >>> agnostic directory? >> >> Well each property: >> >> /sys/class/firmware-properties/dell-bios/<property-name> >> >> Will have a type attribute: >> >> /sys/class/firmware-properties/dell-bios/<property-name>/type >> >> You can use dell-special-auth-mechanism as type for this and >> then it is clear it is dell specific. As mentioned above I >> fully expect new types to get added over this and userspace tools >> will be expected to just skip properties with unknown types >> (possibly with a warning). > > So I think the nuance that is missed here is the actual flow for interacting with > an attribute when password security is enabled in today's patch set (both v1 and v2). > > Userspace would perform like this: > 1) Check "is_password_set" attribute to determine if admin password required > 2) If yes write password into the current_password attribute (location changed in 2 patches) > 3) write new attribute value(s) > 4) If necessary clear current_password attribute > > This works like a "session" today with admin password. So if you have a generic interface > representing things as attributes you need to also have a generic attribute indicating > authentication required. That would mean ALL attributes need to have a "authentication_required" > type of attribute. > > And then that comes back to the point that authentication flow is definitely not generic. > Dell requires you to write the password in every WMI call today, but the sysfs interface actually > behaves like a session and caches the password in memory for the next call. > > As a completely hypothetical idea what if another vendor also supports an admin password but decides for > their threat model it's actually a password hashed appended with a nonce and hashed and hence > needs to be set every time from sysfs? > > Their flow might look something like this: > 1) Check auth_required attribute > 2) Write hash(password|nonce) to current_password > 3) Write attribute > 4) Write hash(password|nonce) to current_password > 5) Not necessary to clear current_password > > Those are very different flows to get to and change the same "types" of data. By Dell's interface > being Dell specific we can guarantee that the documented flow works how it should. Documenting the flow could be part of the documentation for the different passwd types. For how things currently work the User and Admin password attributed would have a type of "password", the hash example you gave will have a different type for its password attribute, e.g. "hotp" and not only the type could be different but also the sysfs-attributes, e.g. the "Admin" password-dir which has a "type" sysfs-atrribute which returns "htop" may not have a current_password attribute at all, instead it may would have a hash attribute, making it (more) clear that this one works differently. This would mean that existing userspace software can not work with systems using the new "hotp" password atrributes, but that is unavoidable. I think that the current generic password flow will work well for other vendors too, they may need to not cache it in the kernel (instead sending it directly to the firmware once), but the basic concept of having to write the plain-text bios Admin password before being able to change protected settings seems like it is something which matches how most current BIOS-es work. And needing a way to re-lock the settings also sounds like something which will be pretty common for most implementations. >> We could even do something like we have for .desktop files >> fields, where we add something to the sysfs ABI documentation >> that vendors may add vendor specific types prefixed with X-<vendorname>. >> >> So all in all I believe that we can make using the proposed sysfs ABI >> a generic one work, and that this will be worth it to avoid having the >> issue of eventually having both a couple of vendor specific APIs + >> one grant unified generic API replacing those vendor-APIs >> (where we can never drop the vendor specific APIs because of >> backward compat. guarantees). > > I'm personally leaning on the right place to have a vendor agnostic view is "outside" > of the kernel in an userland library. All the vendor drivers that change settings can > behave very similarly for the most part, but differences between vendor implementations > can be better expressed there. We have tried that before in several cases (that I'm aware of) and this never works out. Esp. not when the basic kernel interface is reasonable sane, then a lot of people / projects avoid the lib and just poke the kernel API directly. We have seen this e.g. with hwmon-class devices and with v4l devices and with backlight-class devices. Since I've seen this happen 3 times already I'm not a big believer in adding a userspace library to hide the vendor differences. Regards, Hans
> > Those are very different flows to get to and change the same "types" of > data. By Dell's interface > > being Dell specific we can guarantee that the documented flow works how it > should. > > Documenting the flow could be part of the documentation for the > different passwd types. In the concept of a "generic" API I don't think the word "password" is futureproof and it would need to be avoided. I think a better term would be "authentication". > For how things currently work the User and > Admin password attributed would have a type of "password", the hash > example you gave will have a different type for its password attribute, > e.g. "hotp" and not only the type could be different but also > the sysfs-attributes, e.g. the "Admin" password-dir which has a "type" > sysfs-atrribute which returns "htop" may not have a current_password > attribute at all, instead it may would have a hash attribute, making > it (more) clear that this one works differently. In our definition `current_password` is intentionally not readable by userspace. One process could be writing it (think obtaining it from an escrow service) and another interacting with attributes, and their threat models might not match. Furthermore - what's to say multiple authentication schemes might not be simultaneously supported and this needs to be expressed? This can be a difference between OEM implementations. > > This would mean that existing userspace software can not work with > systems using the new "hotp" password atrributes, but that is > unavoidable. > > I think that the current generic password flow will work well > for other vendors too, they may need to not cache it in the > kernel (instead sending it directly to the firmware once), but the basic > concept of having to write the plain-text bios Admin password before > being able to change protected settings seems like it is something which > matches how most current BIOS-es work. And needing a way to re-lock the > settings also sounds like something which will be pretty common for most > implementations. > OK so here is another place that I think vendors might have a different implementation. When you have a BIOS admin password set, Dell requires that password to interact with any of these attributes. Another vendor might only require it only for certain attributes they deemed protected. So again, Dell's flow might not scale to everyone else. I do acknowledge this might be mitigatable by adding a sysfs file to every attribute for Dell's implementation "is_authentication_required" that is always 1 when admin password required and in another implementation an OEM might choose to set that on a case by case basis. > >> We could even do something like we have for .desktop files > >> fields, where we add something to the sysfs ABI documentation > >> that vendors may add vendor specific types prefixed with X-<vendorname>. > >> > >> So all in all I believe that we can make using the proposed sysfs ABI > >> a generic one work, and that this will be worth it to avoid having the > >> issue of eventually having both a couple of vendor specific APIs + > >> one grant unified generic API replacing those vendor-APIs > >> (where we can never drop the vendor specific APIs because of > >> backward compat. guarantees). > > > > I'm personally leaning on the right place to have a vendor agnostic view is > "outside" > > of the kernel in an userland library. All the vendor drivers that change > settings can > > behave very similarly for the most part, but differences between vendor > implementations > > can be better expressed there. > > We have tried that before in several cases (that I'm aware of) and this never > works out. Esp. not when the basic kernel interface is reasonable sane, > then a lot of people / projects avoid the lib and just poke the kernel API > directly. We have seen this e.g. with hwmon-class devices and with v4l devices > and with backlight-class devices. Since I've seen this happen 3 times already > I'm not a big believer in adding a userspace library to hide the vendor > differences. > > Regards, > > Hans Another area that comes to mind is Dell's value_modifier and modifier rules. This dependency logic is handled and expressed by the firmware. You'll notice the Dell driver only displays the information that came out of the firmware in sysfs and doesn't do any processing in driver. So by using Dell's format, another vendor's driver will need to follow Dell's formatting and rule validation/generation which their firmware might not support and they will be forced to implement Dell's schema in their kernel driver. Lastly I want to caution that individual firmware items with the same name might have a different meaning across vendors. Here is my hypothetical example: Dell has an attribute called "Camera" With V3 it populates under: /sys/devices/platform/dell-wmi-sysman/attributes/Camera The description sysfs for it reads as "Enable Camera" and it's possible values are "Disabled;Enabled;". For Dell this is pretty obviously it turns on and off the camera functionality. For another vendor they might actually not offer to enable/disable the camera but instead To enable the control of an electromagnetic camera shutter from such an attribute. Their attribute could still be called "Camera" but the description might read as "Enable camera shutter control". For them it would still read as "Disabled;Enabled;" for possible values but have a completely different meaning! There is no standard for this, and again userspace will need to basically look at the directory and structure to figure out what the meaning actually is.
Hi, On 9/17/20 6:18 PM, Limonciello, Mario wrote: >>> Those are very different flows to get to and change the same "types" of >> data. By Dell's interface >>> being Dell specific we can guarantee that the documented flow works how it >> should. >> >> Documenting the flow could be part of the documentation for the >> different passwd types. > > In the concept of a "generic" API I don't think the word "password" is futureproof > and it would need to be avoided. I think a better term would be "authentication". Ack. >> For how things currently work the User and >> Admin password attributed would have a type of "password", the hash >> example you gave will have a different type for its password attribute, >> e.g. "hotp" and not only the type could be different but also >> the sysfs-attributes, e.g. the "Admin" password-dir which has a "type" >> sysfs-atrribute which returns "htop" may not have a current_password >> attribute at all, instead it may would have a hash attribute, making >> it (more) clear that this one works differently. > > In our definition `current_password` is intentionally not readable by userspace. > One process could be writing it (think obtaining it from an escrow service) and > another interacting with attributes, and their threat models might not match. I completely agree that current_password should be write-only, I don't see that s a problem. > Furthermore - what's to say multiple authentication schemes might not be > simultaneously supported and this needs to be expressed? This can be a difference > between OEM implementations. Well if different schemes are supported and each scheme has its own type, then I would expect there to be say / e.g.: /sys/class/firmware-attributes/dell/authentication/admin-password (with a type of "password") and: /sys/class/firmware-attributes/dell/authentication/admin-hotp (with a type of "hotp") And then the user / userspace can choose which one to use, I guess if the kernel knows that only hotp has been setup and there is no standard password set, then it could hide the /sys/class/firmware-attributes/dell/authentication/admin-password password. TBH I think all these things are (mostly) easily solvable if/when we encounter them. I mean it is definitely good to keep these kind of things in mind. But at some point we might get lost in all the what-ifs we can come up with. >> This would mean that existing userspace software can not work with >> systems using the new "hotp" password atrributes, but that is >> unavoidable. > >> >> I think that the current generic password flow will work well >> for other vendors too, they may need to not cache it in the >> kernel (instead sending it directly to the firmware once), but the basic >> concept of having to write the plain-text bios Admin password before >> being able to change protected settings seems like it is something which >> matches how most current BIOS-es work. And needing a way to re-lock the >> settings also sounds like something which will be pretty common for most >> implementations. >> > > OK so here is another place that I think vendors might have a different > implementation. When you have a BIOS admin password set, Dell requires that > password to interact with any of these attributes. Another vendor might > only require it only for certain attributes they deemed protected. > > So again, Dell's flow might not scale to everyone else. > > I do acknowledge this might be mitigatable by adding a sysfs file to every > attribute for Dell's implementation "is_authentication_required" that is always > 1 when admin password required and in another implementation an OEM might choose > to set that on a case by case basis. If a vendor comes along where authentication is not necessary for *all* attributes, then we could add the "is_authentication_required" as an optional sysfs-attribute for the firmware-attributes and state in the documentation that if that file is lacking that means that authentication is always required. That way the Dell code would not even have to have the "is_authentication_required" sysfs-attribute. >>>> We could even do something like we have for .desktop files >>>> fields, where we add something to the sysfs ABI documentation >>>> that vendors may add vendor specific types prefixed with X-<vendorname>. >>>> >>>> So all in all I believe that we can make using the proposed sysfs ABI >>>> a generic one work, and that this will be worth it to avoid having the >>>> issue of eventually having both a couple of vendor specific APIs + >>>> one grant unified generic API replacing those vendor-APIs >>>> (where we can never drop the vendor specific APIs because of >>>> backward compat. guarantees). >>> >>> I'm personally leaning on the right place to have a vendor agnostic view is >> "outside" >>> of the kernel in an userland library. All the vendor drivers that change >> settings can >>> behave very similarly for the most part, but differences between vendor >> implementations >>> can be better expressed there. >> >> We have tried that before in several cases (that I'm aware of) and this never >> works out. Esp. not when the basic kernel interface is reasonable sane, >> then a lot of people / projects avoid the lib and just poke the kernel API >> directly. We have seen this e.g. with hwmon-class devices and with v4l devices >> and with backlight-class devices. Since I've seen this happen 3 times already >> I'm not a big believer in adding a userspace library to hide the vendor >> differences. >> >> Regards, >> >> Hans > > Another area that comes to mind is Dell's value_modifier and modifier rules. This > dependency logic is handled and expressed by the firmware. You'll notice the Dell > driver only displays the information that came out of the firmware in sysfs and doesn't > do any processing in driver. > > So by using Dell's format, another vendor's driver will need to follow Dell's > formatting and rule validation/generation which their firmware might not support and > they will be forced to implement Dell's schema in their kernel driver. Since we also seem to have some trouble to get these 2 properly documented (I have not looked at v3 yet), I'm fine with making them dell specific by prefixing them with dell-. I guess that that probably even makes sense. > Lastly I want to caution that individual firmware items with the same name might have > a different meaning across vendors. Here is my hypothetical example: > > Dell has an attribute called "Camera" With V3 it populates under: > /sys/devices/platform/dell-wmi-sysman/attributes/Camera > > The description sysfs for it reads as "Enable Camera" and it's possible values are > "Disabled;Enabled;". For Dell this is pretty obviously it turns on and off the camera > functionality. > > For another vendor they might actually not offer to enable/disable the camera but instead > To enable the control of an electromagnetic camera shutter from such an attribute. > Their attribute could still be called "Camera" but the description might read as > "Enable camera shutter control". For them it would still read as "Disabled;Enabled;" > for possible values but have a completely different meaning! > > There is no standard for this, and again userspace will need to basically look at > the directory and structure to figure out what the meaning actually is. I can envision similar issues popping up between different generations / models of Dell hardware even. Specifying what changing the attributes actually does falls (way) outside of the scope of the sysfs ABI IMHO. That will always be the case of please consult your Laptop's / Workstation's / Server's manual. That is actually not much different from the current builtin firmware setup utility experience where the help text is often, well, not helpful. For all I care there is an enum called "HWvirt" with a description of "Hardware virtualization support" and values of "Enabled" and "Disabled" which controls something somewhat or even totally different from what the name and description suggest. That would be less then ideal, but not a problem from the pov of the sysfs ABI for firmware-attributes. It would be a simple case of the garbage in garbage out principle. So this is one problem which I'm happy to punt to userspace and I guess Dell might do a Dell specific utility, which only works one certain model Dell's, which is a lot fancier then the basic sysfs functionality and e.g. consumes the dell-value_modifier and dell-modifier sysfs-attribures. The purpose behind having a unified userspace ABI is to e.g. allow configuring firmware settings for a fleet of machines from: https://wiki.gnome.org/Projects/FleetCommander Using a generic plugin which works across different vendors. And maybe have a simple vendor-agnostic pygtk3 UI which allows users to poke at things, even if they have to figure out in which order they need to change things in some cases (which again is actually not that different from the current builtin firmware setup utility experience for a lot of vendors). I guess a could way to look at the generic sysfs firmware attributes class I'm proposing is looking at it as a lowest common denominator solution. With the addition of vendor specific extensions so that vendors (e.g. Dell) are not limited to only offering functionality offered by the generic, shared ABI. Does that make sense ? Regards, Hans
> > Well if different schemes are supported and each scheme has its own type, > then I would expect there to be say / e.g.: > > /sys/class/firmware-attributes/dell/authentication/admin-password > (with a type of "password") and: > /sys/class/firmware-attributes/dell/authentication/admin-hotp > (with a type of "hotp") > > And then the user / userspace can choose which one to use, > I guess if the kernel knows that only hotp has been setup and > there is no standard password set, then it could hide the > /sys/class/firmware-attributes/dell/authentication/admin-password > password. So you're proposing the flow to userspace that would look like this: Authentication is off ---- # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed 0 # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value Turning on and things that happen using authentication (error examples too): ---- # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed 0 # echo "foobar123" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/new_password # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed 1 # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value -EOPNOTSUPP # echo "foobar456" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value -EACCES # echo "foobar123" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value # echo "" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value -EOPNOTSUPP > > TBH I think all these things are (mostly) easily solvable if/when we > encounter them. I mean it is definitely good to keep these kind of things > in mind. But at some point we might get lost in all the what-ifs we > can come up with. In trying to come up with a generic interface that scales to everyone's needs the what-ifs are critical. Making assumptions on how authentication works means future authentication mechanisms will be painful. > > If a vendor comes along where authentication is not necessary > for *all* attributes, then we could add the "is_authentication_required" > as an optional sysfs-attribute for the firmware-attributes and state > in the documentation that if that file is lacking that means that > authentication is always required. That way the Dell code would not > even have to have the "is_authentication_required" sysfs-attribute. But it's not true on Dell's systems even right now. If you don't have an Admin password configured then you don't need it set for any attribute. If you do have one set you need them for all. And if you need to know to look for /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/is_password_set then userspace needs to know to do this differently for Dell and someone else. So you either need to have a top level is_authentication_required IE /sys/class/firmware-attributes/dell-wmi-sysman/is_authentication_required Or a per attribute one IE /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_required And this decision can't be put off because it has an implication that another vendor may choose to do their authentication differently than Dell. > > Since we also seem to have some trouble to get these 2 properly documented > (I have not looked at v3 yet), I'm fine with making them dell specific by > prefixing them > with dell-. I guess that that probably even makes sense. They're documented in v3. The moment that you have a "Dell specific" attribute what's the point of a common class? You're going to end up with Dell expresses dependencies this way, Lenovo expresses them that way, and HP expresses them some other way and userspace is going to have to sort out the differences. So in userspace you end up with logic that is something like this: 1) (Generic) Check if authentication is set 2) (Dell) Check if you're running on Dell's driver, interpret this dependency or show a message 3) (Lenovo) Check if you're running on Lenovo's driver, interpret this dependency or show a message 4) (HP) Check if you're running on HP's driver, interpret this dependency or show a message 5) (Generic) Check what authentication schemes are supported 6) (Dell) Apply Dell's admin password authentication scheme 7) (Lenovo Example) Apply Lenovo's admin password authentication scheme or their TOTP authentication scheme 8) (Generic) write value into current_value 9) (Generic) Disable authentication So if userspace is going to have to be different anyway for evaluating dependencies and authentication, why go through the trouble to fit everyone into the same class? > > > Lastly I want to caution that individual firmware items with the same name > might have > > a different meaning across vendors. Here is my hypothetical example: > > > > Dell has an attribute called "Camera" With V3 it populates under: > > /sys/devices/platform/dell-wmi-sysman/attributes/Camera > > > > The description sysfs for it reads as "Enable Camera" and it's possible > values are > > "Disabled;Enabled;". For Dell this is pretty obviously it turns on and off > the camera > > functionality. > > > > For another vendor they might actually not offer to enable/disable the > camera but instead > > To enable the control of an electromagnetic camera shutter from such an > attribute. > > Their attribute could still be called "Camera" but the description might > read as > > "Enable camera shutter control". For them it would still read as > "Disabled;Enabled;" > > for possible values but have a completely different meaning! > > > > There is no standard for this, and again userspace will need to basically > look at > > the directory and structure to figure out what the meaning actually is. > > I can envision similar issues popping up between different generations / > models > of Dell hardware even. Dell has an internal committee that oversees the attribute registry. Not all platforms will expose the same attributes. Across generations different sets of attributes will be exposed based upon what they do. So userspace would be able to look at /sys/devices/platform/dell-wmi-sysman/attributes/Camera and know it's turning on/off camera on a Dell system. >Specifying what changing the attributes actually does > falls > (way) outside of the scope of the sysfs ABI IMHO. That will always be the case > of please consult your Laptop's / Workstation's / Server's manual. > That is actually not much different from the current builtin > firmware setup utility experience where the help text is often, > well, not helpful. > > For all I care there is an enum called "HWvirt" with a description of > "Hardware virtualization support" and values of "Enabled" and "Disabled" > which controls something somewhat or even totally different from what the > name and description suggest. That would be less then ideal, but not a problem > from the pov of the sysfs ABI for firmware-attributes. It would be a simple > case of the garbage in garbage out principle. > > So this is one problem which I'm happy to punt to userspace and I guess Dell > might do a Dell specific utility, which only works one certain model Dell's, > which is a lot fancier then the basic sysfs functionality and e.g. consumes > the dell-value_modifier and dell-modifier sysfs-attribures. The goal here is that all of the functionality that would otherwise be expressed in a proprietary utility could also be expressed in sysfs. Having to de-feature the sysfs interface for the purpose of fitting into what's generic across vendors defeats that goal and is why I think it should be a Dell interface in the first place. > > The purpose behind having a unified userspace ABI is to e.g. allow configuring > firmware settings for a fleet of machines from: > > https://wiki.gnome.org/Projects/FleetCommander > > Using a generic plugin which works across different vendors. > Conceptually this makes great sense until you dig into details. In addition to the things I've outlined above here's a few more places that might break: * Attribute naming won't be the same across vendors. To my point with Camera above what if Lenovo DOES support a camera control and calls their value EnableCamera? You couldn't have a single setting across vendors in your fleet and need to carry some logic in your plugin anyway to say if Dell: Camera if Lenovo: EnableCamera. * you could run into situations that Dell's firmware accepts certain requirements for password length or complexity that aren't present in another firmware so you can't use the same password for your whole fleet > And maybe have a simple vendor-agnostic pygtk3 UI which allows users to > poke at things, even if they have to figure out in which order they need > to change things in some cases (which again is actually not that > different from the current builtin firmware setup utility experience > for a lot of vendors). > > I guess a could way to look at the generic sysfs firmware attributes > class I'm proposing is looking at it as a lowest common denominator > solution. With the addition of vendor specific extensions so that > vendors (e.g. Dell) are not limited to only offering functionality > offered by the generic, shared ABI. Does that make sense ? > > Regards, > I really think that trying to fit all the vendors into the same interface is going to stifle areas for innovation in the firmware and kernel space in the name of "simplicity" which really only goes as far as the kernel side. Userspace has to carry delta between vendors no matter what, so why introduce a LCD then? Just as easily we could have: /sys/devices/platform/dell-wmi-sysman/attributes/ Which works 90% the same as: /sys/devices/platform/lenovo-wmi-sysman/attributes/
Hi, On 9/21/20 5:26 PM, Limonciello, Mario wrote: <snip> I will do another more detailed reply in another email, but I would like to focus at the main pain point here, which is the using a generic sysfs-ABI/class vs using a Dell specific sysfs-ABI. >> I guess a could way to look at the generic sysfs firmware attributes >> class I'm proposing is looking at it as a lowest common denominator >> solution. With the addition of vendor specific extensions so that >> vendors (e.g. Dell) are not limited to only offering functionality >> offered by the generic, shared ABI. Does that make sense ? >> >> Regards, >> > > I really think that trying to fit all the vendors into the same interface is going > to stifle areas for innovation in the firmware and kernel space in the name of > "simplicity" which really only goes as far as the kernel side. Userspace has > to carry delta between vendors no matter what, so why introduce a LCD then? > > Just as easily we could have: > /sys/devices/platform/dell-wmi-sysman/attributes/ > > Which works 90% the same as: > /sys/devices/platform/lenovo-wmi-sysman/attributes/ So the reason why I want a class interface for this is to allow say FleetCommander to have a generic plugin implementing that 90%, so no deps, only support plain admin-password authentication. Allowing such a generic plugin requires 2 things: 1) Ensuring that the 90% overlapping functionality offers a 100% identical userspace ABI, thus a shared sysfs ABI definition 2) That userspace has a generic way to enumerate devices/drivers implementing this shared sysfs ABI, and we have a standard mechanism for enumerating drivers which implement a standard ABI, that is we make them register class devices under /sys/class/<abi-name>. I have not heard any convincing arguments against why would should not or can not have these 2 things. All I'm hearing is a vague fear that this may "stifle areas for innovation in the firmware and kernel space". Honestly I have the feeling we are going in circles in this discussion and I really do not understand why you are so dead set against having a common sysfs ABI/class for this? In part of the snipped text you write "Having to de-feature the sysfs interface", but I have not asked you to remove any features anywhere in this thread! So I really do not understand where this fear of not being able to implement certain, possibly Dell specific, features comes from? You mentioned that the way the dependencies are expressed are highly Dell specific, so I suggested allowing having vendor extensions like dell-modifiers and dell-value_modifiers. The whole idea behind allowing vendor-extensions is actually the exact opposite of de-featuring the sysfs interface. Regards, Hans
Hi, On 9/21/20 5:26 PM, Limonciello, Mario wrote: >> >> Well if different schemes are supported and each scheme has its own type, >> then I would expect there to be say / e.g.: >> >> /sys/class/firmware-attributes/dell/authentication/admin-password >> (with a type of "password") and: >> /sys/class/firmware-attributes/dell/authentication/admin-hotp >> (with a type of "hotp") >> >> And then the user / userspace can choose which one to use, >> I guess if the kernel knows that only hotp has been setup and >> there is no standard password set, then it could hide the >> /sys/class/firmware-attributes/dell/authentication/admin-password >> password. > > So you're proposing the flow to userspace that would look like this: > > Authentication is off > ---- > # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed > 0 > # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value > > > > Turning on and things that happen using authentication (error examples too): > ---- > # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed > 0 > # echo "foobar123" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/new_password > # cat /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_needed > 1 > # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value > -EOPNOTSUPP Why would this be -EOPNOTSUPP and not just -EACCESS too ? To in the end both are access denials, no password being set is really just a variant of the wrong password being set. > # echo "foobar456" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password > # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value > -EACCES > # echo "foobar123" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password > # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value > # echo "" | sudo tee /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password > # echo "enabled" | sud tee /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/current_value > -EOPNOTSUPP > > >> >> TBH I think all these things are (mostly) easily solvable if/when we >> encounter them. I mean it is definitely good to keep these kind of things >> in mind. But at some point we might get lost in all the what-ifs we >> can come up with. > > In trying to come up with a generic interface that scales to everyone's needs > the what-ifs are critical. Making assumptions on how authentication works means > future authentication mechanisms will be painful. The way I see it the authentication mechanism and the ABI for actually changing the settings are pretty much orthogonal. So we can add new authentication mechanisms without that impacting the ABI for actually changing the settings. >> If a vendor comes along where authentication is not necessary >> for *all* attributes, then we could add the "is_authentication_required" >> as an optional sysfs-attribute for the firmware-attributes and state >> in the documentation that if that file is lacking that means that >> authentication is always required. That way the Dell code would not >> even have to have the "is_authentication_required" sysfs-attribute. > > But it's not true on Dell's systems even right now. If you don't have > an Admin password configured then you don't need it set for any attribute. > If you do have one set you need them for all. And if you need to know to look for > /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/is_password_set > then userspace needs to know to do this differently for Dell and someone else. You are mixing up authentication and authorization here. I guess that you are arguing for is an is_authorization_required attribute, which can return "none" and "admin" (for now). When no Admin password is set, everyone automatically get admin level authority and in the Dell case (for now) all firmware-attributes require admin authorization. Looking at it this way has the advantage that for the current Dell scenario the is_authorization_required sysfs-attribute would simply always return admin. Which is why I argued that we could omit it for now and add it as an optional attribute, defaulting to admin when not present, later. But if you want to we can certainly add it now and have it present from the get go. > So you either need to have a top level is_authentication_required > IE /sys/class/firmware-attributes/dell-wmi-sysman/is_authentication_required > > Or a per attribute one > IE /sys/class/firmware-attributes/dell-wmi-sysman/attributes/Touchscreen/is_authentication_required This obviously needs to be a per firmware-attribute setting, because in the future and/or with other vendors different attributes may require different levels of authorization. > And this decision can't be put off because it has an implication that another > vendor may choose to do their authentication differently than Dell. > >> >> Since we also seem to have some trouble to get these 2 properly documented >> (I have not looked at v3 yet), I'm fine with making them dell specific by >> prefixing them >> with dell-. I guess that that probably even makes sense. > > They're documented in v3. The moment that you have a "Dell specific" attribute > what's the point of a common class? You're going to end up with Dell expresses > dependencies this way, Lenovo expresses them that way, and HP expresses them some > other way and userspace is going to have to sort out the differences. > > So in userspace you end up with logic that is something like this: > 1) (Generic) Check if authentication is set > 2) (Dell) Check if you're running on Dell's driver, interpret this dependency or show a message > 3) (Lenovo) Check if you're running on Lenovo's driver, interpret this dependency or show a message > 4) (HP) Check if you're running on HP's driver, interpret this dependency or show a message > 5) (Generic) Check what authentication schemes are supported > 6) (Dell) Apply Dell's admin password authentication scheme > 7) (Lenovo Example) Apply Lenovo's admin password authentication scheme or their TOTP authentication scheme > 8) (Generic) write value into current_value > 9) (Generic) Disable authentication > > So if userspace is going to have to be different anyway for evaluating dependencies and authentication, why > go through the trouble to fit everyone into the same class? Parsing the dependencies is not required for a functional userspace application. As I explicitly stated in my previous email in many existing builtin firmware setup UIs the dependencies are not even shown. As for the authentication for now having a straight forward admin + system/boot level passwords covers like 99% of the existing use-cases. There is no need for separate Dell / Lenovo admin password schemes as you list above those will work identically from the sysfs interface pov. So having a common class interface will allow userspace apps which work with 99% of the systems currently out there (assuming they ever get a driver implementing the class). <snip> >> Specifying what changing the attributes actually does >> falls >> (way) outside of the scope of the sysfs ABI IMHO. That will always be the case >> of please consult your Laptop's / Workstation's / Server's manual. >> That is actually not much different from the current builtin >> firmware setup utility experience where the help text is often, >> well, not helpful. >> >> For all I care there is an enum called "HWvirt" with a description of >> "Hardware virtualization support" and values of "Enabled" and "Disabled" >> which controls something somewhat or even totally different from what the >> name and description suggest. That would be less then ideal, but not a problem >> from the pov of the sysfs ABI for firmware-attributes. It would be a simple >> case of the garbage in garbage out principle. >> >> So this is one problem which I'm happy to punt to userspace and I guess Dell >> might do a Dell specific utility, which only works one certain model Dell's, >> which is a lot fancier then the basic sysfs functionality and e.g. consumes >> the dell-value_modifier and dell-modifier sysfs-attribures. > > The goal here is that all of the functionality that would otherwise be expressed > in a proprietary utility could also be expressed in sysfs. Having to de-feature > the sysfs interface for the purpose of fitting into what's generic across vendors > defeats that goal and is why I think it should be a Dell interface in the first > place. I have not asked you to de-feature the sysfs interface anywhere in this thread! Regards, Hans
Hi, On 9/22/20 10:57 AM, Hans de Goede wrote: > Hi, > > On 9/21/20 5:26 PM, Limonciello, Mario wrote: > > <snip> > > I will do another more detailed reply in another email, but I would like to focus > at the main pain point here, which is the using a generic sysfs-ABI/class vs using > a Dell specific sysfs-ABI. > >>> I guess a could way to look at the generic sysfs firmware attributes >>> class I'm proposing is looking at it as a lowest common denominator >>> solution. With the addition of vendor specific extensions so that >>> vendors (e.g. Dell) are not limited to only offering functionality >>> offered by the generic, shared ABI. Does that make sense ? >>> >>> Regards, >>> >> >> I really think that trying to fit all the vendors into the same interface is going >> to stifle areas for innovation in the firmware and kernel space in the name of >> "simplicity" which really only goes as far as the kernel side. Userspace has >> to carry delta between vendors no matter what, so why introduce a LCD then? >> >> Just as easily we could have: >> /sys/devices/platform/dell-wmi-sysman/attributes/ >> >> Which works 90% the same as: >> /sys/devices/platform/lenovo-wmi-sysman/attributes/ > > So the reason why I want a class interface for this is to allow say > FleetCommander to have a generic plugin implementing that 90%, so > no deps, only support plain admin-password authentication. > > Allowing such a generic plugin requires 2 things: > > 1) Ensuring that the 90% overlapping functionality offers a 100% > identical userspace ABI, thus a shared sysfs ABI definition > > 2) That userspace has a generic way to enumerate devices/drivers > implementing this shared sysfs ABI, and we have a standard > mechanism for enumerating drivers which implement a standard ABI, > that is we make them register class devices under /sys/class/<abi-name>. > > I have not heard any convincing arguments against why would > should not or can not have these 2 things. All I'm hearing is > a vague fear that this may "stifle areas for innovation in the firmware > and kernel space". > > Honestly I have the feeling we are going in circles in this discussion > and I really do not understand why you are so dead set against having > a common sysfs ABI/class for this? > > In part of the snipped text you write "Having to de-feature the sysfs > interface", but I have not asked you to remove any features anywhere in > this thread! > > So I really do not understand where this fear of not being able to > implement certain, possibly Dell specific, features comes from? > > You mentioned that the way the dependencies are expressed are > highly Dell specific, so I suggested allowing having vendor > extensions like dell-modifiers and dell-value_modifiers. The whole > idea behind allowing vendor-extensions is actually the exact > opposite of de-featuring the sysfs interface. So I've been thinking more about this and to me this whole argument sounds a lot like we just want to have our own little corner to play in, without needing to worry about what other vendors do. And then Lenovo, and HP and who knows else will all want the same and we and up with at least 5 different interfaces. It is bad enough that we already have to deal with having 5+ different firmware interfaces for this and worse even for silly things like setting the brightness level for the kbd backlight, which is such a trivial thing that you would think PC vendors should be able to sit down and agree on a single ACPI API for it. We are NOT going to take this lets all do our own thing approach and also let this trickle up in the stack to the kernel <-> userspace API! One of the tasks of the kernel is to act as a HAL and this clearly falls under that. Imagine if userspace code would need to use different kernel APIs for storage/filesystem accesses depending on if they were running on a Dell or a Lenovo machines. Or having different APIs to access the network depending on the machine vendor... So I'm sorry, but I'm drawing a line in the sand here, unless you can come up with some really convincing NEW arguments why this needs to be a Dell specific interface, the Dell firmware-attributes code must use a generic sysfs-ABI/class to get accepted upstream. Note that I think the currently suggested private Dell ABI is actually pretty suitable for such a generic sysfs-ABI/class, so I'm not asking you to make a lot of changes here. Regards, Hans
> So I've been thinking more about this and to me this whole argument > sounds a lot like we just want to have our own little corner to > play in, without needing to worry about what other vendors do. > > And then Lenovo, and HP and who knows else will all want the same > and we and up with at least 5 different interfaces. > > It is bad enough that we already have to deal with having 5+ > different firmware interfaces for this and worse even for silly > things like setting the brightness level for the kbd backlight, > which is such a trivial thing that you would think PC vendors > should be able to sit down and agree on a single ACPI API for it. > > We are NOT going to take this lets all do our own thing approach and > also let this trickle up in the stack to the kernel <-> userspace API! > > One of the tasks of the kernel is to act as a HAL and this clearly > falls under that. Imagine if userspace code would need to use different > kernel APIs for storage/filesystem accesses depending on if they were > running on a Dell or a Lenovo machines. Or having different APIs to > access the network depending on the machine vendor... > > So I'm sorry, but I'm drawing a line in the sand here, unless you can > come up with some really convincing NEW arguments why this needs to > be a Dell specific interface, the Dell firmware-attributes code must > use a generic sysfs-ABI/class to get accepted upstream. > > Note that I think the currently suggested private Dell ABI is actually > pretty suitable for such a generic sysfs-ABI/class, so I'm not asking > you to make a lot of changes here. > > Regards, > > Hans We'll try this for the v4 patch series.
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman new file mode 100644 index 000000000000..8ca3e502ed89 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-sysman @@ -0,0 +1,201 @@ +What: /sys/devices/platform/dell-wmi-sysman/attributes/ +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + The Dell WMI Systems Management Driver provides a sysfs interface + for systems management software to enable BIOS configuration + capability on certain Dell systems. This directory exposes + interfaces for interacting with BIOS attributes + +What: /sys/devices/platform/dell-wmi-sysman/attributes/reset_bios +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + This attribute can be used to reset the BIOS Configuration. + Specifically, it tells which type of reset BIOS configuration is being + requested on the host. + + Reading from it returns a list of supported options encoded as: + + 'builtinsafe' (Built in safe configuration profile) + 'lastknowngood' (Last known good saved configuration profile) + 'factory' (Default factory settings configuration profile) + 'custom' (Custom saved configuration profile) + + The currently selected option is printed in square brackets as + shown below: + + # echo "factory" > sys/devices/platform/dell-wmi-sysman/attributes/reset_bios + + # cat sys/devices/platform/dell-wmi-sysman/attributes/reset_bios + # builtinsafe lastknowngood [factory] custom + + Note that any changes to this attribute requires a reboot + for changes to take effect. + +What: /sys/devices/platform/dell-wmi-sysman/attributes/pending_reboot +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + A read-only attribute enumerating if a reboot is pending + on any BIOS attribute change. + + 0: No pending reboot + 1: Pending reboot + +What: /sys/devices/platform/dell-wmi-sysman/attributes/enumeration/<attr>/ +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + This directory exposes interfaces for interaction with + BIOS enumeration attributes. + + Enumeration attributes are settings that accept set of + pre-defined valid values. + + current_value: A file that can be read to obtain the current + value of the <attr> + + This file can also be written to in order to update + the value of a <attr> + + default_value: A file that can be read to obtain the default + value of the <attr> + + display_name: A file that can be read to obtain a user friendly + description of the at <attr> + + display_name_language_code: A file that can be read to obtain + the language code corresponding to the "display_name" of the <attr> + + modifier: A file that can be read to obtain attribute-level + dependency rule which has to be met to configure <attr> + + possible_value: A file that can be read to obtain the possible + value of the <attr> + + value_modifier: A file that can be read to obtain value-level + dependency on a possible value which has to be met to configure <attr> + +What: /sys/devices/platform/dell-wmi-sysman/attributes/integer/<attr>/ +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + This directory exposes interfaces for interaction with + BIOS integer attributes. + + Integer attributes are settings that accept a range of + numerical values for inputs. Each BIOS integer has a + lower bound and an upper bound on the values that it can take. + + current_value: A file that can be read to obtain the current + value of the <attr> + + This file can also be written to in order to update + the value of an <attr>. + + default_value: A file that can be read to obtain the default + value of the <attr> + + display_name: A file that can be read to obtain a user friendly + description of the at <attr> + + display_name_language_code: A file that can be read to obtain + the language code corresponding to the "display_name" of the <attr> + + lower_bound: A file that can be read to obtain the lower + bound value of the <attr> + + modifier: A file that can be read to obtain attribute-level + dependency rule which has to be met to configure <attr> + + scalar_increment: A file that can be read to obtain the + resolution of the incremental value this attribute accepts. + + upper_bound: A file that can be read to obtain the upper + bound value of the <attr> + +What: /sys/devices/platform/dell-wmi-sysman/attributes/string/<attr>/ +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + This directory exposes interfaces for interaction with + BIOS string attributes. + + String attributes are settings that accept a string for an input. + Each BIOS string is characterized by the minimum length of the string, + the maximum length of the string, and the type of the string. + + current_value: A file that can be read to obtain the current + value of the <attr> + + This file can also be written to in order to update + the value of an <attr>. + + default_value: A file that can be read to obtain the default + value of the <attr> + + display_name: A file that can be read to obtain a user friendly + description of the at <attr> + + display_name_language_code: A file that can be read to obtain + the language code corresponding to the "display_name" of the <attr> + + max_length: A file that can be read to obtain the maximum + length value of the <attr> + + min_length: A file that can be read to obtain the minimum + length value of the <attr> + + modifier: A file that can be read to obtain attribute-level + dependency rule which has to be met to configure <attr> + +What: /sys/devices/platform/dell-wmi-sysman/attributes/password/<attr>/ +Date: October 2020 +KernelVersion: 5.9 +Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, + Mario Limonciello <mario.limonciello@dell.com>, + Prasanth KSR <prasanth.ksr@dell.com> +Description: + This directory exposes interfaces for interaction with + BIOS password attributes. + + BIOS Admin password and System Password can be set, reset or cleared + using these attributes. + + An "Admin" password is used for preventing modification to the BIOS settings. + A "System" password is required to boot a machine. + + is_password_set: A file that can be read + to obtain flag to see if a password is set on <attr> + + max_password_length: A file that can be read to obtain the + maximum length of the Password + + min_password_length: A file that can be read to obtain the + minimum length of the Password + + current_password: A write only value used for privileged access + such as setting attributes when a system or admin password is set + or resetting to a new password + + new_password: A write only value that when used in tandem with + current_password will reset a system or admin password. diff --git a/MAINTAINERS b/MAINTAINERS index e64cdde81851..176311d712db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4879,6 +4879,15 @@ M: Mario Limonciello <mario.limonciello@dell.com> S: Maintained F: drivers/platform/x86/dell-wmi-descriptor.c +DELL WMI SYSMAN DRIVER +M: Divya Bharathi <divya.bharathi@dell.com> +M: Mario Limonciello <mario.limonciello@dell.com> +M: Prasanth Ksr <prasanth.ksr@dell.com> +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/dell-wmi-*-attributes.* +F: drivers/platform/x86/dell-wmi-*-interface.c + DELL WMI NOTIFICATIONS DRIVER M: Matthew Garrett <mjg59@srcf.ucam.org> M: Pali Rohár <pali@kernel.org> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0581a54cf562..de66373554c2 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -430,6 +430,17 @@ config DELL_WMI To compile this driver as a module, choose M here: the module will be called dell-wmi. +config DELL_WMI_SYSMAN + tristate "Dell WMI SYSMAN" + depends on ACPI_WMI + depends on DMI + help + This driver allows changing BIOS settings on many Dell machines from + 2018 and newer without the use of any additional software. + + To compile this driver as a module, choose M here: the module will + be called dell-wmi-sysman. + config DELL_WMI_DESCRIPTOR tristate depends on ACPI_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 2b85852a1a87..bee03f1af28f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -47,6 +47,14 @@ obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o +obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o +dell-wmi-sysman-objs := dell-wmi-sysman-attributes.o \ + dell-wmi-enum-attributes.o \ + dell-wmi-int-attributes.o \ + dell-wmi-string-attributes.o \ + dell-wmi-passobj-attributes.o \ + dell-wmi-passwordattr-interface.o \ + dell-wmi-biosattr-interface.o # Fujitsu obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o diff --git a/drivers/platform/x86/dell-wmi-biosattr-interface.c b/drivers/platform/x86/dell-wmi-biosattr-interface.c new file mode 100644 index 000000000000..decf124d4388 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-biosattr-interface.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions corresponding to SET methods under BIOS attributes interface GUID for use + * with dell-wmi-sysman + * + * Copyright (c) 2020 Dell Inc. + */ + +#include <linux/wmi.h> +#include "dell-wmi-sysman-attributes.h" + +#define SETDEFAULTVALUES_METHOD_ID 0x02 +#define SETBIOSDEFAULTS_METHOD_ID 0x03 +#define SETATTRIBUTE_METHOD_ID 0x04 + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +struct wmi_interface_priv { + struct list_head list; + struct wmi_device *wdev; + struct device *child; + bool pending_changes; +}; +static LIST_HEAD(interface_list); + +static inline struct wmi_interface_priv *get_first_interface_priv(void) +{ + return list_first_entry_or_null(&interface_list, + struct wmi_interface_priv, + list); +} + +static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, + int method_id) +{ + 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, method_id, &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); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return map_wmi_error(ret); +} + +/** + * get_pending_changes() - Fetch if any changes are pending + * + * Checks if any changes have been written that will be changed + * after a system reboot + **/ +bool get_pending_changes(void) +{ + struct wmi_interface_priv *priv; + + priv = get_first_interface_priv(); + if (priv) + return priv->pending_changes; + return 0; +} + +/** + * set_attribute() - Update an attribute value + * @a_name: The attribute name + * @a_value: The attribute value + * + * Sets an attribute to new value + **/ +int set_attribute(const char *a_name, const char *a_value) +{ + int ret = -1; + int i; + u8 *name_len, *value_len; + char *current_password, *attribute_name, *attribute_value; + size_t security_area_size; + size_t string_area_size; + size_t buffer_size; + struct wmi_interface_priv *priv; + char *buffer; + + /* look up if user set a password for the requests */ + current_password = get_current_password("Admin"); + if (!current_password) + return -ENODEV; + + /* password is set */ + if (strlen(current_password) > 0) + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + + strlen(current_password) % 2; + /* password not set */ + else + security_area_size = sizeof(u32) * 2; + string_area_size = (strlen(a_name) + strlen(a_value))*2; + buffer_size = security_area_size + string_area_size + sizeof(u16) * 2; + + buffer = kzalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* build security area */ + if (strlen(current_password) > 0) + populate_security_buffer(buffer, current_password); + + name_len = buffer + security_area_size; + attribute_name = name_len + sizeof(u16); + value_len = attribute_name + strlen(a_name)*2; + attribute_value = value_len + sizeof(u16); + + /* turn into UTF16 strings, no NULL terminator */ + *name_len = strlen(a_name)*2; + *value_len = strlen(a_value)*2; + for (i = 0; i < strlen(a_name); i++) + attribute_name[i*2] = a_name[i]; + for (i = 0; i < strlen(a_value); i++) + attribute_value[i*2] = a_value[i]; + + mutex_lock(&call_mutex); + priv = get_first_interface_priv(); + if (!priv) { + ret = -ENODEV; + pr_err(DRIVER_NAME ": no WMI backend bound"); + goto out_set_attribute; + } + + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, + SETATTRIBUTE_METHOD_ID); + if (ret == -EOPNOTSUPP) + dev_err(&priv->wdev->dev, "admin password must be configured"); + else if (ret == -EACCES) + dev_err(&priv->wdev->dev, "invalid password"); + + priv->pending_changes = 1; +out_set_attribute: + kfree(buffer); + mutex_unlock(&call_mutex); + + return ret; +} + +/** + * set_bios_defaults() - Resets BIOS defaults + * @deftype: the type of BIOS value reset to issue. + * + * Resets BIOS defaults + **/ +int set_bios_defaults(u8 deftype) +{ + int ret = -1; + u8 *defaultType; + char *current_password, *buffer; + size_t security_area_size; + size_t integer_area_size = sizeof(u8); + size_t buffer_size; + struct wmi_interface_priv *priv; + + /* look up if user set a password for the requests */ + current_password = get_current_password("Admin"); + if (!current_password) + return -ENODEV; + + /* password is set */ + if (strlen(current_password) > 0) + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + + strlen(current_password) % 2; + /* password not set */ + else + security_area_size = sizeof(u32) * 2; + + buffer_size = security_area_size + integer_area_size; + buffer = kzalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* build security area */ + if (strlen(current_password) > 0) + populate_security_buffer(buffer, current_password); + + mutex_lock(&call_mutex); + priv = get_first_interface_priv(); + if (!priv) { + ret = -ENODEV; + pr_err(DRIVER_NAME ": no WMI backend bound"); + goto out_bios_defaults; + } + + defaultType = buffer + security_area_size; + *defaultType = deftype; + + ret = call_biosattributes_interface(priv->wdev, buffer, buffer_size, + SETBIOSDEFAULTS_METHOD_ID); + if (ret) + dev_err(&priv->wdev->dev, "reset BIOS defaults failed: %d", ret); + priv->pending_changes = 1; +out_bios_defaults: + kfree(buffer); + mutex_unlock(&call_mutex); + + return ret; +} + +static int dell_wmi_bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) +{ + struct wmi_interface_priv *priv; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &interface_list); + mutex_unlock(&list_mutex); + return 0; +} + +static int dell_wmi_bios_attr_set_interface_remove(struct wmi_device *wdev) +{ + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); + + mutex_lock(&call_mutex); + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + mutex_unlock(&call_mutex); + return 0; +} + +static const struct wmi_device_id dell_wmi_bios_attr_set_interface_id_table[] = { + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, + { }, +}; +static struct wmi_driver dell_wmi_bios_attr_set_interface_driver = { + .driver = { + .name = DRIVER_NAME"-set" + }, + .probe = dell_wmi_bios_attr_set_interface_probe, + .remove = dell_wmi_bios_attr_set_interface_remove, + .id_table = dell_wmi_bios_attr_set_interface_id_table, +}; + +int init_dell_wmi_bios_attr_set_interface(void) +{ + return wmi_driver_register(&dell_wmi_bios_attr_set_interface_driver); +} + +void exit_dell_wmi_bios_attr_set_interface(void) +{ + wmi_driver_unregister(&dell_wmi_bios_attr_set_interface_driver); +} + +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_set_interface_id_table); 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..a2d8ae291d5c --- /dev/null +++ b/drivers/platform/x86/dell-wmi-enum-attributes.c @@ -0,0 +1,207 @@ +// 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" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ +struct enumeration_data { + char attribute_name[MAX_BUFF]; + char display_name_language_code[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_value_count; + char possible_value[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; + + 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) +{ + int ret = -EINVAL; + char *options, *tmp, *p; + + options = tmp = kstrdup((enumeration_data[instance_id].possible_value), 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_value, enumeration); +struct kobj_attribute poss_val = + __ATTR_RO(possible_value); + +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, + 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 i, next_obj; + int retval = sysfs_create_group(attr_name_kobj, &enumeration_attr_group); + + if (retval) + goto out; + + mutex_lock(&call_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_value_count = + (uintptr_t) enumeration_obj[++next_obj].string.pointer; + + for (i = 0; i < enumeration_data[instance_id].possible_value_count; i++) { + strcat(enumeration_data[instance_id].possible_value, + enumeration_obj[++next_obj].string.pointer); + strcat(enumeration_data[instance_id].possible_value, ";"); + } + mutex_unlock(&call_mutex); + +out: + return retval; +} + +/** + * exit_enum_attributes() - Clear all attribute data + * @kset: The kset to free + * + * Clears all data allocated for this group of attributes + **/ +void exit_enum_attributes(struct kset *kset) +{ + struct kobject *pos, *next; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pos, next, &kset->list, entry) { + sysfs_remove_group(pos, &enumeration_attr_group); + } + mutex_unlock(&list_mutex); + mutex_lock(&call_mutex); + kfree(enumeration_data); + mutex_unlock(&call_mutex); +} diff --git a/drivers/platform/x86/dell-wmi-int-attributes.c b/drivers/platform/x86/dell-wmi-int-attributes.c new file mode 100644 index 000000000000..222971be2b48 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-int-attributes.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions corresponding to integer type attributes under BIOS Integer GUID for use with + * dell-wmi-sysman + * + * Copyright (c) 2020 Dell Inc. + */ + +#include "dell-wmi-sysman-attributes.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +enum int_properties {LOWER_BOUND = 6, UPPER_BOUND, SCALAR_INCR}; + +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ +struct integer_data { + char attribute_name[MAX_BUFF]; + char display_name_language_code[MAX_BUFF]; + char display_name[MAX_BUFF]; + int default_value; + int current_value; + char modifier[MAX_BUFF]; + int lower_bound; + int upper_bound; + int scalar_increment; +}; + +static struct integer_data *integer_data; +static int integer_instances_count; +get_instance_id(integer); + +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_integer_instance_id(kobj); + if (instance_id >= 0) { + union acpi_object *obj; + + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); + if (!obj) + return -AE_ERROR; + integer_data[instance_id].current_value = + (uintptr_t)obj->package.elements[CURRENT_VAL].string.pointer; + kfree(obj); + return sprintf(buf, "%d\n", integer_data[instance_id].current_value); + } + return -EIO; +} + +/** + * validate_integer_input() - Validate input of current_value against lower and upper bound + * @instance_id: The instance on which input is validated + * @buf: Input value + **/ +int validate_integer_input(int instance_id, const char *buf) +{ + int in_val; + int ret = -EINVAL; + + if (kstrtoint(buf, 0, &in_val)) + return -EINVAL; + if ((in_val >= integer_data[instance_id].lower_bound) && + (in_val <= integer_data[instance_id].upper_bound)) + ret = 0; + + return ret; +} + +attribute_s_property_show(display_name_language_code, integer); +static struct kobj_attribute integer_displ_langcode = + __ATTR_RO(display_name_language_code); + +attribute_s_property_show(display_name, integer); +struct kobj_attribute integer_displ_name = + __ATTR_RO(display_name); + +attribute_n_property_show(default_value, integer); +struct kobj_attribute integer_default_val = + __ATTR_RO(default_value); + +attribute_property_store(current_value, integer); +struct kobj_attribute integer_current_val = + __ATTR_RW(current_value); + +attribute_s_property_show(modifier, integer); +struct kobj_attribute integer_modifier = + __ATTR_RO(modifier); + +attribute_n_property_show(lower_bound, integer); +struct kobj_attribute integer_lower_bound = + __ATTR_RO(lower_bound); + +attribute_n_property_show(upper_bound, integer); +struct kobj_attribute integer_upper_bound = + __ATTR_RO(upper_bound); + +attribute_n_property_show(scalar_increment, integer); +struct kobj_attribute integer_scalar_increment = + __ATTR_RO(scalar_increment); + +static struct attribute *integer_attrs[] = { + &integer_displ_langcode.attr, + &integer_displ_name.attr, + &integer_default_val.attr, + &integer_current_val.attr, + &integer_modifier.attr, + &integer_lower_bound.attr, + &integer_upper_bound.attr, + &integer_scalar_increment.attr, + NULL, +}; + +static const struct attribute_group integer_attr_group = { + .attrs = integer_attrs, +}; + +int alloc_int_data(void) +{ + int ret = 0; + + integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); + integer_data = kzalloc((sizeof(struct integer_data) * integer_instances_count), GFP_KERNEL); + if (!integer_data) + ret = -ENOMEM; + return ret; +} + +/** + * populate_enum_data() - Populate all properties of an instance under integer attribute + * @integer_obj: ACPI object with integer data + * @instance_id: The instance to enumerate + * @attr_name_kobj: The parent kernel object + **/ +int populate_int_data(union acpi_object *integer_obj, int instance_id, + struct kobject *attr_name_kobj) +{ + int retval = sysfs_create_group(attr_name_kobj, &integer_attr_group); + + if (retval) + goto out; + + mutex_lock(&call_mutex); + strncpy_attr(integer_data[instance_id].attribute_name, + integer_obj[ATTR_NAME].string.pointer); + strncpy_attr(integer_data[instance_id].display_name_language_code, + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); + strncpy_attr(integer_data[instance_id].display_name, + integer_obj[DISPLAY_NAME].string.pointer); + integer_data[instance_id].default_value = + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; + integer_data[instance_id].current_value = + (uintptr_t)integer_obj[CURRENT_VAL].string.pointer; + strncpy_attr(integer_data[instance_id].modifier, integer_obj[MODIFIER].string.pointer); + integer_data[instance_id].lower_bound = + (uintptr_t)integer_obj[LOWER_BOUND].string.pointer; + integer_data[instance_id].upper_bound = + (uintptr_t)integer_obj[UPPER_BOUND].string.pointer; + integer_data[instance_id].scalar_increment = + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; + mutex_unlock(&call_mutex); + +out: + return retval; +} + +/** + * exit_int_attributes() - Clear all attribute data + * @kset: The kset to free + * + * Clears all data allocated for this group of attributes + **/ +void exit_int_attributes(struct kset *kset) +{ + struct kobject *pos, *next; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pos, next, &kset->list, entry) { + sysfs_remove_group(pos, &integer_attr_group); + } + mutex_unlock(&list_mutex); + mutex_lock(&call_mutex); + kfree(integer_data); + mutex_unlock(&call_mutex); +} 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..d430ecf2387b --- /dev/null +++ b/drivers/platform/x86/dell-wmi-passobj-attributes.c @@ -0,0 +1,161 @@ +// 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" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +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; +}; + +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; + + 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; + + ret = set_current_password(kobj->name, buf); + 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); + +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, + 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(&call_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; + mutex_unlock(&call_mutex); + +out: + return retval; +} + +/** + * exit_po_attributes() - Clear all attribute data + * @kset: The kset to free + * + * Clears all data allocated for this group of attributes + **/ +void exit_po_attributes(struct kset *kset) +{ + struct kobject *pos, *next; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pos, next, &kset->list, entry) { + sysfs_remove_group(pos, &po_attr_group); + } + mutex_unlock(&list_mutex); + mutex_lock(&call_mutex); + kfree(po_data); + mutex_unlock(&call_mutex); +} 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..9d7ac36e232f --- /dev/null +++ b/drivers/platform/x86/dell-wmi-passwordattr-interface.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions corresponding to SET password methods under BIOS attributes interface GUID + * + * Copyright (c) 2020 Dell Inc. + */ + +#include <linux/wmi.h> +#include "dell-wmi-sysman-attributes.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +struct wmi_interface_priv { + struct list_head list; + struct wmi_device *wdev; + struct device *child; + char current_admin_password[MAX_BUFF]; + char current_system_password[MAX_BUFF]; +}; +static LIST_HEAD(interface_list); + +static inline struct wmi_interface_priv *get_first_interface_priv(void) +{ + return list_first_entry_or_null(&interface_list, + struct wmi_interface_priv, + list); +} + +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); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return map_wmi_error(ret); +} + +/** + * get_current_password() - Get the current stored password value + * @password_type: The type of password to store + **/ +char *get_current_password(const char *password_type) +{ + struct wmi_interface_priv *priv = get_first_interface_priv(); + + if (!priv) { + pr_err(DRIVER_NAME ": no WMI backend bound"); + return NULL; + } + if (strcmp(password_type, "Admin") == 0) + return priv->current_admin_password; + if (strcmp(password_type, "System") == 0) + return priv->current_system_password; + dev_err(&priv->wdev->dev, "unknown password type %s", password_type); + return NULL; +} + +/** + * set_current_password() - Store current password + * @password_type: The type of password to store + * @current: The current value + * + * Sets the current value of the password. + * This is used for: + * - Resetting password + * - Basis for any functions requiring password to execute + **/ +int set_current_password(const char *password_type, const char *cur) +{ + char *current_password = get_current_password(password_type); + + if (!current_password) + return -ENODEV; + strncpy(current_password, cur, (strlen(cur) + 1)); + return 0; +} + +/** + * set_new_password() - Sets a system admin password + * @password_type: The type of password to set + * @new: The new password + * + * Sets the password using plaintext interface + **/ +int set_new_password(const char *password_type, const char *new) +{ + int ret = -1; + int i; + u8 *type_len, *old_len, *new_len; + char *current_password, *type_value, *old_value, *new_value; + size_t security_area_size; + size_t string_area_size; + size_t buffer_size; + struct wmi_interface_priv *priv; + char *buffer; + + mutex_lock(&call_mutex); + priv = get_first_interface_priv(); + if (!priv) { + ret = -ENODEV; + pr_err(DRIVER_NAME ": no WMI backend bound"); + goto out_close_mutex; + } + current_password = get_current_password(password_type); + if (!current_password) { + ret = -ENODEV; + goto out_close_mutex; + } + /* password is set */ + if (strlen(current_password) > 0) + security_area_size = (sizeof(u32) * 2) + strlen(current_password) + + strlen(current_password) % 2; + /* password not set */ + else + security_area_size = sizeof(u32) * 2; + string_area_size = (strlen(password_type) + strlen(current_password) + strlen(new))*2; + buffer_size = security_area_size + string_area_size + sizeof(u16) * 3; + + buffer = kzalloc(buffer_size, GFP_KERNEL); + + if (!buffer) { + ret = -ENOMEM; + goto out_close_mutex; + } + + /* build security area */ + if (strlen(current_password) > 0) + populate_security_buffer(buffer, current_password); + + type_len = buffer + security_area_size; + type_value = type_len + sizeof(u16); + old_len = type_value + strlen(password_type)*2; + old_value = old_len + sizeof(u16); + new_len = old_value + strlen(current_password)*2; + new_value = new_len + sizeof(u16); + + /* turn into UTF16 strings, no NULL terminator */ + *type_len = strlen(password_type)*2; + *old_len = strlen(current_password)*2; + *new_len = strlen(new)*2; + for (i = 0; i < strlen(password_type); i++) + type_value[i*2] = password_type[i]; + for (i = 0; i < strlen(current_password); i++) + old_value[i*2] = current_password[i]; + for (i = 0; i < strlen(new); i++) + new_value[i*2] = new[i]; + + ret = call_password_interface(priv->wdev, buffer, buffer_size); + /* update current password so commands work after reset */ + if (!ret) + ret = set_current_password(password_type, new); + /* explain to user the detailed failure reason */ + else if (ret == -EOPNOTSUPP) + dev_err(&priv->wdev->dev, "admin password must be configured"); + else if (ret == -EACCES) + dev_err(&priv->wdev->dev, "invalid password"); + kfree(buffer); + +out_close_mutex: + mutex_unlock(&call_mutex); + + return ret; +} + +static int dell_wmi_bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) +{ + struct wmi_interface_priv *priv; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_interface_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &interface_list); + mutex_unlock(&list_mutex); + return 0; +} + +static int dell_wmi_bios_attr_pass_interface_remove(struct wmi_device *wdev) +{ + struct wmi_interface_priv *priv = dev_get_drvdata(&wdev->dev); + + mutex_lock(&call_mutex); + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + mutex_unlock(&call_mutex); + return 0; +} + +static const struct wmi_device_id dell_wmi_bios_attr_pass_interface_id_table[] = { + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, + { }, +}; +static struct wmi_driver dell_wmi_bios_attr_pass_interface_driver = { + .driver = { + .name = DRIVER_NAME"-password" + }, + .probe = dell_wmi_bios_attr_pass_interface_probe, + .remove = dell_wmi_bios_attr_pass_interface_remove, + .id_table = dell_wmi_bios_attr_pass_interface_id_table, +}; + +int init_dell_wmi_bios_attr_pass_interface(void) +{ + return wmi_driver_register(&dell_wmi_bios_attr_pass_interface_driver); +} + +void exit_dell_wmi_bios_attr_pass_interface(void) +{ + wmi_driver_unregister(&dell_wmi_bios_attr_pass_interface_driver); +} + +MODULE_DEVICE_TABLE(wmi, dell_wmi_bios_attr_pass_interface_id_table); diff --git a/drivers/platform/x86/dell-wmi-string-attributes.c b/drivers/platform/x86/dell-wmi-string-attributes.c new file mode 100644 index 000000000000..562d09055dd1 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-string-attributes.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions corresponding to string type attributes under BIOS String GUID for use with + * dell-wmi-sysman + * + * Copyright (c) 2020 Dell Inc. + */ + +#include "dell-wmi-sysman-attributes.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +enum string_properties {MIN_LEN = 6, MAX_LEN}; + +/* kept variable names same as in sysfs file name for sysfs_show macro definition */ +struct str_data { + char attribute_name[MAX_BUFF]; + char display_name_language_code[MAX_BUFF]; + char display_name[MAX_BUFF]; + char default_value[MAX_BUFF]; + char current_value[MAX_BUFF]; + char modifier[MAX_BUFF]; + int min_length; + int max_length; +}; + +static struct str_data *str_data; +static int str_instances_count; +get_instance_id(str); + +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_str_instance_id(kobj); + if (instance_id >= 0) { + union acpi_object *obj; + + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); + if (!obj) + return -AE_ERROR; + strncpy_attr(str_data[instance_id].current_value, + obj->package.elements[CURRENT_VAL].string.pointer); + kfree(obj); + return sprintf(buf, "%s\n", str_data[instance_id].current_value); + } + return -EIO; +} + +/** + * validate_str_input() - Validate input of current_value against min and max lengths + * @instance_id: The instance on which input is validated + * @buf: Input value + **/ +int validate_str_input(int instance_id, const char *buf) +{ + int in_len = strlen(buf); + + if ((in_len >= str_data[instance_id].min_length) && + (in_len <= str_data[instance_id].max_length)) + return 0; + + return -EINVAL; +} + +attribute_s_property_show(display_name_language_code, str); +static struct kobj_attribute str_displ_langcode = + __ATTR_RO(display_name_language_code); + +attribute_s_property_show(display_name, str); +struct kobj_attribute str_displ_name = + __ATTR_RO(display_name); + +attribute_s_property_show(default_value, str); +struct kobj_attribute str_default_val = + __ATTR_RO(default_value); + +attribute_property_store(current_value, str); +struct kobj_attribute str_current_val = + __ATTR_RW(current_value); + +attribute_s_property_show(modifier, str); +struct kobj_attribute str_modifier = + __ATTR_RO(modifier); + +attribute_n_property_show(min_length, str); +struct kobj_attribute str_min_length = + __ATTR_RO(min_length); + +attribute_n_property_show(max_length, str); +struct kobj_attribute str_max_length = + __ATTR_RO(max_length); + +static struct attribute *str_attrs[] = { + &str_displ_langcode.attr, + &str_displ_name.attr, + &str_default_val.attr, + &str_current_val.attr, + &str_modifier.attr, + &str_min_length.attr, + &str_max_length.attr, + NULL, +}; + +static const struct attribute_group str_attr_group = { + .attrs = str_attrs, +}; + +int alloc_str_data(void) +{ + int ret = 0; + + str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); + str_data = kzalloc((sizeof(struct str_data) * str_instances_count), GFP_KERNEL); + if (!str_data) + ret = -ENOMEM; + return ret; +} + +/** + * populate_enum_data() - Populate all properties of an instance under string attribute + * @str_obj: ACPI object with integer data + * @instance_id: The instance to enumerate + * @attr_name_kobj: The parent kernel object + **/ +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) +{ + int retval = sysfs_create_group(attr_name_kobj, &str_attr_group); + + if (retval) + goto out; + + mutex_lock(&call_mutex); + strncpy_attr(str_data[instance_id].attribute_name, str_obj[ATTR_NAME].string.pointer); + strncpy_attr(str_data[instance_id].display_name_language_code, + str_obj[DISPL_NAME_LANG_CODE].string.pointer); + strncpy_attr(str_data[instance_id].display_name, str_obj[DISPLAY_NAME].string.pointer); + strncpy_attr(str_data[instance_id].default_value, str_obj[DEFAULT_VAL].string.pointer); + strncpy_attr(str_data[instance_id].current_value, str_obj[CURRENT_VAL].string.pointer); + strncpy_attr(str_data[instance_id].modifier, str_obj[MODIFIER].string.pointer); + str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer; + str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; + mutex_unlock(&call_mutex); + +out: + return retval; +} + +/** + * exit_str_attributes() - Clear all attribute data + * @kset: The kset to free + * + * Clears all data allocated for this group of attributes + **/ +void exit_str_attributes(struct kset *kset) +{ + struct kobject *pos, *next; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pos, next, &kset->list, entry) { + sysfs_remove_group(pos, &str_attr_group); + } + mutex_unlock(&list_mutex); + mutex_lock(&call_mutex); + kfree(str_data); + mutex_unlock(&call_mutex); +} diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.c b/drivers/platform/x86/dell-wmi-sysman-attributes.c new file mode 100644 index 000000000000..485545ab6c8b --- /dev/null +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.c @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common methods for use with dell-wmi-sysman + * + * Copyright (c) 2020 Dell Inc. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dmi.h> +#include <linux/wmi.h> +#include "dell-wmi-sysman-attributes.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); + +#define MAX_TYPES 4 + +static struct platform_device *platform_device; + +static struct platform_driver platform_driver = { + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* attribute directory under platform dev */ +struct kset *main_dir_kset; +/* subtypes of attributes */ +struct kset *type_dir_kset[MAX_TYPES]; + +/* reset bios to defaults */ +static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; +static int reset_option = -1; + +/** + * populate_security_buffer() - builds a security buffer for authentication scheme + * @buffer: the buffer to populate + * @authentication: the authentication content + * + * Currently only supported type is PLAIN TEXT + **/ +void populate_security_buffer(char *buffer, char *authentication) +{ + u32 *sectype = (u32 *) buffer; + u32 *seclen = sectype + 1; + char *auth = buffer + sizeof(u32)*2; + /* plain text */ + *sectype = 1; + *seclen = strlen(authentication); + strncpy(auth, authentication, *seclen); +} + +/** + * map_wmi_error() - map errors from WMI methods to kernel error codes + **/ +int map_wmi_error(int error_code) +{ + switch (error_code) { + case 0: + /* success */ + return 0; + case 1: + /* failed */ + return -EIO; + case 2: + /* invalid parameter */ + return -EINVAL; + case 3: + /* access denied */ + return -EACCES; + case 4: + /* not supported */ + return -EOPNOTSUPP; + case 5: + /* memory error */ + return -ENOMEM; + case 6: + /* protocol error */ + return -EPROTO; + } + /* unspecified error */ + return -EIO; +} + +/** + * reset_bios_show() - sysfs implementaton for read reset_bios + * @kobj: Kernel object for this attribute + * @attr: Kernel object attribute + * @buf: The buffer to display to userspace + **/ +static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int i; + char *start = buf; + + for (i = 0; i < MAX_TYPES; i++) { + if (i == reset_option) + buf += sprintf(buf, "[%s] ", reset_types[i]); + else + buf += sprintf(buf, "%s ", reset_types[i]); + } + buf += sprintf(buf, "\n"); + return buf-start; +} + +/** + * reset_bios_store() - sysfs implementaton for write reset_bios + * @kobj: Kernel object for this attribute + * @attr: Kernel object attribute + * @buf: The buffer from userspace + * @count: the size of the buffer from userspace + **/ +static ssize_t reset_bios_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int len, ret, i; + int type = -1; + char *p; + + p = memchr(buf, '\n', count); + if (p != NULL) + *p = '\0'; + len = p ? p - buf : count; + + for (i = 0; i < MAX_TYPES; i++) { + if (len == strlen(reset_types[i]) + && !strncmp(buf, reset_types[i], len)) { + type = i; + break; + } + } + + if (type < 0 || type >= MAX_TYPES) { + ret = -EINVAL; + goto out; + } + + ret = set_bios_defaults(type); + dev_dbg(&platform_device->dev, "reset all attributes request type %d: %d", type, ret); + if (ret) { + ret = -EINVAL; + } else { + reset_option = type; + ret = count; + } + +out: + return ret; +} + +/** + * pending_reboot_show() - sysfs implementaton for read pending_reboot + * @kobj: Kernel object for this attribute + * @attr: Kernel object attribute + * @buf: The buffer to display to userspace + * + * Stores default value as 0 + * When current_value is changed this attribute is set to 1 to notify reboot may be required + **/ +static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", get_pending_changes()); +} + +static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); + + +/** + * create_reset_bios() - Creates reset_bios and pending_reboot attributes + **/ +static int create_reset_bios(void) +{ + int ret = sysfs_create_file(&main_dir_kset->kobj, &reset_bios.attr); + + if (ret) { + dev_dbg(&platform_device->dev, "could not create reset_bios file"); + return ret; + } + + ret = sysfs_create_file(&main_dir_kset->kobj, &pending_reboot.attr); + if (ret) { + dev_dbg(&platform_device->dev, "could not create changing_pending_reboot file"); + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); + } + return ret; +} + +static void release_reset_bios_data(void) +{ + sysfs_remove_file(&main_dir_kset->kobj, &reset_bios.attr); + sysfs_remove_file(&main_dir_kset->kobj, &pending_reboot.attr); +} + +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show) + ret = kattr->show(kobj, kattr, buf); + return ret; +} + +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store) + ret = kattr->store(kobj, kattr, buf, count); + return ret; +} + +const struct sysfs_ops kobj_sysfs_ops = { + .show = kobj_attr_show, + .store = kobj_attr_store, +}; + +static void attr_name_release(struct kobject *kobj) +{ + kfree(kobj); +} + +static struct kobj_type attr_name_ktype = { + .release = attr_name_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +/** + * strncpy_attr - Copy a length-limited, NULL-terminated string with bound checks + * @dest: Where to copy the string to + * @src: Where to copy the string from + **/ +void strncpy_attr(char *dest, char *src) +{ + size_t len = strlen(src) + 1; + + if (len > 1 && len < MAX_BUFF) + strncpy(dest, src, len); +} + +/** + * get_wmiobj_pointer() - Get Content of WMI block for particular instance + * @instance_id: WMI instance ID + * @guid_string: WMI GUID (in str form) + * + * Fetches the content for WMI block (instance_id) under GUID (guid_string) + * Caller must kfree the return + **/ +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) +{ + acpi_status status; + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_query_block(guid_string, instance_id, &out); + + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; +} + +/** + * get_instance_count() - Compute total number of instances under guid_string + * @guid_string: WMI GUID (in string form) + **/ +int get_instance_count(const char *guid_string) +{ + int i = 0; + union acpi_object *wmi_obj = NULL; + + do { + kfree(wmi_obj); + wmi_obj = get_wmiobj_pointer(i, guid_string); + i++; + } while (wmi_obj); + + return (i-1); +} + +/** + * alloc_attributes_data() - Allocate attributes data for a particular type + * @attr_type: Attribute type to allocate + **/ +static int alloc_attributes_data(int attr_type) +{ + int retval = 0; + char *type[MAX_TYPES] = {"enumeration", "integer", "string", "password"}; + + type_dir_kset[attr_type] = kset_create_and_add(type[attr_type], NULL, &main_dir_kset->kobj); + if (!type_dir_kset[attr_type]) + goto err_bios_attr; + + switch (attr_type) { + case ENUM: + retval = alloc_enum_data(); + break; + case INT: + retval = alloc_int_data(); + break; + case STR: + retval = alloc_str_data(); + break; + case PO: + retval = alloc_po_data(); + break; + default: + break; + } + + goto out; + +err_bios_attr: + kset_unregister(main_dir_kset); + +out: + return retval; +} + +/** + * destroy_attribute_objs() - Free a kset of kobjects + * @kset: The kset to destroy + * + * Fress kobjects created for each attribute_name under attribute type kset + **/ +static void destroy_attribute_objs(struct kset *kset) +{ + struct kobject *pos, *next; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pos, next, &kset->list, entry) { + kobject_put(pos); + } + mutex_unlock(&list_mutex); +} + +/** + * release_attributes_data() - Clean-up all sysfs directories and files created + **/ +static void release_attributes_data(void) +{ + release_reset_bios_data(); + + exit_enum_attributes(type_dir_kset[ENUM]); + exit_int_attributes(type_dir_kset[INT]); + exit_str_attributes(type_dir_kset[STR]); + exit_po_attributes(type_dir_kset[PO]); + + mutex_lock(&call_mutex); + if (main_dir_kset) { + int i; + + /* unregister all 4 types ENUM,INT,STR and PO */ + for (i = ENUM; i <= PO; i++) { + destroy_attribute_objs(type_dir_kset[i]); + kset_unregister(type_dir_kset[i]); + } + kset_unregister(main_dir_kset); + } + mutex_unlock(&call_mutex); +} + +/** + * init_bios_attributes() - Initialize all attributes for a type + * @attr_type: The attribute type to initialize + * @guid: The WMI GUID associated with this type to initialize + * + * Initialiaze all 4 types of attributes enumeration, integer, string and password object. + * Populates each attrbute typ's respective properties under sysfs files + **/ +static int init_bios_attributes(int attr_type, const char *guid) +{ + union acpi_object *obj = NULL; + union acpi_object *elements; + int retval = 0; + int instance_id = 0; + struct kobject *attr_name_kobj; //individual attribute names + + retval = alloc_attributes_data(attr_type); + if (retval) + return retval; + obj = get_wmiobj_pointer(instance_id, guid); + if (!obj) + return -ENODEV; + elements = obj->package.elements; + + while (elements) { + /* sanity checking */ + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { + dev_dbg(&platform_device->dev, "empty attribute found"); + goto nextobj; + } + if (kset_find_obj(type_dir_kset[attr_type], elements[ATTR_NAME].string.pointer)) { + dev_dbg(&platform_device->dev, "duplicate attribute name found - %s", + elements[ATTR_NAME].string.pointer); + goto nextobj; + } + + /* build attribute */ + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); + if (!attr_name_kobj) + goto err_attr_init; + + attr_name_kobj->kset = type_dir_kset[attr_type]; + + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", + elements[ATTR_NAME].string.pointer); + if (retval) { + kobject_put(attr_name_kobj); + goto err_attr_init; + } + + /* enumerate all of this attribute */ + switch (attr_type) { + case ENUM: + retval = populate_enum_data(elements, instance_id, attr_name_kobj); + break; + case INT: + retval = populate_int_data(elements, instance_id, attr_name_kobj); + break; + case STR: + retval = populate_str_data(elements, instance_id, attr_name_kobj); + break; + case PO: + retval = populate_po_data(elements, instance_id, attr_name_kobj); + break; + default: + break; + } + + if (retval) { + dev_dbg(&platform_device->dev, "failed to populate %s", + elements[ATTR_NAME].string.pointer); + goto err_attr_init; + } + +nextobj: + kfree(obj); + instance_id++; + obj = get_wmiobj_pointer(instance_id, guid); + elements = obj ? obj->package.elements : NULL; + } + + goto out; + +err_attr_init: + release_attributes_data(); + kfree(obj); +out: + return retval; +} + +static int __init init_dell_bios_attrib_wmi(void) +{ + int ret = 0; + + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { + pr_err("Unable to run on non-Dell system\n"); + return -ENODEV; + } + + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + + platform_device = platform_device_alloc(DRIVER_NAME, -1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device_alloc; + } + + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device_add; + + main_dir_kset = kset_create_and_add("attributes", NULL, &platform_device->dev.kobj); + if (!main_dir_kset) { + ret = -ENOMEM; + goto fail_platform_device_add; + } + + ret = init_dell_wmi_bios_attr_set_interface(); + if (ret) { + dev_dbg(&platform_device->dev, "failed to initialize set interface"); + goto fail_kset; + } + + ret = init_dell_wmi_bios_attr_pass_interface(); + if (ret) { + dev_dbg(&platform_device->dev, "failed to initialize pass interface"); + goto fail_set_interface; + } + + ret = create_reset_bios(); + if (ret) { + dev_dbg(&platform_device->dev, "could not create reset BIOS attribute"); + goto fail_pass_interface; + } + + ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); + if (ret) { + dev_dbg(&platform_device->dev, "failed to populate enumeration type attributes"); + goto fail_create_group; + } + + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); + if (ret) { + dev_dbg(&platform_device->dev, "failed to populate integer type attributes"); + goto fail_create_group; + } + + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); + if (ret) { + dev_dbg(&platform_device->dev, "failed to populate string type attributes"); + goto fail_create_group; + } + + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); + if (ret) { + dev_dbg(&platform_device->dev, "failed to populate pass object type attributes"); + goto fail_create_group; + } + + return 0; + +fail_create_group: + release_attributes_data(); + platform_device_del(platform_device); + +fail_pass_interface: + exit_dell_wmi_bios_attr_pass_interface(); + +fail_set_interface: + exit_dell_wmi_bios_attr_set_interface(); + +fail_kset: + kset_unregister(main_dir_kset); + +fail_platform_device_add: + platform_device_put(platform_device); + +fail_platform_device_alloc: + platform_driver_unregister(&platform_driver); + +fail_platform_driver: + return ret; +} + +static void __exit exit_dell_bios_attrib_wmi(void) +{ + release_attributes_data(); + mutex_lock(&call_mutex); + exit_dell_wmi_bios_attr_set_interface(); + exit_dell_wmi_bios_attr_pass_interface(); + if (platform_device) { + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } + mutex_unlock(&call_mutex); +} + +module_init(init_dell_bios_attrib_wmi); +module_exit(exit_dell_bios_attrib_wmi); + +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); +MODULE_DESCRIPTION("Dell platform setting control interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-wmi-sysman-attributes.h b/drivers/platform/x86/dell-wmi-sysman-attributes.h new file mode 100644 index 000000000000..bb5053b91ec7 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-sysman-attributes.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Definitions for kernel modules using Dell WMI System Management Driver + * + * Copyright (c) 2020 Dell Inc. + */ + +#ifndef _DELL_WMI_BIOS_ATTR_H_ +#define _DELL_WMI_BIOS_ATTR_H_ + +#include <linux/wmi.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/capability.h> + +#define DRIVER_NAME "dell-wmi-sysman" +#define MAX_BUFF 512 + +#define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF5" +#define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BFA" +#define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF9" +#define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719-97D7-6AD24108BFD4" +#define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF4" +#define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214-A1C6-1F884B1A892A" + +enum { ENUM, INT, STR, PO }; + +enum { + ATTR_NAME, + DISPL_NAME_LANG_CODE, + DISPLAY_NAME, + DEFAULT_VAL, + CURRENT_VAL, + MODIFIER +}; + +#define get_instance_id(type) \ +int get_##type##_instance_id(struct kobject *kobj) \ +{ \ + int i; \ + for (i = 0; i <= type##_instances_count; i++) { \ + if (!(strcmp(kobj->name, type##_data[i].attribute_name))) \ + return i; \ + } \ + return -EIO; \ +} + +#define attribute_s_property_show(name, type) \ +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ + char *buf) \ +{ \ + int i = get_##type##_instance_id(kobj); \ + if (i >= 0) \ + return sprintf(buf, "%s\n", type##_data[i].name); \ + return 0; \ +} + +#define attribute_n_property_show(name, type) \ +static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ + char *buf) \ +{ \ + int i = get_##type##_instance_id(kobj); \ + if (i >= 0) \ + return sprintf(buf, "%d\n", type##_data[i].name); \ + return 0; \ +} + +#define attribute_property_store(curr_val, type) \ +static ssize_t curr_val##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + char *p = memchr(buf, '\n', count); \ + int ret = -EIO; \ + int i; \ + \ + if (p != NULL) \ + *p = '\0'; \ + i = get_##type##_instance_id(kobj); \ + if (i >= 0) \ + ret = validate_##type##_input(i, buf); \ + if (!ret) \ + ret = set_attribute(kobj->name, buf); \ + return ret ? ret : count; \ +} + +union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); +int get_instance_count(const char *guid_string); +void strncpy_attr(char *dest, char *src); + +int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, + struct kobject *attr_name_kobj); +int alloc_enum_data(void); +void exit_enum_attributes(struct kset *kset); + +int populate_int_data(union acpi_object *integer_obj, int instance_id, + struct kobject *attr_name_kobj); +int alloc_int_data(void); +void exit_int_attributes(struct kset *kset); + +int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj); +int alloc_str_data(void); +void exit_str_attributes(struct kset *kset); + +int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj); +int alloc_po_data(void); +void exit_po_attributes(struct kset *kset); + +int set_attribute(const char *a_name, const char *a_value); +int set_bios_defaults(u8 defType); + +void exit_dell_wmi_bios_attr_set_interface(void); +int init_dell_wmi_bios_attr_set_interface(void); +bool get_pending_changes(void); +int map_wmi_error(int error_code); +void populate_security_buffer(char *buffer, char *authentication); + +char *get_current_password(const char *password_type); +int set_current_password(const char *password_type, const char *cur); +int set_new_password(const char *password_type, const char *new); +int init_dell_wmi_bios_attr_pass_interface(void); +void exit_dell_wmi_bios_attr_pass_interface(void); + +#endif