Commit e8a60aa7 authored by Divya Bharathi's avatar Divya Bharathi Committed by Hans de Goede

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: default avatarMario Limonciello <mario.limonciello@dell.com>
Signed-off-by: default avatarMario Limonciello <mario.limonciello@dell.com>
Co-developed-by: default avatarPrasanth KSR <prasanth.ksr@dell.com>
Signed-off-by: default avatarPrasanth KSR <prasanth.ksr@dell.com>
Signed-off-by: default avatarDivya Bharathi <divya.bharathi@dell.com>
Link: https://lore.kernel.org/r/20201027134944.316730-1-divya.bharathi@dell.comReviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 56afb8d4
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
......
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
// 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);
/* 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
// 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,
&current_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);
}
// 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);
}
// 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);
}
// 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);
// 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);
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment