18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Slim Bootloader(SBL) firmware update signaling driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware 68c2ecf20Sopenharmony_ci * optimized for running on certain Intel platforms. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>. 98c2ecf20Sopenharmony_ci * This driver further adds "firmware_update_request" device attribute. 108c2ecf20Sopenharmony_ci * This attribute normally has a value of 0 and userspace can signal SBL 118c2ecf20Sopenharmony_ci * to update firmware, on next reboot, by writing a value of 1. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * More details of SBL firmware update process is available at: 148c2ecf20Sopenharmony_ci * https://slimbootloader.github.io/security/firmware-update.html 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/wmi.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define INTEL_WMI_SBL_GUID "44FADEB1-B204-40F2-8581-394BBDC1B651" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int get_fwu_request(struct device *dev, u32 *out) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL}; 298c2ecf20Sopenharmony_ci union acpi_object *obj; 308c2ecf20Sopenharmony_ci acpi_status status; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result); 338c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 348c2ecf20Sopenharmony_ci dev_err(dev, "wmi_query_block failed\n"); 358c2ecf20Sopenharmony_ci return -ENODEV; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci obj = (union acpi_object *)result.pointer; 398c2ecf20Sopenharmony_ci if (!obj || obj->type != ACPI_TYPE_INTEGER) { 408c2ecf20Sopenharmony_ci dev_warn(dev, "wmi_query_block returned invalid value\n"); 418c2ecf20Sopenharmony_ci kfree(obj); 428c2ecf20Sopenharmony_ci return -EINVAL; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci *out = obj->integer.value; 468c2ecf20Sopenharmony_ci kfree(obj); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int set_fwu_request(struct device *dev, u32 in) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct acpi_buffer input; 548c2ecf20Sopenharmony_ci acpi_status status; 558c2ecf20Sopenharmony_ci u32 value; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci value = in; 588c2ecf20Sopenharmony_ci input.length = sizeof(u32); 598c2ecf20Sopenharmony_ci input.pointer = &value; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input); 628c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 638c2ecf20Sopenharmony_ci dev_err(dev, "wmi_set_block failed\n"); 648c2ecf20Sopenharmony_ci return -ENODEV; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic ssize_t firmware_update_request_show(struct device *dev, 718c2ecf20Sopenharmony_ci struct device_attribute *attr, 728c2ecf20Sopenharmony_ci char *buf) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u32 val; 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ret = get_fwu_request(dev, &val); 788c2ecf20Sopenharmony_ci if (ret) 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", val); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic ssize_t firmware_update_request_store(struct device *dev, 858c2ecf20Sopenharmony_ci struct device_attribute *attr, 868c2ecf20Sopenharmony_ci const char *buf, size_t count) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned int val; 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ret = kstrtouint(buf, 0, &val); 928c2ecf20Sopenharmony_ci if (ret) 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* May later be extended to support values other than 0 and 1 */ 968c2ecf20Sopenharmony_ci if (val > 1) 978c2ecf20Sopenharmony_ci return -ERANGE; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = set_fwu_request(dev, val); 1008c2ecf20Sopenharmony_ci if (ret) 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return count; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(firmware_update_request); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic struct attribute *firmware_update_attrs[] = { 1088c2ecf20Sopenharmony_ci &dev_attr_firmware_update_request.attr, 1098c2ecf20Sopenharmony_ci NULL 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(firmware_update); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev, 1148c2ecf20Sopenharmony_ci const void *context) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n"); 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n"); 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const struct wmi_device_id intel_wmi_sbl_id_table[] = { 1278c2ecf20Sopenharmony_ci { .guid_string = INTEL_WMI_SBL_GUID }, 1288c2ecf20Sopenharmony_ci {} 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct wmi_driver intel_wmi_sbl_fw_update_driver = { 1338c2ecf20Sopenharmony_ci .driver = { 1348c2ecf20Sopenharmony_ci .name = "intel-wmi-sbl-fw-update", 1358c2ecf20Sopenharmony_ci .dev_groups = firmware_update_groups, 1368c2ecf20Sopenharmony_ci }, 1378c2ecf20Sopenharmony_ci .probe = intel_wmi_sbl_fw_update_probe, 1388c2ecf20Sopenharmony_ci .remove = intel_wmi_sbl_fw_update_remove, 1398c2ecf20Sopenharmony_ci .id_table = intel_wmi_sbl_id_table, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_cimodule_wmi_driver(intel_wmi_sbl_fw_update_driver); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>"); 1448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver"); 1458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 146