Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Slim Bootloader(SBL) firmware update signaling driver
0004  *
0005  * Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware
0006  * optimized for running on certain Intel platforms.
0007  *
0008  * SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>.
0009  * This driver further adds "firmware_update_request" device attribute.
0010  * This attribute normally has a value of 0 and userspace can signal SBL
0011  * to update firmware, on next reboot, by writing a value of 1.
0012  *
0013  * More details of SBL firmware update process is available at:
0014  * https://slimbootloader.github.io/security/firmware-update.html
0015  */
0016 
0017 #include <linux/acpi.h>
0018 #include <linux/device.h>
0019 #include <linux/module.h>
0020 #include <linux/slab.h>
0021 #include <linux/sysfs.h>
0022 #include <linux/wmi.h>
0023 
0024 #define INTEL_WMI_SBL_GUID  "44FADEB1-B204-40F2-8581-394BBDC1B651"
0025 
0026 static int get_fwu_request(struct device *dev, u32 *out)
0027 {
0028     struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL};
0029     union acpi_object *obj;
0030     acpi_status status;
0031 
0032     status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result);
0033     if (ACPI_FAILURE(status)) {
0034         dev_err(dev, "wmi_query_block failed\n");
0035         return -ENODEV;
0036     }
0037 
0038     obj = (union acpi_object *)result.pointer;
0039     if (!obj || obj->type != ACPI_TYPE_INTEGER) {
0040         dev_warn(dev, "wmi_query_block returned invalid value\n");
0041         kfree(obj);
0042         return -EINVAL;
0043     }
0044 
0045     *out = obj->integer.value;
0046     kfree(obj);
0047 
0048     return 0;
0049 }
0050 
0051 static int set_fwu_request(struct device *dev, u32 in)
0052 {
0053     struct acpi_buffer input;
0054     acpi_status status;
0055     u32 value;
0056 
0057     value = in;
0058     input.length = sizeof(u32);
0059     input.pointer = &value;
0060 
0061     status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input);
0062     if (ACPI_FAILURE(status)) {
0063         dev_err(dev, "wmi_set_block failed\n");
0064         return -ENODEV;
0065     }
0066 
0067     return 0;
0068 }
0069 
0070 static ssize_t firmware_update_request_show(struct device *dev,
0071                         struct device_attribute *attr,
0072                         char *buf)
0073 {
0074     u32 val;
0075     int ret;
0076 
0077     ret = get_fwu_request(dev, &val);
0078     if (ret)
0079         return ret;
0080 
0081     return sprintf(buf, "%d\n", val);
0082 }
0083 
0084 static ssize_t firmware_update_request_store(struct device *dev,
0085                          struct device_attribute *attr,
0086                          const char *buf, size_t count)
0087 {
0088     unsigned int val;
0089     int ret;
0090 
0091     ret = kstrtouint(buf, 0, &val);
0092     if (ret)
0093         return ret;
0094 
0095     /* May later be extended to support values other than 0 and 1 */
0096     if (val > 1)
0097         return -ERANGE;
0098 
0099     ret = set_fwu_request(dev, val);
0100     if (ret)
0101         return ret;
0102 
0103     return count;
0104 }
0105 static DEVICE_ATTR_RW(firmware_update_request);
0106 
0107 static struct attribute *firmware_update_attrs[] = {
0108     &dev_attr_firmware_update_request.attr,
0109     NULL
0110 };
0111 ATTRIBUTE_GROUPS(firmware_update);
0112 
0113 static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
0114                      const void *context)
0115 {
0116     dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n");
0117     return 0;
0118 }
0119 
0120 static void intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
0121 {
0122     dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
0123 }
0124 
0125 static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
0126     { .guid_string = INTEL_WMI_SBL_GUID },
0127     {}
0128 };
0129 MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table);
0130 
0131 static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
0132     .driver = {
0133         .name = "intel-wmi-sbl-fw-update",
0134         .dev_groups = firmware_update_groups,
0135     },
0136     .probe = intel_wmi_sbl_fw_update_probe,
0137     .remove = intel_wmi_sbl_fw_update_remove,
0138     .id_table = intel_wmi_sbl_id_table,
0139 };
0140 module_wmi_driver(intel_wmi_sbl_fw_update_driver);
0141 
0142 MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>");
0143 MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver");
0144 MODULE_LICENSE("GPL v2");