Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86: Add Slim Bootloader firmware update signaling driver

Slim Bootloader(SBL) is a small open-source boot firmware,
designed for running on certain Intel platforms. SBL can be
thought-of as fulfilling the role of a minimal BIOS
implementation, i.e initializing the hardware and booting
Operating System.

Since SBL is not UEFI compliant, firmware update cannot be triggered
using standard UEFI runtime services. Further considering performance
impact, SBL doesn't look for a firmware update image on every reset
and does so only when firmware update signal is asserted.

SBL exposes an ACPI-WMI device which comes up in sysfs as
/sys/bus/wmi/44FADEB1xxx and this driver adds a
"firmware_update_request" device attribute. This attribute normally
has a value of 0 and userspace can signal SBL to update firmware,
on next reboot, by writing a value of 1 like:

echo 1 > /sys/bus/wmi/devices/44FADEB1xxx/firmware_update_request

This driver only implements a signaling mechanism, the actual firmware
update process and various details like firmware update image format,
firmware image location etc are defined by SBL and are not in the
scope of this driver.

DocLink: https://slimbootloader.github.io/security/firmware-update.html
Signed-off-by: Jithu Joseph <jithu.joseph@intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

authored by

Jithu Joseph and committed by
Andy Shevchenko
2d30fcdd 3ce2db60

+175
+12
Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update
··· 1 + What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request 2 + Date: April 2020 3 + KernelVersion: 5.7 4 + Contact: "Jithu Joseph" <jithu.joseph@intel.com> 5 + Description: 6 + Allow user space entities to trigger update of Slim 7 + Bootloader (SBL). This attribute normally has a value 8 + of 0 and userspace can signal SBL to update firmware, 9 + on next reboot, by writing a value of 1. 10 + There are two available states: 11 + * 0 -> Skip firmware update while rebooting 12 + * 1 -> Attempt firmware update on next reboot
+7
MAINTAINERS
··· 8837 8837 F: drivers/net/wimax/i2400m/ 8838 8838 F: include/uapi/linux/wimax/i2400m.h 8839 8839 8840 + INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER 8841 + M: Jithu Joseph <jithu.joseph@intel.com> 8842 + R: Maurice Ma <maurice.ma@intel.com> 8843 + S: Maintained 8844 + W: https://slimbootloader.github.io/security/firmware-update.html 8845 + F: drivers/platform/x86/intel-wmi-sbl-fw-update.c 8846 + 8840 8847 INTEL WMI THUNDERBOLT FORCE POWER DRIVER 8841 8848 M: Mario Limonciello <mario.limonciello@dell.com> 8842 8849 S: Maintained
+10
drivers/platform/x86/Kconfig
··· 78 78 To compile this driver as a module, choose M here: the module 79 79 will be called huawei-wmi. 80 80 81 + config INTEL_WMI_SBL_FW_UPDATE 82 + tristate "Intel WMI Slim Bootloader firmware update signaling driver" 83 + depends on ACPI_WMI 84 + help 85 + Say Y here if you want to be able to use the WMI interface to signal 86 + Slim Bootloader to trigger update on next reboot. 87 + 88 + To compile this driver as a module, choose M here: the module will 89 + be called intel-wmi-sbl-fw-update. 90 + 81 91 config INTEL_WMI_THUNDERBOLT 82 92 tristate "Intel WMI thunderbolt force power driver" 83 93 depends on ACPI_WMI
+1
drivers/platform/x86/Makefile
··· 11 11 # WMI drivers 12 12 obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o 13 13 obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o 14 + obj-$(CONFIG_INTEL_WMI_SBL_FW_UPDATE) += intel-wmi-sbl-fw-update.o 14 15 obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o 15 16 obj-$(CONFIG_MXM_WMI) += mxm-wmi.o 16 17 obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
+145
drivers/platform/x86/intel-wmi-sbl-fw-update.c
··· 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 + { 28 + struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL}; 29 + union acpi_object *obj; 30 + acpi_status status; 31 + 32 + status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result); 33 + if (ACPI_FAILURE(status)) { 34 + dev_err(dev, "wmi_query_block failed\n"); 35 + return -ENODEV; 36 + } 37 + 38 + obj = (union acpi_object *)result.pointer; 39 + if (!obj || obj->type != ACPI_TYPE_INTEGER) { 40 + dev_warn(dev, "wmi_query_block returned invalid value\n"); 41 + kfree(obj); 42 + return -EINVAL; 43 + } 44 + 45 + *out = obj->integer.value; 46 + kfree(obj); 47 + 48 + return 0; 49 + } 50 + 51 + static int set_fwu_request(struct device *dev, u32 in) 52 + { 53 + struct acpi_buffer input; 54 + acpi_status status; 55 + u32 value; 56 + 57 + value = in; 58 + input.length = sizeof(u32); 59 + input.pointer = &value; 60 + 61 + status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input); 62 + if (ACPI_FAILURE(status)) { 63 + dev_err(dev, "wmi_set_block failed\n"); 64 + return -ENODEV; 65 + } 66 + 67 + return 0; 68 + } 69 + 70 + static ssize_t firmware_update_request_show(struct device *dev, 71 + struct device_attribute *attr, 72 + char *buf) 73 + { 74 + u32 val; 75 + int ret; 76 + 77 + ret = get_fwu_request(dev, &val); 78 + if (ret) 79 + return ret; 80 + 81 + return sprintf(buf, "%d\n", val); 82 + } 83 + 84 + static ssize_t firmware_update_request_store(struct device *dev, 85 + struct device_attribute *attr, 86 + const char *buf, size_t count) 87 + { 88 + unsigned int val; 89 + int ret; 90 + 91 + ret = kstrtouint(buf, 0, &val); 92 + if (ret) 93 + return ret; 94 + 95 + /* May later be extended to support values other than 0 and 1 */ 96 + if (val > 1) 97 + return -ERANGE; 98 + 99 + ret = set_fwu_request(dev, val); 100 + if (ret) 101 + return ret; 102 + 103 + return count; 104 + } 105 + static DEVICE_ATTR_RW(firmware_update_request); 106 + 107 + static struct attribute *firmware_update_attrs[] = { 108 + &dev_attr_firmware_update_request.attr, 109 + NULL 110 + }; 111 + ATTRIBUTE_GROUPS(firmware_update); 112 + 113 + static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev, 114 + const void *context) 115 + { 116 + dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n"); 117 + return 0; 118 + } 119 + 120 + static int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev) 121 + { 122 + dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n"); 123 + return 0; 124 + } 125 + 126 + static const struct wmi_device_id intel_wmi_sbl_id_table[] = { 127 + { .guid_string = INTEL_WMI_SBL_GUID }, 128 + {} 129 + }; 130 + MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table); 131 + 132 + static struct wmi_driver intel_wmi_sbl_fw_update_driver = { 133 + .driver = { 134 + .name = "intel-wmi-sbl-fw-update", 135 + .dev_groups = firmware_update_groups, 136 + }, 137 + .probe = intel_wmi_sbl_fw_update_probe, 138 + .remove = intel_wmi_sbl_fw_update_remove, 139 + .id_table = intel_wmi_sbl_id_table, 140 + }; 141 + module_wmi_driver(intel_wmi_sbl_fw_update_driver); 142 + 143 + MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>"); 144 + MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver"); 145 + MODULE_LICENSE("GPL v2");