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