162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Functions corresponding to SET methods under BIOS attributes interface GUID for use 462306a36Sopenharmony_ci * with dell-wmi-sysman 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2020 Dell Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/wmi.h> 1062306a36Sopenharmony_ci#include "dell-wmi-sysman.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define SETDEFAULTVALUES_METHOD_ID 0x02 1362306a36Sopenharmony_ci#define SETBIOSDEFAULTS_METHOD_ID 0x03 1462306a36Sopenharmony_ci#define SETATTRIBUTE_METHOD_ID 0x04 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, 1762306a36Sopenharmony_ci int method_id) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 2062306a36Sopenharmony_ci struct acpi_buffer input; 2162306a36Sopenharmony_ci union acpi_object *obj; 2262306a36Sopenharmony_ci acpi_status status; 2362306a36Sopenharmony_ci int ret = -EIO; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci input.length = (acpi_size) size; 2662306a36Sopenharmony_ci input.pointer = in_args; 2762306a36Sopenharmony_ci status = wmidev_evaluate_method(wdev, 0, method_id, &input, &output); 2862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 2962306a36Sopenharmony_ci return -EIO; 3062306a36Sopenharmony_ci obj = (union acpi_object *)output.pointer; 3162306a36Sopenharmony_ci if (obj->type == ACPI_TYPE_INTEGER) 3262306a36Sopenharmony_ci ret = obj->integer.value; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (wmi_priv.pending_changes == 0) { 3562306a36Sopenharmony_ci wmi_priv.pending_changes = 1; 3662306a36Sopenharmony_ci /* let userland know it may need to check reboot pending again */ 3762306a36Sopenharmony_ci kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci kfree(output.pointer); 4062306a36Sopenharmony_ci return map_wmi_error(ret); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * set_attribute() - Update an attribute value 4562306a36Sopenharmony_ci * @a_name: The attribute name 4662306a36Sopenharmony_ci * @a_value: The attribute value 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Sets an attribute to new value 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ciint set_attribute(const char *a_name, const char *a_value) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci size_t security_area_size, buffer_size; 5362306a36Sopenharmony_ci size_t a_name_size, a_value_size; 5462306a36Sopenharmony_ci char *buffer = NULL, *start; 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci mutex_lock(&wmi_priv.mutex); 5862306a36Sopenharmony_ci if (!wmi_priv.bios_attr_wdev) { 5962306a36Sopenharmony_ci ret = -ENODEV; 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* build/calculate buffer */ 6462306a36Sopenharmony_ci security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); 6562306a36Sopenharmony_ci a_name_size = calculate_string_buffer(a_name); 6662306a36Sopenharmony_ci a_value_size = calculate_string_buffer(a_value); 6762306a36Sopenharmony_ci buffer_size = security_area_size + a_name_size + a_value_size; 6862306a36Sopenharmony_ci buffer = kzalloc(buffer_size, GFP_KERNEL); 6962306a36Sopenharmony_ci if (!buffer) { 7062306a36Sopenharmony_ci ret = -ENOMEM; 7162306a36Sopenharmony_ci goto out; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* build security area */ 7562306a36Sopenharmony_ci populate_security_buffer(buffer, wmi_priv.current_admin_password); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* build variables to set */ 7862306a36Sopenharmony_ci start = buffer + security_area_size; 7962306a36Sopenharmony_ci ret = populate_string_buffer(start, a_name_size, a_name); 8062306a36Sopenharmony_ci if (ret < 0) 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci start += ret; 8362306a36Sopenharmony_ci ret = populate_string_buffer(start, a_value_size, a_value); 8462306a36Sopenharmony_ci if (ret < 0) 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size); 8862306a36Sopenharmony_ci ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, 8962306a36Sopenharmony_ci buffer, buffer_size, 9062306a36Sopenharmony_ci SETATTRIBUTE_METHOD_ID); 9162306a36Sopenharmony_ci if (ret == -EOPNOTSUPP) 9262306a36Sopenharmony_ci dev_err(&wmi_priv.bios_attr_wdev->dev, "admin password must be configured\n"); 9362306a36Sopenharmony_ci else if (ret == -EACCES) 9462306a36Sopenharmony_ci dev_err(&wmi_priv.bios_attr_wdev->dev, "invalid password\n"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciout: 9762306a36Sopenharmony_ci kfree(buffer); 9862306a36Sopenharmony_ci mutex_unlock(&wmi_priv.mutex); 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/** 10362306a36Sopenharmony_ci * set_bios_defaults() - Resets BIOS defaults 10462306a36Sopenharmony_ci * @deftype: the type of BIOS value reset to issue. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Resets BIOS defaults 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ciint set_bios_defaults(u8 deftype) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci size_t security_area_size, buffer_size; 11162306a36Sopenharmony_ci size_t integer_area_size = sizeof(u8); 11262306a36Sopenharmony_ci char *buffer = NULL; 11362306a36Sopenharmony_ci u8 *defaultType; 11462306a36Sopenharmony_ci int ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci mutex_lock(&wmi_priv.mutex); 11762306a36Sopenharmony_ci if (!wmi_priv.bios_attr_wdev) { 11862306a36Sopenharmony_ci ret = -ENODEV; 11962306a36Sopenharmony_ci goto out; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); 12362306a36Sopenharmony_ci buffer_size = security_area_size + integer_area_size; 12462306a36Sopenharmony_ci buffer = kzalloc(buffer_size, GFP_KERNEL); 12562306a36Sopenharmony_ci if (!buffer) { 12662306a36Sopenharmony_ci ret = -ENOMEM; 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* build security area */ 13162306a36Sopenharmony_ci populate_security_buffer(buffer, wmi_priv.current_admin_password); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci defaultType = buffer + security_area_size; 13462306a36Sopenharmony_ci *defaultType = deftype; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, buffer, buffer_size, 13762306a36Sopenharmony_ci SETBIOSDEFAULTS_METHOD_ID); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci dev_err(&wmi_priv.bios_attr_wdev->dev, "reset BIOS defaults failed: %d\n", ret); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci kfree(buffer); 14262306a36Sopenharmony_ciout: 14362306a36Sopenharmony_ci mutex_unlock(&wmi_priv.mutex); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci mutex_lock(&wmi_priv.mutex); 15062306a36Sopenharmony_ci wmi_priv.bios_attr_wdev = wdev; 15162306a36Sopenharmony_ci mutex_unlock(&wmi_priv.mutex); 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void bios_attr_set_interface_remove(struct wmi_device *wdev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci mutex_lock(&wmi_priv.mutex); 15862306a36Sopenharmony_ci wmi_priv.bios_attr_wdev = NULL; 15962306a36Sopenharmony_ci mutex_unlock(&wmi_priv.mutex); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct wmi_device_id bios_attr_set_interface_id_table[] = { 16362306a36Sopenharmony_ci { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, 16462306a36Sopenharmony_ci { }, 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_cistatic struct wmi_driver bios_attr_set_interface_driver = { 16762306a36Sopenharmony_ci .driver = { 16862306a36Sopenharmony_ci .name = DRIVER_NAME 16962306a36Sopenharmony_ci }, 17062306a36Sopenharmony_ci .probe = bios_attr_set_interface_probe, 17162306a36Sopenharmony_ci .remove = bios_attr_set_interface_remove, 17262306a36Sopenharmony_ci .id_table = bios_attr_set_interface_id_table, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciint init_bios_attr_set_interface(void) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return wmi_driver_register(&bios_attr_set_interface_driver); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_civoid exit_bios_attr_set_interface(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci wmi_driver_unregister(&bios_attr_set_interface_driver); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, bios_attr_set_interface_id_table); 186