1f1e1ea51SMario Limonciello // SPDX-License-Identifier: GPL-2.0
2f1e1ea51SMario Limonciello /*
3f1e1ea51SMario Limonciello * Functions corresponding to SET methods under BIOS attributes interface GUID for use
4f1e1ea51SMario Limonciello * with dell-wmi-sysman
5f1e1ea51SMario Limonciello *
6f1e1ea51SMario Limonciello * Copyright (c) 2020 Dell Inc.
7f1e1ea51SMario Limonciello */
8f1e1ea51SMario Limonciello
9f1e1ea51SMario Limonciello #include <linux/wmi.h>
10f1e1ea51SMario Limonciello #include "dell-wmi-sysman.h"
11f1e1ea51SMario Limonciello
12f1e1ea51SMario Limonciello #define SETDEFAULTVALUES_METHOD_ID 0x02
13f1e1ea51SMario Limonciello #define SETBIOSDEFAULTS_METHOD_ID 0x03
14f1e1ea51SMario Limonciello #define SETATTRIBUTE_METHOD_ID 0x04
15f1e1ea51SMario Limonciello
call_biosattributes_interface(struct wmi_device * wdev,char * in_args,size_t size,int method_id)16f1e1ea51SMario Limonciello static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size,
17f1e1ea51SMario Limonciello int method_id)
18f1e1ea51SMario Limonciello {
19f1e1ea51SMario Limonciello struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
20f1e1ea51SMario Limonciello struct acpi_buffer input;
21f1e1ea51SMario Limonciello union acpi_object *obj;
22f1e1ea51SMario Limonciello acpi_status status;
23f1e1ea51SMario Limonciello int ret = -EIO;
24f1e1ea51SMario Limonciello
25f1e1ea51SMario Limonciello input.length = (acpi_size) size;
26f1e1ea51SMario Limonciello input.pointer = in_args;
27f1e1ea51SMario Limonciello status = wmidev_evaluate_method(wdev, 0, method_id, &input, &output);
28f1e1ea51SMario Limonciello if (ACPI_FAILURE(status))
29f1e1ea51SMario Limonciello return -EIO;
30f1e1ea51SMario Limonciello obj = (union acpi_object *)output.pointer;
31f1e1ea51SMario Limonciello if (obj->type == ACPI_TYPE_INTEGER)
32f1e1ea51SMario Limonciello ret = obj->integer.value;
33f1e1ea51SMario Limonciello
34f1e1ea51SMario Limonciello if (wmi_priv.pending_changes == 0) {
35f1e1ea51SMario Limonciello wmi_priv.pending_changes = 1;
36f1e1ea51SMario Limonciello /* let userland know it may need to check reboot pending again */
37f1e1ea51SMario Limonciello kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE);
38f1e1ea51SMario Limonciello }
39f1e1ea51SMario Limonciello kfree(output.pointer);
40f1e1ea51SMario Limonciello return map_wmi_error(ret);
41f1e1ea51SMario Limonciello }
42f1e1ea51SMario Limonciello
43f1e1ea51SMario Limonciello /**
44f1e1ea51SMario Limonciello * set_attribute() - Update an attribute value
45f1e1ea51SMario Limonciello * @a_name: The attribute name
46f1e1ea51SMario Limonciello * @a_value: The attribute value
47f1e1ea51SMario Limonciello *
48f1e1ea51SMario Limonciello * Sets an attribute to new value
49f1e1ea51SMario Limonciello */
set_attribute(const char * a_name,const char * a_value)50f1e1ea51SMario Limonciello int set_attribute(const char *a_name, const char *a_value)
51f1e1ea51SMario Limonciello {
52f1e1ea51SMario Limonciello size_t security_area_size, buffer_size;
53f1e1ea51SMario Limonciello size_t a_name_size, a_value_size;
54f1e1ea51SMario Limonciello char *buffer = NULL, *start;
55f1e1ea51SMario Limonciello int ret;
56f1e1ea51SMario Limonciello
57f1e1ea51SMario Limonciello mutex_lock(&wmi_priv.mutex);
58f1e1ea51SMario Limonciello if (!wmi_priv.bios_attr_wdev) {
59f1e1ea51SMario Limonciello ret = -ENODEV;
60f1e1ea51SMario Limonciello goto out;
61f1e1ea51SMario Limonciello }
62f1e1ea51SMario Limonciello
63f1e1ea51SMario Limonciello /* build/calculate buffer */
64f1e1ea51SMario Limonciello security_area_size = calculate_security_buffer(wmi_priv.current_admin_password);
65f1e1ea51SMario Limonciello a_name_size = calculate_string_buffer(a_name);
66f1e1ea51SMario Limonciello a_value_size = calculate_string_buffer(a_value);
67f1e1ea51SMario Limonciello buffer_size = security_area_size + a_name_size + a_value_size;
68f1e1ea51SMario Limonciello buffer = kzalloc(buffer_size, GFP_KERNEL);
69f1e1ea51SMario Limonciello if (!buffer) {
70f1e1ea51SMario Limonciello ret = -ENOMEM;
71f1e1ea51SMario Limonciello goto out;
72f1e1ea51SMario Limonciello }
73f1e1ea51SMario Limonciello
74f1e1ea51SMario Limonciello /* build security area */
75f1e1ea51SMario Limonciello populate_security_buffer(buffer, wmi_priv.current_admin_password);
76f1e1ea51SMario Limonciello
77f1e1ea51SMario Limonciello /* build variables to set */
78f1e1ea51SMario Limonciello start = buffer + security_area_size;
79f1e1ea51SMario Limonciello ret = populate_string_buffer(start, a_name_size, a_name);
80f1e1ea51SMario Limonciello if (ret < 0)
81f1e1ea51SMario Limonciello goto out;
82f1e1ea51SMario Limonciello start += ret;
83f1e1ea51SMario Limonciello ret = populate_string_buffer(start, a_value_size, a_value);
84f1e1ea51SMario Limonciello if (ret < 0)
85f1e1ea51SMario Limonciello goto out;
86f1e1ea51SMario Limonciello
87f1e1ea51SMario Limonciello print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
88f1e1ea51SMario Limonciello ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev,
89f1e1ea51SMario Limonciello buffer, buffer_size,
90f1e1ea51SMario Limonciello SETATTRIBUTE_METHOD_ID);
91f1e1ea51SMario Limonciello if (ret == -EOPNOTSUPP)
92f1e1ea51SMario Limonciello dev_err(&wmi_priv.bios_attr_wdev->dev, "admin password must be configured\n");
93f1e1ea51SMario Limonciello else if (ret == -EACCES)
94f1e1ea51SMario Limonciello dev_err(&wmi_priv.bios_attr_wdev->dev, "invalid password\n");
95f1e1ea51SMario Limonciello
96f1e1ea51SMario Limonciello out:
97f1e1ea51SMario Limonciello kfree(buffer);
98f1e1ea51SMario Limonciello mutex_unlock(&wmi_priv.mutex);
99f1e1ea51SMario Limonciello return ret;
100f1e1ea51SMario Limonciello }
101f1e1ea51SMario Limonciello
102f1e1ea51SMario Limonciello /**
103f1e1ea51SMario Limonciello * set_bios_defaults() - Resets BIOS defaults
104f1e1ea51SMario Limonciello * @deftype: the type of BIOS value reset to issue.
105f1e1ea51SMario Limonciello *
106f1e1ea51SMario Limonciello * Resets BIOS defaults
107f1e1ea51SMario Limonciello */
set_bios_defaults(u8 deftype)108f1e1ea51SMario Limonciello int set_bios_defaults(u8 deftype)
109f1e1ea51SMario Limonciello {
110f1e1ea51SMario Limonciello size_t security_area_size, buffer_size;
111f1e1ea51SMario Limonciello size_t integer_area_size = sizeof(u8);
112f1e1ea51SMario Limonciello char *buffer = NULL;
113f1e1ea51SMario Limonciello u8 *defaultType;
114f1e1ea51SMario Limonciello int ret;
115f1e1ea51SMario Limonciello
116f1e1ea51SMario Limonciello mutex_lock(&wmi_priv.mutex);
117f1e1ea51SMario Limonciello if (!wmi_priv.bios_attr_wdev) {
118f1e1ea51SMario Limonciello ret = -ENODEV;
119f1e1ea51SMario Limonciello goto out;
120f1e1ea51SMario Limonciello }
121f1e1ea51SMario Limonciello
122f1e1ea51SMario Limonciello security_area_size = calculate_security_buffer(wmi_priv.current_admin_password);
123f1e1ea51SMario Limonciello buffer_size = security_area_size + integer_area_size;
124f1e1ea51SMario Limonciello buffer = kzalloc(buffer_size, GFP_KERNEL);
125f1e1ea51SMario Limonciello if (!buffer) {
126f1e1ea51SMario Limonciello ret = -ENOMEM;
127f1e1ea51SMario Limonciello goto out;
128f1e1ea51SMario Limonciello }
129f1e1ea51SMario Limonciello
130f1e1ea51SMario Limonciello /* build security area */
131f1e1ea51SMario Limonciello populate_security_buffer(buffer, wmi_priv.current_admin_password);
132f1e1ea51SMario Limonciello
133f1e1ea51SMario Limonciello defaultType = buffer + security_area_size;
134f1e1ea51SMario Limonciello *defaultType = deftype;
135f1e1ea51SMario Limonciello
136f1e1ea51SMario Limonciello ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, buffer, buffer_size,
137f1e1ea51SMario Limonciello SETBIOSDEFAULTS_METHOD_ID);
138f1e1ea51SMario Limonciello if (ret)
139f1e1ea51SMario Limonciello dev_err(&wmi_priv.bios_attr_wdev->dev, "reset BIOS defaults failed: %d\n", ret);
140f1e1ea51SMario Limonciello
141f1e1ea51SMario Limonciello kfree(buffer);
142f1e1ea51SMario Limonciello out:
143f1e1ea51SMario Limonciello mutex_unlock(&wmi_priv.mutex);
144f1e1ea51SMario Limonciello return ret;
145f1e1ea51SMario Limonciello }
146f1e1ea51SMario Limonciello
bios_attr_set_interface_probe(struct wmi_device * wdev,const void * context)147f1e1ea51SMario Limonciello static int bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context)
148f1e1ea51SMario Limonciello {
149f1e1ea51SMario Limonciello mutex_lock(&wmi_priv.mutex);
150f1e1ea51SMario Limonciello wmi_priv.bios_attr_wdev = wdev;
151f1e1ea51SMario Limonciello mutex_unlock(&wmi_priv.mutex);
152f1e1ea51SMario Limonciello return 0;
153f1e1ea51SMario Limonciello }
154f1e1ea51SMario Limonciello
bios_attr_set_interface_remove(struct wmi_device * wdev)155*2b329f56SUwe Kleine-König static void bios_attr_set_interface_remove(struct wmi_device *wdev)
156f1e1ea51SMario Limonciello {
157f1e1ea51SMario Limonciello mutex_lock(&wmi_priv.mutex);
158f1e1ea51SMario Limonciello wmi_priv.bios_attr_wdev = NULL;
159f1e1ea51SMario Limonciello mutex_unlock(&wmi_priv.mutex);
160f1e1ea51SMario Limonciello }
161f1e1ea51SMario Limonciello
162f1e1ea51SMario Limonciello static const struct wmi_device_id bios_attr_set_interface_id_table[] = {
163f1e1ea51SMario Limonciello { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID },
164f1e1ea51SMario Limonciello { },
165f1e1ea51SMario Limonciello };
166f1e1ea51SMario Limonciello static struct wmi_driver bios_attr_set_interface_driver = {
167f1e1ea51SMario Limonciello .driver = {
168f1e1ea51SMario Limonciello .name = DRIVER_NAME
169f1e1ea51SMario Limonciello },
170f1e1ea51SMario Limonciello .probe = bios_attr_set_interface_probe,
171f1e1ea51SMario Limonciello .remove = bios_attr_set_interface_remove,
172f1e1ea51SMario Limonciello .id_table = bios_attr_set_interface_id_table,
173f1e1ea51SMario Limonciello };
174f1e1ea51SMario Limonciello
init_bios_attr_set_interface(void)175f1e1ea51SMario Limonciello int init_bios_attr_set_interface(void)
176f1e1ea51SMario Limonciello {
177f1e1ea51SMario Limonciello return wmi_driver_register(&bios_attr_set_interface_driver);
178f1e1ea51SMario Limonciello }
179f1e1ea51SMario Limonciello
exit_bios_attr_set_interface(void)180f1e1ea51SMario Limonciello void exit_bios_attr_set_interface(void)
181f1e1ea51SMario Limonciello {
182f1e1ea51SMario Limonciello wmi_driver_unregister(&bios_attr_set_interface_driver);
183f1e1ea51SMario Limonciello }
184f1e1ea51SMario Limonciello
185f1e1ea51SMario Limonciello MODULE_DEVICE_TABLE(wmi, bios_attr_set_interface_id_table);
186