1bd5b4fb4SKate Hsuan // SPDX-License-Identifier: GPL-2.0
2bd5b4fb4SKate Hsuan /*
3bd5b4fb4SKate Hsuan * Slim Bootloader(SBL) firmware update signaling driver
4bd5b4fb4SKate Hsuan *
5bd5b4fb4SKate Hsuan * Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware
6bd5b4fb4SKate Hsuan * optimized for running on certain Intel platforms.
7bd5b4fb4SKate Hsuan *
8bd5b4fb4SKate Hsuan * SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>.
9bd5b4fb4SKate Hsuan * This driver further adds "firmware_update_request" device attribute.
10bd5b4fb4SKate Hsuan * This attribute normally has a value of 0 and userspace can signal SBL
11bd5b4fb4SKate Hsuan * to update firmware, on next reboot, by writing a value of 1.
12bd5b4fb4SKate Hsuan *
13bd5b4fb4SKate Hsuan * More details of SBL firmware update process is available at:
14bd5b4fb4SKate Hsuan * https://slimbootloader.github.io/security/firmware-update.html
15bd5b4fb4SKate Hsuan */
16bd5b4fb4SKate Hsuan
17bd5b4fb4SKate Hsuan #include <linux/acpi.h>
18bd5b4fb4SKate Hsuan #include <linux/device.h>
19bd5b4fb4SKate Hsuan #include <linux/module.h>
20bd5b4fb4SKate Hsuan #include <linux/slab.h>
21bd5b4fb4SKate Hsuan #include <linux/sysfs.h>
22bd5b4fb4SKate Hsuan #include <linux/wmi.h>
23bd5b4fb4SKate Hsuan
24bd5b4fb4SKate Hsuan #define INTEL_WMI_SBL_GUID "44FADEB1-B204-40F2-8581-394BBDC1B651"
25bd5b4fb4SKate Hsuan
get_fwu_request(struct device * dev,u32 * out)26bd5b4fb4SKate Hsuan static int get_fwu_request(struct device *dev, u32 *out)
27bd5b4fb4SKate Hsuan {
28bd5b4fb4SKate Hsuan union acpi_object *obj;
29bd5b4fb4SKate Hsuan
3075c487fcSArmin Wolf obj = wmidev_block_query(to_wmi_device(dev), 0);
3175c487fcSArmin Wolf if (!obj)
32bd5b4fb4SKate Hsuan return -ENODEV;
33bd5b4fb4SKate Hsuan
3475c487fcSArmin Wolf if (obj->type != ACPI_TYPE_INTEGER) {
35348d9cc7SArmin Wolf dev_warn(dev, "wmidev_block_query returned invalid value\n");
36bd5b4fb4SKate Hsuan kfree(obj);
37bd5b4fb4SKate Hsuan return -EINVAL;
38bd5b4fb4SKate Hsuan }
39bd5b4fb4SKate Hsuan
40bd5b4fb4SKate Hsuan *out = obj->integer.value;
41bd5b4fb4SKate Hsuan kfree(obj);
42bd5b4fb4SKate Hsuan
43bd5b4fb4SKate Hsuan return 0;
44bd5b4fb4SKate Hsuan }
45bd5b4fb4SKate Hsuan
set_fwu_request(struct device * dev,u32 in)46bd5b4fb4SKate Hsuan static int set_fwu_request(struct device *dev, u32 in)
47bd5b4fb4SKate Hsuan {
48bd5b4fb4SKate Hsuan struct acpi_buffer input;
49bd5b4fb4SKate Hsuan acpi_status status;
50bd5b4fb4SKate Hsuan u32 value;
51bd5b4fb4SKate Hsuan
52bd5b4fb4SKate Hsuan value = in;
53bd5b4fb4SKate Hsuan input.length = sizeof(u32);
54bd5b4fb4SKate Hsuan input.pointer = &value;
55bd5b4fb4SKate Hsuan
5675c487fcSArmin Wolf status = wmidev_block_set(to_wmi_device(dev), 0, &input);
57bd5b4fb4SKate Hsuan if (ACPI_FAILURE(status)) {
58348d9cc7SArmin Wolf dev_err(dev, "wmidev_block_set failed\n");
59bd5b4fb4SKate Hsuan return -ENODEV;
60bd5b4fb4SKate Hsuan }
61bd5b4fb4SKate Hsuan
62bd5b4fb4SKate Hsuan return 0;
63bd5b4fb4SKate Hsuan }
64bd5b4fb4SKate Hsuan
firmware_update_request_show(struct device * dev,struct device_attribute * attr,char * buf)65bd5b4fb4SKate Hsuan static ssize_t firmware_update_request_show(struct device *dev,
66bd5b4fb4SKate Hsuan struct device_attribute *attr,
67bd5b4fb4SKate Hsuan char *buf)
68bd5b4fb4SKate Hsuan {
69bd5b4fb4SKate Hsuan u32 val;
70bd5b4fb4SKate Hsuan int ret;
71bd5b4fb4SKate Hsuan
72bd5b4fb4SKate Hsuan ret = get_fwu_request(dev, &val);
73bd5b4fb4SKate Hsuan if (ret)
74bd5b4fb4SKate Hsuan return ret;
75bd5b4fb4SKate Hsuan
76bd5b4fb4SKate Hsuan return sprintf(buf, "%d\n", val);
77bd5b4fb4SKate Hsuan }
78bd5b4fb4SKate Hsuan
firmware_update_request_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)79bd5b4fb4SKate Hsuan static ssize_t firmware_update_request_store(struct device *dev,
80bd5b4fb4SKate Hsuan struct device_attribute *attr,
81bd5b4fb4SKate Hsuan const char *buf, size_t count)
82bd5b4fb4SKate Hsuan {
83bd5b4fb4SKate Hsuan unsigned int val;
84bd5b4fb4SKate Hsuan int ret;
85bd5b4fb4SKate Hsuan
86bd5b4fb4SKate Hsuan ret = kstrtouint(buf, 0, &val);
87bd5b4fb4SKate Hsuan if (ret)
88bd5b4fb4SKate Hsuan return ret;
89bd5b4fb4SKate Hsuan
90bd5b4fb4SKate Hsuan /* May later be extended to support values other than 0 and 1 */
91bd5b4fb4SKate Hsuan if (val > 1)
92bd5b4fb4SKate Hsuan return -ERANGE;
93bd5b4fb4SKate Hsuan
94bd5b4fb4SKate Hsuan ret = set_fwu_request(dev, val);
95bd5b4fb4SKate Hsuan if (ret)
96bd5b4fb4SKate Hsuan return ret;
97bd5b4fb4SKate Hsuan
98bd5b4fb4SKate Hsuan return count;
99bd5b4fb4SKate Hsuan }
100bd5b4fb4SKate Hsuan static DEVICE_ATTR_RW(firmware_update_request);
101bd5b4fb4SKate Hsuan
102bd5b4fb4SKate Hsuan static struct attribute *firmware_update_attrs[] = {
103bd5b4fb4SKate Hsuan &dev_attr_firmware_update_request.attr,
104bd5b4fb4SKate Hsuan NULL
105bd5b4fb4SKate Hsuan };
106bd5b4fb4SKate Hsuan ATTRIBUTE_GROUPS(firmware_update);
107bd5b4fb4SKate Hsuan
intel_wmi_sbl_fw_update_probe(struct wmi_device * wdev,const void * context)108bd5b4fb4SKate Hsuan static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
109bd5b4fb4SKate Hsuan const void *context)
110bd5b4fb4SKate Hsuan {
111bd5b4fb4SKate Hsuan dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n");
112bd5b4fb4SKate Hsuan return 0;
113bd5b4fb4SKate Hsuan }
114bd5b4fb4SKate Hsuan
intel_wmi_sbl_fw_update_remove(struct wmi_device * wdev)115bd5b4fb4SKate Hsuan static void intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
116bd5b4fb4SKate Hsuan {
117bd5b4fb4SKate Hsuan dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
118bd5b4fb4SKate Hsuan }
119bd5b4fb4SKate Hsuan
120bd5b4fb4SKate Hsuan static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
121bd5b4fb4SKate Hsuan { .guid_string = INTEL_WMI_SBL_GUID },
122bd5b4fb4SKate Hsuan {}
123bd5b4fb4SKate Hsuan };
124bd5b4fb4SKate Hsuan MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table);
125bd5b4fb4SKate Hsuan
126bd5b4fb4SKate Hsuan static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
127bd5b4fb4SKate Hsuan .driver = {
128bd5b4fb4SKate Hsuan .name = "intel-wmi-sbl-fw-update",
129bd5b4fb4SKate Hsuan .dev_groups = firmware_update_groups,
130bd5b4fb4SKate Hsuan },
131bd5b4fb4SKate Hsuan .probe = intel_wmi_sbl_fw_update_probe,
132bd5b4fb4SKate Hsuan .remove = intel_wmi_sbl_fw_update_remove,
133bd5b4fb4SKate Hsuan .id_table = intel_wmi_sbl_id_table,
134*a66ccfc2SArmin Wolf .no_singleton = true,
135bd5b4fb4SKate Hsuan };
136bd5b4fb4SKate Hsuan module_wmi_driver(intel_wmi_sbl_fw_update_driver);
137bd5b4fb4SKate Hsuan
138bd5b4fb4SKate Hsuan MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>");
139bd5b4fb4SKate Hsuan MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver");
140bd5b4fb4SKate Hsuan MODULE_LICENSE("GPL v2");
141