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