sbl-fw-update.c (3601B)
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 26static 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 51static 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 70static 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 84static 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} 105static DEVICE_ATTR_RW(firmware_update_request); 106 107static struct attribute *firmware_update_attrs[] = { 108 &dev_attr_firmware_update_request.attr, 109 NULL 110}; 111ATTRIBUTE_GROUPS(firmware_update); 112 113static 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 120static void intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev) 121{ 122 dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n"); 123} 124 125static const struct wmi_device_id intel_wmi_sbl_id_table[] = { 126 { .guid_string = INTEL_WMI_SBL_GUID }, 127 {} 128}; 129MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table); 130 131static struct wmi_driver intel_wmi_sbl_fw_update_driver = { 132 .driver = { 133 .name = "intel-wmi-sbl-fw-update", 134 .dev_groups = firmware_update_groups, 135 }, 136 .probe = intel_wmi_sbl_fw_update_probe, 137 .remove = intel_wmi_sbl_fw_update_remove, 138 .id_table = intel_wmi_sbl_id_table, 139}; 140module_wmi_driver(intel_wmi_sbl_fw_update_driver); 141 142MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>"); 143MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver"); 144MODULE_LICENSE("GPL v2");