mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
platform/x86: Introduce support for Systems Management Driver over WMI for Dell Systems
The Dell WMI Systems Management Driver provides a sysfs interface for systems management to enable BIOS configuration capability on certain Dell Systems. This driver allows user to configure Dell systems with a uniform common interface. To facilitate this, the patch introduces a generic way for driver to be able to create configurable BIOS Attributes available in Setup (F2) screen. Cc: Hans de Goede <hdegoede@redhat.com> Cc: Andy Shevchenko <andy.shevchenko@gmail.com> Cc: mark gross <mgross@linux.intel.com> 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> Link: https://lore.kernel.org/r/20201027134944.316730-1-divya.bharathi@dell.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
committed by
Hans de Goede
parent
56afb8d480
commit
e8a60aa740
@@ -0,0 +1,224 @@
|
||||
What: /sys/class/firmware-attributes/*/attributes/*/
|
||||
Date: February 2021
|
||||
KernelVersion: 5.11
|
||||
Contact: Divya Bharathi <Divya.Bharathi@Dell.com>,
|
||||
Mario Limonciello <mario.limonciello@dell.com>,
|
||||
Prasanth KSR <prasanth.ksr@dell.com>
|
||||
Description:
|
||||
A sysfs interface for systems management software to enable
|
||||
configuration capability on supported systems. This directory
|
||||
exposes interfaces for interacting with configuration options.
|
||||
|
||||
Unless otherwise specified in an attribute description all attributes are optional
|
||||
and will accept UTF-8 input.
|
||||
|
||||
type: A file that can be read to obtain the type of attribute. This attribute is
|
||||
mandatory.
|
||||
|
||||
The following are known types:
|
||||
- enumeration: a set of pre-defined valid values
|
||||
- integer: a range of numerical values
|
||||
- string
|
||||
|
||||
All attribute types support the following 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>
|
||||
|
||||
This attribute is mandatory.
|
||||
|
||||
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 IETF language tag corresponding to the
|
||||
"display_name" of the <attr>
|
||||
|
||||
"enumeration"-type specific properties:
|
||||
|
||||
possible_values: A file that can be read to obtain the possible
|
||||
values of the <attr>. Values are separated using
|
||||
semi-colon (``;``).
|
||||
|
||||
"integer"-type specific properties:
|
||||
|
||||
min_value: A file that can be read to obtain the lower
|
||||
bound value of the <attr>
|
||||
|
||||
max_value: A file that can be read to obtain the upper
|
||||
bound value of the <attr>
|
||||
|
||||
scalar_increment: A file that can be read to obtain the scalar value used for
|
||||
increments of current_value this attribute accepts.
|
||||
|
||||
"string"-type specific properties:
|
||||
|
||||
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>
|
||||
|
||||
Dell specific class extensions
|
||||
--------------------------
|
||||
|
||||
On Dell systems the following additional attributes are available:
|
||||
|
||||
dell_modifier: A file that can be read to obtain attribute-level
|
||||
dependency rule. It says an attribute X will become read-only or
|
||||
suppressed, if/if-not attribute Y is configured.
|
||||
|
||||
modifier rules can be in following format:
|
||||
[ReadOnlyIf:<attribute>=<value>]
|
||||
[ReadOnlyIfNot:<attribute>=<value>]
|
||||
[SuppressIf:<attribute>=<value>]
|
||||
[SuppressIfNot:<attribute>=<value>]
|
||||
|
||||
For example:
|
||||
AutoOnFri/dell_modifier has value,
|
||||
[SuppressIfNot:AutoOn=SelectDays]
|
||||
|
||||
This means AutoOnFri will be suppressed in BIOS setup if AutoOn
|
||||
attribute is not "SelectDays" and its value will not be effective
|
||||
through sysfs until this rule is met.
|
||||
|
||||
Enumeration attributes also support the following:
|
||||
|
||||
dell_value_modifier: A file that can be read to obtain value-level dependency.
|
||||
This file is similar to dell_modifier but here, an
|
||||
attribute's current value will be forcefully changed based
|
||||
dependent attributes value.
|
||||
|
||||
dell_value_modifier rules can be in following format:
|
||||
<value>[ForceIf:<attribute>=<value>]
|
||||
<value>[ForceIfNot:<attribute>=<value>]
|
||||
|
||||
For example,
|
||||
LegacyOrom/dell_value_modifier has value:
|
||||
Disabled[ForceIf:SecureBoot=Enabled]
|
||||
This means LegacyOrom's current value will be forced to
|
||||
"Disabled" in BIOS setup if SecureBoot is Enabled and its
|
||||
value will not be effective through sysfs until this rule is
|
||||
met.
|
||||
|
||||
What: /sys/class/firmware-attributes/*/authentication/
|
||||
Date: February 2021
|
||||
KernelVersion: 5.11
|
||||
Contact: Divya Bharathi <Divya.Bharathi@Dell.com>,
|
||||
Mario Limonciello <mario.limonciello@dell.com>,
|
||||
Prasanth KSR <prasanth.ksr@dell.com>
|
||||
|
||||
Devices support various authentication mechanisms which can be exposed
|
||||
as a separate configuration object.
|
||||
|
||||
For example a "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.
|
||||
|
||||
Change in any of these two authentication methods will also generate an
|
||||
uevent KOBJ_CHANGE.
|
||||
|
||||
is_enabled: A file that can be read to obtain a 0/1 flag to see if
|
||||
<attr> authentication is enabled.
|
||||
This attribute is mandatory.
|
||||
|
||||
role: The type of authentication used.
|
||||
This attribute is mandatory.
|
||||
Known types:
|
||||
bios-admin: Representing BIOS administrator password
|
||||
power-on: Representing a password required to use
|
||||
the system
|
||||
|
||||
mechanism: The means of authentication. This attribute is mandatory.
|
||||
Only supported type currently is "password".
|
||||
|
||||
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
|
||||
|
||||
This attribute is mandatory when mechanism == "password".
|
||||
|
||||
new_password: A write only value that when used in tandem with
|
||||
current_password will reset a system or admin password.
|
||||
|
||||
Note, password management is session specific. If Admin password is set,
|
||||
same password must be written into current_password file (required for
|
||||
password-validation) and must be cleared once the session is over.
|
||||
For example:
|
||||
echo "password" > current_password
|
||||
echo "disabled" > TouchScreen/current_value
|
||||
echo "" > current_password
|
||||
|
||||
Drivers may emit a CHANGE uevent when a password is set or unset
|
||||
userspace may check it again.
|
||||
|
||||
On Dell systems, if Admin password is set, then all BIOS attributes
|
||||
require password validation.
|
||||
|
||||
What: /sys/class/firmware-attributes/*/attributes/pending_reboot
|
||||
Date: February 2021
|
||||
KernelVersion: 5.11
|
||||
Contact: Divya Bharathi <Divya.Bharathi@Dell.com>,
|
||||
Mario Limonciello <mario.limonciello@dell.com>,
|
||||
Prasanth KSR <prasanth.ksr@dell.com>
|
||||
Description:
|
||||
A read-only attribute reads 1 if a reboot is necessary to apply
|
||||
pending BIOS attribute changes. Also, an uevent_KOBJ_CHANGE is
|
||||
generated when it changes to 1.
|
||||
|
||||
0: All BIOS attributes setting are current
|
||||
1: A reboot is necessary to get pending BIOS attribute changes
|
||||
applied
|
||||
|
||||
Note, userspace applications need to follow below steps for efficient
|
||||
BIOS management,
|
||||
1. Check if admin password is set. If yes, follow session method for
|
||||
password management as briefed under authentication section above.
|
||||
2. Before setting any attribute, check if it has any modifiers
|
||||
or value_modifiers. If yes, incorporate them and then modify
|
||||
attribute.
|
||||
|
||||
Drivers may emit a CHANGE uevent when this value changes and userspace
|
||||
may check it again.
|
||||
|
||||
What: /sys/class/firmware-attributes/*/attributes/reset_bios
|
||||
Date: February 2021
|
||||
KernelVersion: 5.11
|
||||
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/class/firmware-attributes/*/device/attributes/reset_bios
|
||||
# cat /sys/class/firmware-attributes/*/device/attributes/reset_bios
|
||||
# builtinsafe lastknowngood [factory] custom
|
||||
|
||||
Note that any changes to this attribute requires a reboot
|
||||
for changes to take effect.
|
||||
@@ -4991,6 +4991,14 @@ 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-syman/*
|
||||
|
||||
DELL WMI NOTIFICATIONS DRIVER
|
||||
M: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
|
||||
@@ -430,6 +430,18 @@ 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-based Systems management driver"
|
||||
depends on ACPI_WMI
|
||||
depends on DMI
|
||||
select NLS
|
||||
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
|
||||
|
||||
@@ -47,6 +47,7 @@ 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/
|
||||
|
||||
# Fujitsu
|
||||
obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o
|
||||
dell-wmi-sysman-objs := sysman.o \
|
||||
enum-attributes.o \
|
||||
int-attributes.o \
|
||||
string-attributes.o \
|
||||
passobj-attributes.o \
|
||||
biosattr-interface.o \
|
||||
passwordattr-interface.o
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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.h"
|
||||
|
||||
#define SETDEFAULTVALUES_METHOD_ID 0x02
|
||||
#define SETBIOSDEFAULTS_METHOD_ID 0x03
|
||||
#define SETATTRIBUTE_METHOD_ID 0x04
|
||||
|
||||
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;
|
||||
|
||||
if (wmi_priv.pending_changes == 0) {
|
||||
wmi_priv.pending_changes = 1;
|
||||
/* let userland know it may need to check reboot pending again */
|
||||
kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE);
|
||||
}
|
||||
kfree(output.pointer);
|
||||
return map_wmi_error(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
size_t security_area_size, buffer_size;
|
||||
size_t a_name_size, a_value_size;
|
||||
char *buffer = NULL, *start;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
if (!wmi_priv.bios_attr_wdev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* build/calculate buffer */
|
||||
security_area_size = calculate_security_buffer(wmi_priv.current_admin_password);
|
||||
a_name_size = calculate_string_buffer(a_name);
|
||||
a_value_size = calculate_string_buffer(a_value);
|
||||
buffer_size = security_area_size + a_name_size + a_value_size;
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* build security area */
|
||||
populate_security_buffer(buffer, wmi_priv.current_admin_password);
|
||||
|
||||
/* build variables to set */
|
||||
start = buffer + security_area_size;
|
||||
ret = populate_string_buffer(start, a_name_size, a_name);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
start += ret;
|
||||
ret = populate_string_buffer(start, a_value_size, a_value);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
|
||||
ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev,
|
||||
buffer, buffer_size,
|
||||
SETATTRIBUTE_METHOD_ID);
|
||||
if (ret == -EOPNOTSUPP)
|
||||
dev_err(&wmi_priv.bios_attr_wdev->dev, "admin password must be configured\n");
|
||||
else if (ret == -EACCES)
|
||||
dev_err(&wmi_priv.bios_attr_wdev->dev, "invalid password\n");
|
||||
|
||||
out:
|
||||
kfree(buffer);
|
||||
mutex_unlock(&wmi_priv.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)
|
||||
{
|
||||
size_t security_area_size, buffer_size;
|
||||
size_t integer_area_size = sizeof(u8);
|
||||
char *buffer = NULL;
|
||||
u8 *defaultType;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
if (!wmi_priv.bios_attr_wdev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
security_area_size = calculate_security_buffer(wmi_priv.current_admin_password);
|
||||
buffer_size = security_area_size + integer_area_size;
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* build security area */
|
||||
populate_security_buffer(buffer, wmi_priv.current_admin_password);
|
||||
|
||||
defaultType = buffer + security_area_size;
|
||||
*defaultType = deftype;
|
||||
|
||||
ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, buffer, buffer_size,
|
||||
SETBIOSDEFAULTS_METHOD_ID);
|
||||
if (ret)
|
||||
dev_err(&wmi_priv.bios_attr_wdev->dev, "reset BIOS defaults failed: %d\n", ret);
|
||||
|
||||
kfree(buffer);
|
||||
out:
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.bios_attr_wdev = wdev;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bios_attr_set_interface_remove(struct wmi_device *wdev)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.bios_attr_wdev = NULL;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id bios_attr_set_interface_id_table[] = {
|
||||
{ .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID },
|
||||
{ },
|
||||
};
|
||||
static struct wmi_driver bios_attr_set_interface_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME
|
||||
},
|
||||
.probe = bios_attr_set_interface_probe,
|
||||
.remove = bios_attr_set_interface_remove,
|
||||
.id_table = bios_attr_set_interface_id_table,
|
||||
};
|
||||
|
||||
int init_bios_attr_set_interface(void)
|
||||
{
|
||||
return wmi_driver_register(&bios_attr_set_interface_driver);
|
||||
}
|
||||
|
||||
void exit_bios_attr_set_interface(void)
|
||||
{
|
||||
wmi_driver_unregister(&bios_attr_set_interface_driver);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(wmi, bios_attr_set_interface_id_table);
|
||||
@@ -0,0 +1,191 @@
|
||||
/* 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/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"
|
||||
|
||||
struct enumeration_data {
|
||||
struct kobject *attr_name_kobj;
|
||||
char display_name_language_code[MAX_BUFF];
|
||||
char dell_value_modifier[MAX_BUFF];
|
||||
char possible_values[MAX_BUFF];
|
||||
char attribute_name[MAX_BUFF];
|
||||
char default_value[MAX_BUFF];
|
||||
char dell_modifier[MAX_BUFF];
|
||||
char display_name[MAX_BUFF];
|
||||
};
|
||||
|
||||
struct integer_data {
|
||||
struct kobject *attr_name_kobj;
|
||||
char display_name_language_code[MAX_BUFF];
|
||||
char attribute_name[MAX_BUFF];
|
||||
char dell_modifier[MAX_BUFF];
|
||||
char display_name[MAX_BUFF];
|
||||
int scalar_increment;
|
||||
int default_value;
|
||||
int min_value;
|
||||
int max_value;
|
||||
};
|
||||
|
||||
struct str_data {
|
||||
struct kobject *attr_name_kobj;
|
||||
char display_name_language_code[MAX_BUFF];
|
||||
char attribute_name[MAX_BUFF];
|
||||
char display_name[MAX_BUFF];
|
||||
char default_value[MAX_BUFF];
|
||||
char dell_modifier[MAX_BUFF];
|
||||
int min_length;
|
||||
int max_length;
|
||||
};
|
||||
|
||||
struct po_data {
|
||||
struct kobject *attr_name_kobj;
|
||||
char attribute_name[MAX_BUFF];
|
||||
int min_password_length;
|
||||
int max_password_length;
|
||||
};
|
||||
|
||||
struct wmi_sysman_priv {
|
||||
char current_admin_password[MAX_BUFF];
|
||||
char current_system_password[MAX_BUFF];
|
||||
struct wmi_device *password_attr_wdev;
|
||||
struct wmi_device *bios_attr_wdev;
|
||||
struct kset *authentication_dir_kset;
|
||||
struct kset *main_dir_kset;
|
||||
struct device *class_dev;
|
||||
struct enumeration_data *enumeration_data;
|
||||
int enumeration_instances_count;
|
||||
struct integer_data *integer_data;
|
||||
int integer_instances_count;
|
||||
struct str_data *str_data;
|
||||
int str_instances_count;
|
||||
struct po_data *po_data;
|
||||
int po_instances_count;
|
||||
bool pending_changes;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
/* global structure used by multiple WMI interfaces */
|
||||
extern struct wmi_sysman_priv wmi_priv;
|
||||
|
||||
enum { ENUM, INT, STR, PO };
|
||||
|
||||
enum {
|
||||
ATTR_NAME,
|
||||
DISPL_NAME_LANG_CODE,
|
||||
DISPLAY_NAME,
|
||||
DEFAULT_VAL,
|
||||
CURRENT_VAL,
|
||||
MODIFIER
|
||||
};
|
||||
|
||||
#define get_instance_id(type) \
|
||||
static int get_##type##_instance_id(struct kobject *kobj) \
|
||||
{ \
|
||||
int i; \
|
||||
for (i = 0; i <= wmi_priv.type##_instances_count; i++) { \
|
||||
if (!(strcmp(kobj->name, wmi_priv.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", wmi_priv.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", wmi_priv.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, *buf_cp; \
|
||||
int i, ret = -EIO; \
|
||||
buf_cp = kstrdup(buf, GFP_KERNEL); \
|
||||
if (!buf_cp) \
|
||||
return -ENOMEM; \
|
||||
p = memchr(buf_cp, '\n', count); \
|
||||
\
|
||||
if (p != NULL) \
|
||||
*p = '\0'; \
|
||||
i = get_##type##_instance_id(kobj); \
|
||||
if (i >= 0) \
|
||||
ret = validate_##type##_input(i, buf_cp); \
|
||||
if (!ret) \
|
||||
ret = set_attribute(kobj->name, buf_cp); \
|
||||
kfree(buf_cp); \
|
||||
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 strlcpy_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(void);
|
||||
|
||||
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(void);
|
||||
|
||||
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(void);
|
||||
|
||||
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(void);
|
||||
|
||||
int set_attribute(const char *a_name, const char *a_value);
|
||||
int set_bios_defaults(u8 defType);
|
||||
|
||||
void exit_bios_attr_set_interface(void);
|
||||
int init_bios_attr_set_interface(void);
|
||||
int map_wmi_error(int error_code);
|
||||
size_t calculate_string_buffer(const char *str);
|
||||
size_t calculate_security_buffer(char *authentication);
|
||||
void populate_security_buffer(char *buffer, char *authentication);
|
||||
ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str);
|
||||
int set_new_password(const char *password_type, const char *new);
|
||||
int init_bios_attr_pass_interface(void);
|
||||
void exit_bios_attr_pass_interface(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
// 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.h"
|
||||
|
||||
get_instance_id(enumeration);
|
||||
|
||||
static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int instance_id = get_enumeration_instance_id(kobj);
|
||||
union acpi_object *obj;
|
||||
ssize_t ret;
|
||||
|
||||
if (instance_id < 0)
|
||||
return instance_id;
|
||||
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
|
||||
if (!obj)
|
||||
return -EIO;
|
||||
if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_STRING) {
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(buf, PAGE_SIZE, "%s\n", obj->package.elements[CURRENT_VAL].string.pointer);
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate_enumeration_input() - Validate input of current_value against possible values
|
||||
* @instance_id: The instance on which input is validated
|
||||
* @buf: Input value
|
||||
*/
|
||||
static int validate_enumeration_input(int instance_id, const char *buf)
|
||||
{
|
||||
char *options, *tmp, *p;
|
||||
int ret = -EINVAL;
|
||||
|
||||
options = tmp = kstrdup(wmi_priv.enumeration_data[instance_id].possible_values,
|
||||
GFP_KERNEL);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
|
||||
while ((p = strsep(&options, ";")) != NULL) {
|
||||
if (!*p)
|
||||
continue;
|
||||
if (!strcasecmp(p, buf)) {
|
||||
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);
|
||||
static struct kobj_attribute displ_name =
|
||||
__ATTR_RO(display_name);
|
||||
|
||||
attribute_s_property_show(default_value, enumeration);
|
||||
static struct kobj_attribute default_val =
|
||||
__ATTR_RO(default_value);
|
||||
|
||||
attribute_property_store(current_value, enumeration);
|
||||
static struct kobj_attribute current_val =
|
||||
__ATTR_RW_MODE(current_value, 0600);
|
||||
|
||||
attribute_s_property_show(dell_modifier, enumeration);
|
||||
static struct kobj_attribute modifier =
|
||||
__ATTR_RO(dell_modifier);
|
||||
|
||||
attribute_s_property_show(dell_value_modifier, enumeration);
|
||||
static struct kobj_attribute value_modfr =
|
||||
__ATTR_RO(dell_value_modifier);
|
||||
|
||||
attribute_s_property_show(possible_values, enumeration);
|
||||
static struct kobj_attribute poss_val =
|
||||
__ATTR_RO(possible_values);
|
||||
|
||||
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "enumeration\n");
|
||||
}
|
||||
static struct kobj_attribute type =
|
||||
__ATTR_RO(type);
|
||||
|
||||
static struct attribute *enumeration_attrs[] = {
|
||||
&displ_langcode.attr,
|
||||
&displ_name.attr,
|
||||
&default_val.attr,
|
||||
¤t_val.attr,
|
||||
&modifier.attr,
|
||||
&value_modfr.attr,
|
||||
&poss_val.attr,
|
||||
&type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group enumeration_attr_group = {
|
||||
.attrs = enumeration_attrs,
|
||||
};
|
||||
|
||||
int alloc_enum_data(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
wmi_priv.enumeration_instances_count =
|
||||
get_instance_count(DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
|
||||
wmi_priv.enumeration_data = kcalloc(wmi_priv.enumeration_instances_count,
|
||||
sizeof(struct enumeration_data), GFP_KERNEL);
|
||||
if (!wmi_priv.enumeration_data) {
|
||||
wmi_priv.enumeration_instances_count = 0;
|
||||
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, value_modifier_count, possible_values_count;
|
||||
|
||||
wmi_priv.enumeration_data[instance_id].attr_name_kobj = attr_name_kobj;
|
||||
strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name,
|
||||
enumeration_obj[ATTR_NAME].string.pointer);
|
||||
strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code,
|
||||
enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);
|
||||
strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name,
|
||||
enumeration_obj[DISPLAY_NAME].string.pointer);
|
||||
strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value,
|
||||
enumeration_obj[DEFAULT_VAL].string.pointer);
|
||||
strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier,
|
||||
enumeration_obj[MODIFIER].string.pointer);
|
||||
|
||||
next_obj = MODIFIER + 1;
|
||||
|
||||
value_modifier_count = (uintptr_t)enumeration_obj[next_obj].string.pointer;
|
||||
|
||||
for (i = 0; i < value_modifier_count; i++) {
|
||||
strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier,
|
||||
enumeration_obj[++next_obj].string.pointer);
|
||||
strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";");
|
||||
}
|
||||
|
||||
possible_values_count = (uintptr_t) enumeration_obj[++next_obj].string.pointer;
|
||||
|
||||
for (i = 0; i < possible_values_count; i++) {
|
||||
strcat(wmi_priv.enumeration_data[instance_id].possible_values,
|
||||
enumeration_obj[++next_obj].string.pointer);
|
||||
strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";");
|
||||
}
|
||||
|
||||
return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_enum_attributes() - Clear all attribute data
|
||||
*
|
||||
* Clears all data allocated for this group of attributes
|
||||
*/
|
||||
void exit_enum_attributes(void)
|
||||
{
|
||||
int instance_id;
|
||||
|
||||
for (instance_id = 0; instance_id < wmi_priv.enumeration_instances_count; instance_id++) {
|
||||
if (wmi_priv.enumeration_data[instance_id].attr_name_kobj)
|
||||
sysfs_remove_group(wmi_priv.enumeration_data[instance_id].attr_name_kobj,
|
||||
&enumeration_attr_group);
|
||||
}
|
||||
kfree(wmi_priv.enumeration_data);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// 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.h"
|
||||
|
||||
enum int_properties {MIN_VALUE = 6, MAX_VALUE, SCALAR_INCR};
|
||||
|
||||
get_instance_id(integer);
|
||||
|
||||
static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int instance_id = get_integer_instance_id(kobj);
|
||||
union acpi_object *obj;
|
||||
ssize_t ret;
|
||||
|
||||
if (instance_id < 0)
|
||||
return instance_id;
|
||||
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID);
|
||||
if (!obj)
|
||||
return -EIO;
|
||||
if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_INTEGER) {
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[CURRENT_VAL].integer.value);
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static int validate_integer_input(int instance_id, const char *buf)
|
||||
{
|
||||
int in_val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &in_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (in_val < wmi_priv.integer_data[instance_id].min_value ||
|
||||
in_val > wmi_priv.integer_data[instance_id].max_value)
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
static struct kobj_attribute integer_displ_name =
|
||||
__ATTR_RO(display_name);
|
||||
|
||||
attribute_n_property_show(default_value, integer);
|
||||
static struct kobj_attribute integer_default_val =
|
||||
__ATTR_RO(default_value);
|
||||
|
||||
attribute_property_store(current_value, integer);
|
||||
static struct kobj_attribute integer_current_val =
|
||||
__ATTR_RW_MODE(current_value, 0600);
|
||||
|
||||
attribute_s_property_show(dell_modifier, integer);
|
||||
static struct kobj_attribute integer_modifier =
|
||||
__ATTR_RO(dell_modifier);
|
||||
|
||||
attribute_n_property_show(min_value, integer);
|
||||
static struct kobj_attribute integer_lower_bound =
|
||||
__ATTR_RO(min_value);
|
||||
|
||||
attribute_n_property_show(max_value, integer);
|
||||
static struct kobj_attribute integer_upper_bound =
|
||||
__ATTR_RO(max_value);
|
||||
|
||||
attribute_n_property_show(scalar_increment, integer);
|
||||
static struct kobj_attribute integer_scalar_increment =
|
||||
__ATTR_RO(scalar_increment);
|
||||
|
||||
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "integer\n");
|
||||
}
|
||||
static struct kobj_attribute integer_type =
|
||||
__ATTR_RO(type);
|
||||
|
||||
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,
|
||||
&integer_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group integer_attr_group = {
|
||||
.attrs = integer_attrs,
|
||||
};
|
||||
|
||||
int alloc_int_data(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
wmi_priv.integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID);
|
||||
wmi_priv.integer_data = kcalloc(wmi_priv.integer_instances_count,
|
||||
sizeof(struct integer_data), GFP_KERNEL);
|
||||
if (!wmi_priv.integer_data) {
|
||||
wmi_priv.integer_instances_count = 0;
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* populate_int_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)
|
||||
{
|
||||
wmi_priv.integer_data[instance_id].attr_name_kobj = attr_name_kobj;
|
||||
strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name,
|
||||
integer_obj[ATTR_NAME].string.pointer);
|
||||
strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code,
|
||||
integer_obj[DISPL_NAME_LANG_CODE].string.pointer);
|
||||
strlcpy_attr(wmi_priv.integer_data[instance_id].display_name,
|
||||
integer_obj[DISPLAY_NAME].string.pointer);
|
||||
wmi_priv.integer_data[instance_id].default_value =
|
||||
(uintptr_t)integer_obj[DEFAULT_VAL].string.pointer;
|
||||
strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier,
|
||||
integer_obj[MODIFIER].string.pointer);
|
||||
wmi_priv.integer_data[instance_id].min_value =
|
||||
(uintptr_t)integer_obj[MIN_VALUE].string.pointer;
|
||||
wmi_priv.integer_data[instance_id].max_value =
|
||||
(uintptr_t)integer_obj[MAX_VALUE].string.pointer;
|
||||
wmi_priv.integer_data[instance_id].scalar_increment =
|
||||
(uintptr_t)integer_obj[SCALAR_INCR].string.pointer;
|
||||
|
||||
return sysfs_create_group(attr_name_kobj, &integer_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_int_attributes() - Clear all attribute data
|
||||
*
|
||||
* Clears all data allocated for this group of attributes
|
||||
*/
|
||||
void exit_int_attributes(void)
|
||||
{
|
||||
int instance_id;
|
||||
|
||||
for (instance_id = 0; instance_id < wmi_priv.integer_instances_count; instance_id++) {
|
||||
if (wmi_priv.integer_data[instance_id].attr_name_kobj)
|
||||
sysfs_remove_group(wmi_priv.integer_data[instance_id].attr_name_kobj,
|
||||
&integer_attr_group);
|
||||
}
|
||||
kfree(wmi_priv.integer_data);
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// 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.h"
|
||||
|
||||
enum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN};
|
||||
|
||||
get_instance_id(po);
|
||||
|
||||
static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int instance_id = get_po_instance_id(kobj);
|
||||
union acpi_object *obj;
|
||||
ssize_t ret;
|
||||
|
||||
if (instance_id < 0)
|
||||
return instance_id;
|
||||
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
|
||||
if (!obj)
|
||||
return -EIO;
|
||||
if (obj->package.elements[IS_PASS_SET].type != ACPI_TYPE_INTEGER) {
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[IS_PASS_SET].integer.value);
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct kobj_attribute po_is_pass_set =
|
||||
__ATTR_RO(is_enabled);
|
||||
|
||||
static ssize_t current_password_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char *target = NULL;
|
||||
int length;
|
||||
|
||||
length = strlen(buf);
|
||||
if (buf[length-1] == '\n')
|
||||
length--;
|
||||
|
||||
/* firmware does verifiation of min/max password length,
|
||||
* hence only check for not exceeding MAX_BUFF here.
|
||||
*/
|
||||
if (length >= MAX_BUFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(kobj->name, "Admin") == 0)
|
||||
target = wmi_priv.current_admin_password;
|
||||
else if (strcmp(kobj->name, "System") == 0)
|
||||
target = wmi_priv.current_system_password;
|
||||
if (!target)
|
||||
return -EIO;
|
||||
memcpy(target, buf, length);
|
||||
target[length] = '\0';
|
||||
|
||||
return 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, *buf_cp;
|
||||
int ret;
|
||||
|
||||
buf_cp = kstrdup(buf, GFP_KERNEL);
|
||||
if (!buf_cp)
|
||||
return -ENOMEM;
|
||||
p = memchr(buf_cp, '\n', count);
|
||||
|
||||
if (p != NULL)
|
||||
*p = '\0';
|
||||
if (strlen(buf_cp) > MAX_BUFF) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = set_new_password(kobj->name, buf_cp);
|
||||
|
||||
out:
|
||||
kfree(buf_cp);
|
||||
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 ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "password\n");
|
||||
}
|
||||
|
||||
struct kobj_attribute po_mechanism =
|
||||
__ATTR_RO(mechanism);
|
||||
|
||||
static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
if (strcmp(kobj->name, "Admin") == 0)
|
||||
return sprintf(buf, "bios-admin\n");
|
||||
else if (strcmp(kobj->name, "System") == 0)
|
||||
return sprintf(buf, "power-on\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
struct kobj_attribute po_role =
|
||||
__ATTR_RO(role);
|
||||
|
||||
static struct attribute *po_attrs[] = {
|
||||
&po_is_pass_set.attr,
|
||||
&po_min_pass_length.attr,
|
||||
&po_max_pass_length.attr,
|
||||
&po_current_password.attr,
|
||||
&po_new_password.attr,
|
||||
&po_role.attr,
|
||||
&po_mechanism.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group po_attr_group = {
|
||||
.attrs = po_attrs,
|
||||
};
|
||||
|
||||
int alloc_po_data(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
wmi_priv.po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
|
||||
wmi_priv.po_data = kcalloc(wmi_priv.po_instances_count, sizeof(struct po_data), GFP_KERNEL);
|
||||
if (!wmi_priv.po_data) {
|
||||
wmi_priv.po_instances_count = 0;
|
||||
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)
|
||||
{
|
||||
wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj;
|
||||
strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,
|
||||
po_obj[ATTR_NAME].string.pointer);
|
||||
wmi_priv.po_data[instance_id].min_password_length =
|
||||
(uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
|
||||
wmi_priv.po_data[instance_id].max_password_length =
|
||||
(uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
|
||||
|
||||
return sysfs_create_group(attr_name_kobj, &po_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_po_attributes() - Clear all attribute data
|
||||
*
|
||||
* Clears all data allocated for this group of attributes
|
||||
*/
|
||||
void exit_po_attributes(void)
|
||||
{
|
||||
int instance_id;
|
||||
|
||||
for (instance_id = 0; instance_id < wmi_priv.po_instances_count; instance_id++) {
|
||||
if (wmi_priv.po_data[instance_id].attr_name_kobj)
|
||||
sysfs_remove_group(wmi_priv.po_data[instance_id].attr_name_kobj,
|
||||
&po_attr_group);
|
||||
}
|
||||
kfree(wmi_priv.po_data);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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.h"
|
||||
|
||||
static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size)
|
||||
{
|
||||
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct acpi_buffer input;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int ret = -EIO;
|
||||
|
||||
input.length = (acpi_size) size;
|
||||
input.pointer = in_args;
|
||||
status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj->type == ACPI_TYPE_INTEGER)
|
||||
ret = obj->integer.value;
|
||||
|
||||
kfree(output.pointer);
|
||||
/* let userland know it may need to check is_password_set again */
|
||||
kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE);
|
||||
return map_wmi_error(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
size_t password_type_size, current_password_size, new_size;
|
||||
size_t security_area_size, buffer_size;
|
||||
char *buffer = NULL, *start;
|
||||
char *current_password;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
if (!wmi_priv.password_attr_wdev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(password_type, "Admin") == 0) {
|
||||
current_password = wmi_priv.current_admin_password;
|
||||
} else if (strcmp(password_type, "System") == 0) {
|
||||
current_password = wmi_priv.current_system_password;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
dev_err(&wmi_priv.password_attr_wdev->dev, "unknown password type %s\n",
|
||||
password_type);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* build/calculate buffer */
|
||||
security_area_size = calculate_security_buffer(wmi_priv.current_admin_password);
|
||||
password_type_size = calculate_string_buffer(password_type);
|
||||
current_password_size = calculate_string_buffer(current_password);
|
||||
new_size = calculate_string_buffer(new);
|
||||
buffer_size = security_area_size + password_type_size + current_password_size + new_size;
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* build security area */
|
||||
populate_security_buffer(buffer, wmi_priv.current_admin_password);
|
||||
|
||||
/* build variables to set */
|
||||
start = buffer + security_area_size;
|
||||
ret = populate_string_buffer(start, password_type_size, password_type);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
start += ret;
|
||||
ret = populate_string_buffer(start, current_password_size, current_password);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
start += ret;
|
||||
ret = populate_string_buffer(start, new_size, new);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
|
||||
ret = call_password_interface(wmi_priv.password_attr_wdev, buffer, buffer_size);
|
||||
/* clear current_password here and use user input from wmi_priv.current_password */
|
||||
if (!ret)
|
||||
memset(current_password, 0, MAX_BUFF);
|
||||
/* explain to user the detailed failure reason */
|
||||
else if (ret == -EOPNOTSUPP)
|
||||
dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n");
|
||||
else if (ret == -EACCES)
|
||||
dev_err(&wmi_priv.password_attr_wdev->dev, "invalid password\n");
|
||||
|
||||
out:
|
||||
kfree(buffer);
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.password_attr_wdev = wdev;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bios_attr_pass_interface_remove(struct wmi_device *wdev)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.password_attr_wdev = NULL;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id bios_attr_pass_interface_id_table[] = {
|
||||
{ .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID },
|
||||
{ },
|
||||
};
|
||||
static struct wmi_driver bios_attr_pass_interface_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME"-password"
|
||||
},
|
||||
.probe = bios_attr_pass_interface_probe,
|
||||
.remove = bios_attr_pass_interface_remove,
|
||||
.id_table = bios_attr_pass_interface_id_table,
|
||||
};
|
||||
|
||||
int init_bios_attr_pass_interface(void)
|
||||
{
|
||||
return wmi_driver_register(&bios_attr_pass_interface_driver);
|
||||
}
|
||||
|
||||
void exit_bios_attr_pass_interface(void)
|
||||
{
|
||||
wmi_driver_unregister(&bios_attr_pass_interface_driver);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(wmi, bios_attr_pass_interface_id_table);
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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.h"
|
||||
|
||||
enum string_properties {MIN_LEN = 6, MAX_LEN};
|
||||
|
||||
get_instance_id(str);
|
||||
|
||||
static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int instance_id = get_str_instance_id(kobj);
|
||||
union acpi_object *obj;
|
||||
ssize_t ret;
|
||||
|
||||
if (instance_id < 0)
|
||||
return -EIO;
|
||||
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID);
|
||||
if (!obj)
|
||||
return -EIO;
|
||||
if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_STRING) {
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(buf, PAGE_SIZE, "%s\n", obj->package.elements[CURRENT_VAL].string.pointer);
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static int validate_str_input(int instance_id, const char *buf)
|
||||
{
|
||||
int in_len = strlen(buf);
|
||||
|
||||
if ((in_len < wmi_priv.str_data[instance_id].min_length) ||
|
||||
(in_len > wmi_priv.str_data[instance_id].max_length))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
static struct kobj_attribute str_displ_name =
|
||||
__ATTR_RO(display_name);
|
||||
|
||||
attribute_s_property_show(default_value, str);
|
||||
static struct kobj_attribute str_default_val =
|
||||
__ATTR_RO(default_value);
|
||||
|
||||
attribute_property_store(current_value, str);
|
||||
static struct kobj_attribute str_current_val =
|
||||
__ATTR_RW_MODE(current_value, 0600);
|
||||
|
||||
attribute_s_property_show(dell_modifier, str);
|
||||
static struct kobj_attribute str_modifier =
|
||||
__ATTR_RO(dell_modifier);
|
||||
|
||||
attribute_n_property_show(min_length, str);
|
||||
static struct kobj_attribute str_min_length =
|
||||
__ATTR_RO(min_length);
|
||||
|
||||
attribute_n_property_show(max_length, str);
|
||||
static struct kobj_attribute str_max_length =
|
||||
__ATTR_RO(max_length);
|
||||
|
||||
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "string\n");
|
||||
}
|
||||
static struct kobj_attribute str_type =
|
||||
__ATTR_RO(type);
|
||||
|
||||
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,
|
||||
&str_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group str_attr_group = {
|
||||
.attrs = str_attrs,
|
||||
};
|
||||
|
||||
int alloc_str_data(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
wmi_priv.str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID);
|
||||
wmi_priv.str_data = kcalloc(wmi_priv.str_instances_count,
|
||||
sizeof(struct str_data), GFP_KERNEL);
|
||||
if (!wmi_priv.str_data) {
|
||||
wmi_priv.str_instances_count = 0;
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* populate_str_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)
|
||||
{
|
||||
wmi_priv.str_data[instance_id].attr_name_kobj = attr_name_kobj;
|
||||
strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name,
|
||||
str_obj[ATTR_NAME].string.pointer);
|
||||
strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code,
|
||||
str_obj[DISPL_NAME_LANG_CODE].string.pointer);
|
||||
strlcpy_attr(wmi_priv.str_data[instance_id].display_name,
|
||||
str_obj[DISPLAY_NAME].string.pointer);
|
||||
strlcpy_attr(wmi_priv.str_data[instance_id].default_value,
|
||||
str_obj[DEFAULT_VAL].string.pointer);
|
||||
strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier,
|
||||
str_obj[MODIFIER].string.pointer);
|
||||
wmi_priv.str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer;
|
||||
wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer;
|
||||
|
||||
return sysfs_create_group(attr_name_kobj, &str_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_str_attributes() - Clear all attribute data
|
||||
*
|
||||
* Clears all data allocated for this group of attributes
|
||||
*/
|
||||
void exit_str_attributes(void)
|
||||
{
|
||||
int instance_id;
|
||||
|
||||
for (instance_id = 0; instance_id < wmi_priv.str_instances_count; instance_id++) {
|
||||
if (wmi_priv.str_data[instance_id].attr_name_kobj)
|
||||
sysfs_remove_group(wmi_priv.str_data[instance_id].attr_name_kobj,
|
||||
&str_attr_group);
|
||||
}
|
||||
kfree(wmi_priv.str_data);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user