Re: [PATCH] Introduce support for Systems Management Driver over WMI for Dell Systems

From: Andy Shevchenko
Date: Mon Aug 10 2020 - 06:31:32 EST


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@xxxxxxxxxxxxxxx <platform-driver-x86-
> > owner@xxxxxxxxxxxxxxx> On Behalf Of Divya Bharathi
> > Sent: Thursday, July 30, 2020 9:31 AM
> > To: dvhart@xxxxxxxxxxxxx
> > Cc: LKML; platform-driver-x86@xxxxxxxxxxxxxxx; 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@xxxxxxxx>
> >
> > 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@xxxxxxxx>
> > Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxxx>
> > Co-developed-by: Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > Signed-off-by: Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > Signed-off-by: Divya Bharathi <divya.bharathi@xxxxxxxx>
> >
> > ---
> > 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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>,
> > + Mario Limonciello <mario.limonciello@xxxxxxxx>,
> > + Prasanth KSR <prasanth.ksr@xxxxxxxx>
> > +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@xxxxxxxx>
> > S: Maintained
> > F: drivers/platform/x86/dell-wmi-descriptor.c
> >
> > +DELL WMI SYSMAN DRIVER
> > +M: Divya Bharathi <divya.bharathi@xxxxxxxx>
> > +M: Mario Limonciello <mario.limonciello@xxxxxxxx>
> > +M: Prasanth Ksr <prasanth.ksr@xxxxxxxx>
> > +L: platform-driver-x86@xxxxxxxxxxxxxxx
> > +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@xxxxxxxxxxxxx>
> > M: Pali Rohár <pali@xxxxxxxxxx>
> > 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,
> > + &current_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@xxxxxxxx>");
> > +MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@xxxxxxxx>");
> > +MODULE_AUTHOR("Divya Bharathi <divya.bharathi@xxxxxxxx>");
> > +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
>

--
With Best Regards,
Andy Shevchenko