mirror of https://gitee.com/openkylin/linux.git
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:
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);
|
||||
}
|
|
@ -0,0 +1,625 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Common methods for use with dell-wmi-sysman
|
||||
*
|
||||
* Copyright (c) 2020 Dell Inc.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wmi.h>
|
||||
#include "dell-wmi-sysman.h"
|
||||
|
||||
#define MAX_TYPES 4
|
||||
#include <linux/nls.h>
|
||||
|
||||
static struct class firmware_attributes_class = {
|
||||
.name = "firmware-attributes",
|
||||
};
|
||||
|
||||
struct wmi_sysman_priv wmi_priv = {
|
||||
.mutex = __MUTEX_INITIALIZER(wmi_priv.mutex),
|
||||
};
|
||||
|
||||
/* reset bios to defaults */
|
||||
static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
|
||||
static int reset_option = -1;
|
||||
|
||||
|
||||
/**
|
||||
* populate_string_buffer() - populates a string buffer
|
||||
* @buffer: the start of the destination buffer
|
||||
* @buffer_len: length of the destination buffer
|
||||
* @str: the string to insert into buffer
|
||||
*/
|
||||
ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str)
|
||||
{
|
||||
u16 *length = (u16 *)buffer;
|
||||
u16 *target = length + 1;
|
||||
int ret;
|
||||
|
||||
ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN,
|
||||
target, buffer_len - sizeof(u16));
|
||||
if (ret < 0) {
|
||||
dev_err(wmi_priv.class_dev, "UTF16 conversion failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret * sizeof(u16)) > U16_MAX) {
|
||||
dev_err(wmi_priv.class_dev, "Error string too long\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
*length = ret * sizeof(u16);
|
||||
return sizeof(u16) + *length;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate_string_buffer() - determines size of string buffer for use with BIOS communication
|
||||
* @str: the string to calculate based upon
|
||||
*
|
||||
*/
|
||||
size_t calculate_string_buffer(const char *str)
|
||||
{
|
||||
/* u16 length field + one UTF16 char for each input char */
|
||||
return sizeof(u16) + strlen(str) * sizeof(u16);
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate_security_buffer() - determines size of security buffer for authentication scheme
|
||||
* @authentication: the authentication content
|
||||
*
|
||||
* Currently only supported type is Admin password
|
||||
*/
|
||||
size_t calculate_security_buffer(char *authentication)
|
||||
{
|
||||
if (strlen(authentication) > 0) {
|
||||
return (sizeof(u32) * 2) + strlen(authentication) +
|
||||
strlen(authentication) % 2;
|
||||
}
|
||||
return sizeof(u32) * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
char *auth = buffer + sizeof(u32) * 2;
|
||||
u32 *sectype = (u32 *) buffer;
|
||||
u32 *seclen = sectype + 1;
|
||||
|
||||
*sectype = strlen(authentication) > 0 ? 1 : 0;
|
||||
*seclen = strlen(authentication);
|
||||
|
||||
/* plain text */
|
||||
if (strlen(authentication) > 0)
|
||||
memcpy(auth, authentication, *seclen);
|
||||
}
|
||||
|
||||
/**
|
||||
* map_wmi_error() - map errors from WMI methods to kernel error codes
|
||||
* @error_code: integer error code returned from Dell's firmware
|
||||
*/
|
||||
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)
|
||||
{
|
||||
char *start = buf;
|
||||
int i;
|
||||
|
||||
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 type = sysfs_match_string(reset_types, buf);
|
||||
int ret;
|
||||
|
||||
if (type < 0)
|
||||
return type;
|
||||
|
||||
ret = set_bios_defaults(type);
|
||||
pr_debug("reset all attributes request type %d: %d\n", type, ret);
|
||||
if (!ret) {
|
||||
reset_option = type;
|
||||
ret = count;
|
||||
}
|
||||
|
||||
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", wmi_priv.pending_changes);
|
||||
}
|
||||
|
||||
static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios);
|
||||
static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
|
||||
|
||||
|
||||
/**
|
||||
* create_attributes_level_sysfs_files() - Creates reset_bios and
|
||||
* pending_reboot attributes
|
||||
*/
|
||||
static int create_attributes_level_sysfs_files(void)
|
||||
{
|
||||
int ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
|
||||
|
||||
if (ret) {
|
||||
pr_debug("could not create reset_bios file\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr);
|
||||
if (ret) {
|
||||
pr_debug("could not create changing_pending_reboot file\n");
|
||||
sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void release_reset_bios_data(void)
|
||||
{
|
||||
sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr);
|
||||
sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr);
|
||||
}
|
||||
|
||||
static ssize_t wmi_sysman_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 wmi_sysman_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 wmi_sysman_kobj_sysfs_ops = {
|
||||
.show = wmi_sysman_attr_show,
|
||||
.store = wmi_sysman_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 = &wmi_sysman_kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* strlcpy_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 strlcpy_attr(char *dest, char *src)
|
||||
{
|
||||
size_t len = strlen(src) + 1;
|
||||
|
||||
if (len > 1 && len <= MAX_BUFF)
|
||||
strlcpy(dest, src, len);
|
||||
|
||||
/*len can be zero because any property not-applicable to attribute can
|
||||
* be empty so check only for too long buffers and log error
|
||||
*/
|
||||
if (len > MAX_BUFF)
|
||||
pr_err("Source string returned from BIOS is out of bound!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
acpi_status status;
|
||||
|
||||
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)
|
||||
{
|
||||
union acpi_object *wmi_obj = NULL;
|
||||
int i = 0;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
list_for_each_entry_safe(pos, next, &kset->list, entry) {
|
||||
kobject_put(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* release_attributes_data() - Clean-up all sysfs directories and files created
|
||||
*/
|
||||
static void release_attributes_data(void)
|
||||
{
|
||||
release_reset_bios_data();
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
exit_enum_attributes();
|
||||
exit_int_attributes();
|
||||
exit_str_attributes();
|
||||
exit_po_attributes();
|
||||
if (wmi_priv.authentication_dir_kset) {
|
||||
destroy_attribute_objs(wmi_priv.authentication_dir_kset);
|
||||
kset_unregister(wmi_priv.authentication_dir_kset);
|
||||
wmi_priv.authentication_dir_kset = NULL;
|
||||
}
|
||||
if (wmi_priv.main_dir_kset) {
|
||||
destroy_attribute_objs(wmi_priv.main_dir_kset);
|
||||
kset_unregister(wmi_priv.main_dir_kset);
|
||||
}
|
||||
mutex_unlock(&wmi_priv.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)
|
||||
{
|
||||
struct kobject *attr_name_kobj; //individual attribute names
|
||||
union acpi_object *obj = NULL;
|
||||
union acpi_object *elements;
|
||||
struct kset *tmp_set;
|
||||
|
||||
/* instance_id needs to be reset for each type GUID
|
||||
* also, instance IDs are unique within GUID but not across
|
||||
*/
|
||||
int instance_id = 0;
|
||||
int retval = 0;
|
||||
|
||||
retval = alloc_attributes_data(attr_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, guid);
|
||||
if (!obj)
|
||||
return -ENODEV;
|
||||
elements = obj->package.elements;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
while (elements) {
|
||||
/* sanity checking */
|
||||
if (strlen(elements[ATTR_NAME].string.pointer) == 0) {
|
||||
pr_debug("empty attribute found\n");
|
||||
goto nextobj;
|
||||
}
|
||||
if (attr_type == PO)
|
||||
tmp_set = wmi_priv.authentication_dir_kset;
|
||||
else
|
||||
tmp_set = wmi_priv.main_dir_kset;
|
||||
|
||||
if (kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer)) {
|
||||
pr_debug("duplicate attribute name found - %s\n",
|
||||
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 = tmp_set;
|
||||
|
||||
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) {
|
||||
pr_debug("failed to populate %s\n",
|
||||
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:
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __init sysman_init(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 = init_bios_attr_set_interface();
|
||||
if (ret || !wmi_priv.bios_attr_wdev) {
|
||||
pr_debug("failed to initialize set interface\n");
|
||||
goto fail_set_interface;
|
||||
}
|
||||
|
||||
ret = init_bios_attr_pass_interface();
|
||||
if (ret || !wmi_priv.password_attr_wdev) {
|
||||
pr_debug("failed to initialize pass interface\n");
|
||||
goto fail_pass_interface;
|
||||
}
|
||||
|
||||
ret = class_register(&firmware_attributes_class);
|
||||
if (ret)
|
||||
goto fail_class;
|
||||
|
||||
wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
||||
NULL, "%s", DRIVER_NAME);
|
||||
if (IS_ERR(wmi_priv.class_dev)) {
|
||||
ret = PTR_ERR(wmi_priv.class_dev);
|
||||
goto fail_classdev;
|
||||
}
|
||||
|
||||
wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL,
|
||||
&wmi_priv.class_dev->kobj);
|
||||
if (!wmi_priv.main_dir_kset) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_main_kset;
|
||||
}
|
||||
|
||||
wmi_priv.authentication_dir_kset = kset_create_and_add("authentication", NULL,
|
||||
&wmi_priv.class_dev->kobj);
|
||||
if (!wmi_priv.authentication_dir_kset) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_authentication_kset;
|
||||
}
|
||||
|
||||
ret = create_attributes_level_sysfs_files();
|
||||
if (ret) {
|
||||
pr_debug("could not create reset BIOS attribute\n");
|
||||
goto fail_reset_bios;
|
||||
}
|
||||
|
||||
ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
|
||||
if (ret) {
|
||||
pr_debug("failed to populate enumeration type attributes\n");
|
||||
goto fail_create_group;
|
||||
}
|
||||
|
||||
ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID);
|
||||
if (ret) {
|
||||
pr_debug("failed to populate integer type attributes\n");
|
||||
goto fail_create_group;
|
||||
}
|
||||
|
||||
ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID);
|
||||
if (ret) {
|
||||
pr_debug("failed to populate string type attributes\n");
|
||||
goto fail_create_group;
|
||||
}
|
||||
|
||||
ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
|
||||
if (ret) {
|
||||
pr_debug("failed to populate pass object type attributes\n");
|
||||
goto fail_create_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_create_group:
|
||||
release_attributes_data();
|
||||
|
||||
fail_reset_bios:
|
||||
if (wmi_priv.authentication_dir_kset) {
|
||||
kset_unregister(wmi_priv.authentication_dir_kset);
|
||||
wmi_priv.authentication_dir_kset = NULL;
|
||||
}
|
||||
|
||||
fail_authentication_kset:
|
||||
if (wmi_priv.main_dir_kset) {
|
||||
kset_unregister(wmi_priv.main_dir_kset);
|
||||
wmi_priv.main_dir_kset = NULL;
|
||||
}
|
||||
|
||||
fail_main_kset:
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
|
||||
fail_classdev:
|
||||
class_unregister(&firmware_attributes_class);
|
||||
|
||||
fail_class:
|
||||
exit_bios_attr_pass_interface();
|
||||
|
||||
fail_pass_interface:
|
||||
exit_bios_attr_set_interface();
|
||||
|
||||
fail_set_interface:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit sysman_exit(void)
|
||||
{
|
||||
release_attributes_data();
|
||||
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
||||
class_unregister(&firmware_attributes_class);
|
||||
exit_bios_attr_set_interface();
|
||||
exit_bios_attr_pass_interface();
|
||||
}
|
||||
|
||||
module_init(sysman_init);
|
||||
module_exit(sysman_exit);
|
||||
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>");
|
||||
MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>");
|
||||
MODULE_DESCRIPTION("Dell platform setting control interface");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue