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