diff options
Diffstat (limited to 'drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c')
-rw-r--r-- | drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c new file mode 100644 index 000000000..c2dd2de6b --- /dev/null +++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c @@ -0,0 +1,185 @@ +// 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 void 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); +} + +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); |