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