From 0db89fa243e5edc5de38c88b369e4c3755c5fb74 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 22 Dec 2021 12:31:41 +0800 Subject: ACPI: Introduce Platform Firmware Runtime Update device driver Introduce the pfr_update driver which can be used for Platform Firmware Runtime code injection and driver update [1]. The user is expected to provide the EFI capsule, and pass it to the driver by writing the capsule to a device special file. The capsule is transferred by the driver to the platform firmware with the help of an ACPI _DSM method under the special ACPI Platform Firmware Runtime Update device (INTC1080), and the actual firmware update is carried out by the low-level Management Mode code in the platform firmware. This change allows certain pieces of the platform firmware to be updated on the fly while the system is running (runtime) without the need to restart it, which is key in the cases when the system needs to be available 100% of the time and it cannot afford the downtime related to restarting it, or when the work carried out by the system is particularly important, so it cannot be interrupted, and it is not practical to wait until it is complete. Link: https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf # [1] Tested-by: Hongyu Ning Signed-off-by: Chen Yu [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 18 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pfr_update.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 594 insertions(+) create mode 100644 drivers/acpi/pfr_update.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index cdbdf68bd98f..d0b3ca9d4a97 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -517,6 +517,24 @@ config ACPI_CONFIGFS userspace. The configurable ACPI groups will be visible under /config/acpi, assuming configfs is mounted under /config. +config ACPI_PFRUT + tristate "ACPI Platform Firmware Runtime Update and Telemetry" + depends on 64BIT + help + This mechanism allows certain pieces of the platform firmware + to be updated on the fly while the system is running (runtime) + without the need to restart it, which is key in the cases when + the system needs to be available 100% of the time and it cannot + afford the downtime related to restarting it, or when the work + carried out by the system is particularly important, so it cannot + be interrupted, and it is not practical to wait until it is complete. + + The existing firmware code can be modified (driver update) or + extended by adding new code to the firmware (code injection). + + To compile this driver as module, choose M here: + the module will be called pfr_update. + if ARM64 source "drivers/acpi/arm64/Kconfig" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3018714e87d9..2ad2e821cc08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o obj-$(CONFIG_ACPI_PPTT) += pptt.o +obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c new file mode 100644 index 000000000000..149b5b2530b9 --- /dev/null +++ b/drivers/acpi/pfr_update.c @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI Platform Firmware Runtime Update Device driver + * + * Copyright (C) 2021 Intel Corporation + * Author: Chen Yu + * + * pfr_update driver is used for Platform Firmware Runtime + * Update, which includes the code injection and driver update. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PFRU_FUNC_STANDARD_QUERY 0 +#define PFRU_FUNC_QUERY_UPDATE_CAP 1 +#define PFRU_FUNC_QUERY_BUF 2 +#define PFRU_FUNC_START 3 + +#define PFRU_CODE_INJECT_TYPE 1 +#define PFRU_DRIVER_UPDATE_TYPE 2 + +#define PFRU_REVID_1 1 +#define PFRU_REVID_2 2 +#define PFRU_DEFAULT_REV_ID PFRU_REVID_1 + +enum cap_index { + CAP_STATUS_IDX = 0, + CAP_UPDATE_IDX = 1, + CAP_CODE_TYPE_IDX = 2, + CAP_FW_VER_IDX = 3, + CAP_CODE_RT_VER_IDX = 4, + CAP_DRV_TYPE_IDX = 5, + CAP_DRV_RT_VER_IDX = 6, + CAP_DRV_SVN_IDX = 7, + CAP_PLAT_ID_IDX = 8, + CAP_OEM_ID_IDX = 9, + CAP_OEM_INFO_IDX = 10, + CAP_NR_IDX +}; + +enum buf_index { + BUF_STATUS_IDX = 0, + BUF_EXT_STATUS_IDX = 1, + BUF_ADDR_LOW_IDX = 2, + BUF_ADDR_HI_IDX = 3, + BUF_SIZE_IDX = 4, + BUF_NR_IDX +}; + +enum update_index { + UPDATE_STATUS_IDX = 0, + UPDATE_EXT_STATUS_IDX = 1, + UPDATE_AUTH_TIME_LOW_IDX = 2, + UPDATE_AUTH_TIME_HI_IDX = 3, + UPDATE_EXEC_TIME_LOW_IDX = 4, + UPDATE_EXEC_TIME_HI_IDX = 5, + UPDATE_NR_IDX +}; + +enum pfru_start_action { + START_STAGE = 0, + START_ACTIVATE = 1, + START_STAGE_ACTIVATE = 2, +}; + +struct pfru_device { + u32 rev_id, index; + struct device *parent_dev; + struct miscdevice miscdev; +}; + +static DEFINE_IDA(pfru_ida); + +/* + * Manual reference: + * https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf + * + * pfru_guid is the parameter for _DSM method + */ +static const guid_t pfru_guid = + GUID_INIT(0xECF9533B, 0x4A3C, 0x4E89, 0x93, 0x9E, 0xC7, 0x71, + 0x12, 0x60, 0x1C, 0x6D); + +/* pfru_code_inj_guid is the UUID to identify code injection EFI capsule file */ +static const guid_t pfru_code_inj_guid = + GUID_INIT(0xB2F84B79, 0x7B6E, 0x4E45, 0x88, 0x5F, 0x3F, 0xB9, + 0xBB, 0x18, 0x54, 0x02); + +/* pfru_drv_update_guid is the UUID to identify driver update EFI capsule file */ +static const guid_t pfru_drv_update_guid = + GUID_INIT(0x4569DD8C, 0x75F1, 0x429A, 0xA3, 0xD6, 0x24, 0xDE, + 0x80, 0x97, 0xA0, 0xDF); + +static inline int pfru_valid_revid(u32 id) +{ + return id == PFRU_REVID_1 || id == PFRU_REVID_2; +} + +static inline struct pfru_device *to_pfru_dev(struct file *file) +{ + return container_of(file->private_data, struct pfru_device, miscdev); +} + +static int query_capability(struct pfru_update_cap_info *cap_hdr, + struct pfru_device *pfru_dev) +{ + acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); + union acpi_object *out_obj; + int ret = -EINVAL; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, + pfru_dev->rev_id, + PFRU_FUNC_QUERY_UPDATE_CAP, + NULL, ACPI_TYPE_PACKAGE); + if (!out_obj) + return ret; + + if (out_obj->package.count < CAP_NR_IDX || + out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_UPDATE_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_CODE_TYPE_IDX].type != ACPI_TYPE_BUFFER || + out_obj->package.elements[CAP_FW_VER_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_CODE_RT_VER_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_DRV_TYPE_IDX].type != ACPI_TYPE_BUFFER || + out_obj->package.elements[CAP_DRV_RT_VER_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER || + out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER || + out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) + goto free_acpi_buffer; + + cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; + if (cap_hdr->status != DSM_SUCCEED) { + ret = -EBUSY; + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status); + goto free_acpi_buffer; + } + + cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value; + memcpy(&cap_hdr->code_type, + out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer, + out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length); + cap_hdr->fw_version = + out_obj->package.elements[CAP_FW_VER_IDX].integer.value; + cap_hdr->code_rt_version = + out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value; + memcpy(&cap_hdr->drv_type, + out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer, + out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length); + cap_hdr->drv_rt_version = + out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value; + cap_hdr->drv_svn = + out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value; + memcpy(&cap_hdr->platform_id, + out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer, + out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length); + memcpy(&cap_hdr->oem_id, + out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer, + out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length); + cap_hdr->oem_info_len = + out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length; + + ret = 0; + +free_acpi_buffer: + kfree(out_obj); + + return ret; +} + +static int query_buffer(struct pfru_com_buf_info *info, + struct pfru_device *pfru_dev) +{ + acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); + union acpi_object *out_obj; + int ret = -EINVAL; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, + pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF, + NULL, ACPI_TYPE_PACKAGE); + if (!out_obj) + return ret; + + if (out_obj->package.count < BUF_NR_IDX || + out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value; + info->ext_status = + out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value; + if (info->status != DSM_SUCCEED) { + ret = -EBUSY; + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status); + dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status); + + goto free_acpi_buffer; + } + + info->addr_lo = + out_obj->package.elements[BUF_ADDR_LOW_IDX].integer.value; + info->addr_hi = + out_obj->package.elements[BUF_ADDR_HI_IDX].integer.value; + info->buf_size = out_obj->package.elements[BUF_SIZE_IDX].integer.value; + + ret = 0; + +free_acpi_buffer: + kfree(out_obj); + + return ret; +} + +static int get_image_type(const struct efi_manage_capsule_image_header *img_hdr, + struct pfru_device *pfru_dev) +{ + const efi_guid_t *image_type_id = &img_hdr->image_type_id; + + /* check whether this is a code injection or driver update */ + if (guid_equal(image_type_id, &pfru_code_inj_guid)) + return PFRU_CODE_INJECT_TYPE; + + if (guid_equal(image_type_id, &pfru_drv_update_guid)) + return PFRU_DRIVER_UPDATE_TYPE; + + return -EINVAL; +} + +static int adjust_efi_size(const struct efi_manage_capsule_image_header *img_hdr, + int size) +{ + /* + * The (u64 hw_ins) was introduced in UEFI spec version 2, + * and (u64 capsule_support) was introduced in version 3. + * The size needs to be adjusted accordingly. That is to + * say, version 1 should subtract the size of hw_ins+capsule_support, + * and version 2 should sbstract the size of capsule_support. + */ + size += sizeof(struct efi_manage_capsule_image_header); + switch (img_hdr->ver) { + case 1: + return size - 2 * sizeof(u64); + + case 2: + return size - sizeof(u64); + + default: + /* only support version 1 and 2 */ + return -EINVAL; + } +} + +static bool applicable_image(const void *data, struct pfru_update_cap_info *cap, + struct pfru_device *pfru_dev) +{ + struct pfru_payload_hdr *payload_hdr; + const efi_capsule_header_t *cap_hdr = data; + const struct efi_manage_capsule_header *m_hdr; + const struct efi_manage_capsule_image_header *m_img_hdr; + const struct efi_image_auth *auth; + int type, size; + + /* + * If the code in the capsule is older than the current + * firmware code, the update will be rejected by the firmware, + * so check the version of it upfront without engaging the + * Management Mode update mechanism which may be costly. + */ + size = cap_hdr->headersize; + m_hdr = data + size; + /* + * Current data structure size plus variable array indicated + * by number of (emb_drv_cnt + payload_cnt) + */ + size += offsetof(struct efi_manage_capsule_header, offset_list) + + (m_hdr->emb_drv_cnt + m_hdr->payload_cnt) * sizeof(u64); + m_img_hdr = data + size; + + type = get_image_type(m_img_hdr, pfru_dev); + if (type < 0) + return false; + + size = adjust_efi_size(m_img_hdr, size); + if (size < 0) + return false; + + auth = data + size; + size += sizeof(u64) + auth->auth_info.hdr.len; + payload_hdr = (struct pfru_payload_hdr *)(data + size); + + /* finally compare the version */ + if (type == PFRU_CODE_INJECT_TYPE) + return payload_hdr->rt_ver >= cap->code_rt_version; + + return payload_hdr->rt_ver >= cap->drv_rt_version; +} + +static void print_update_debug_info(struct pfru_updated_result *result, + struct pfru_device *pfru_dev) +{ + dev_dbg(pfru_dev->parent_dev, "Update result:\n"); + dev_dbg(pfru_dev->parent_dev, "Authentication Time Low:%lld\n", + result->low_auth_time); + dev_dbg(pfru_dev->parent_dev, "Authentication Time High:%lld\n", + result->high_auth_time); + dev_dbg(pfru_dev->parent_dev, "Execution Time Low:%lld\n", + result->low_exec_time); + dev_dbg(pfru_dev->parent_dev, "Execution Time High:%lld\n", + result->high_exec_time); +} + +static int start_update(int action, struct pfru_device *pfru_dev) +{ + union acpi_object *out_obj, in_obj, in_buf; + struct pfru_updated_result update_result; + acpi_handle handle; + int ret = -EINVAL; + + memset(&in_obj, 0, sizeof(in_obj)); + memset(&in_buf, 0, sizeof(in_buf)); + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = 1; + in_obj.package.elements = &in_buf; + in_buf.type = ACPI_TYPE_INTEGER; + in_buf.integer.value = action; + + handle = ACPI_HANDLE(pfru_dev->parent_dev); + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, + pfru_dev->rev_id, PFRU_FUNC_START, + &in_obj, ACPI_TYPE_PACKAGE); + if (!out_obj) + return ret; + + if (out_obj->package.count < UPDATE_NR_IDX || + out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[UPDATE_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + update_result.status = + out_obj->package.elements[UPDATE_STATUS_IDX].integer.value; + update_result.ext_status = + out_obj->package.elements[UPDATE_EXT_STATUS_IDX].integer.value; + + if (update_result.status != DSM_SUCCEED) { + ret = -EBUSY; + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status); + dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", + update_result.ext_status); + + goto free_acpi_buffer; + } + + update_result.low_auth_time = + out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].integer.value; + update_result.high_auth_time = + out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].integer.value; + update_result.low_exec_time = + out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].integer.value; + update_result.high_exec_time = + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].integer.value; + + print_update_debug_info(&update_result, pfru_dev); + ret = 0; + +free_acpi_buffer: + kfree(out_obj); + + return ret; +} + +static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pfru_update_cap_info cap_hdr; + struct pfru_device *pfru_dev = to_pfru_dev(file); + void __user *p = (void __user *)arg; + u32 rev; + int ret; + + switch (cmd) { + case PFRU_IOC_QUERY_CAP: + ret = query_capability(&cap_hdr, pfru_dev); + if (ret) + return ret; + + if (copy_to_user(p, &cap_hdr, sizeof(cap_hdr))) + return -EFAULT; + + return 0; + + case PFRU_IOC_SET_REV: + if (copy_from_user(&rev, p, sizeof(rev))) + return -EFAULT; + + if (!pfru_valid_revid(rev)) + return -EINVAL; + + pfru_dev->rev_id = rev; + + return 0; + + case PFRU_IOC_STAGE: + return start_update(START_STAGE, pfru_dev); + + case PFRU_IOC_ACTIVATE: + return start_update(START_ACTIVATE, pfru_dev); + + case PFRU_IOC_STAGE_ACTIVATE: + return start_update(START_STAGE_ACTIVATE, pfru_dev); + + default: + return -ENOTTY; + } +} + +static ssize_t pfru_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct pfru_device *pfru_dev = to_pfru_dev(file); + struct pfru_update_cap_info cap; + struct pfru_com_buf_info buf_info; + phys_addr_t phy_addr; + struct iov_iter iter; + struct iovec iov; + char *buf_ptr; + int ret; + + ret = query_buffer(&buf_info, pfru_dev); + if (ret) + return ret; + + if (len > buf_info.buf_size) + return -EINVAL; + + iov.iov_base = (void __user *)buf; + iov.iov_len = len; + iov_iter_init(&iter, WRITE, &iov, 1, len); + + /* map the communication buffer */ + phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); + buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); + if (IS_ERR(buf_ptr)) + return PTR_ERR(buf_ptr); + + if (!copy_from_iter_full(buf_ptr, len, &iter)) { + ret = -EINVAL; + goto unmap; + } + + /* check if the capsule header has a valid version number */ + ret = query_capability(&cap, pfru_dev); + if (ret) + goto unmap; + + if (!applicable_image(buf_ptr, &cap, pfru_dev)) + ret = -EINVAL; + +unmap: + memunmap(buf_ptr); + + return ret ?: len; +} + +static const struct file_operations acpi_pfru_fops = { + .owner = THIS_MODULE, + .write = pfru_write, + .unlocked_ioctl = pfru_ioctl, + .llseek = noop_llseek, +}; + +static int acpi_pfru_remove(struct platform_device *pdev) +{ + struct pfru_device *pfru_dev = platform_get_drvdata(pdev); + + misc_deregister(&pfru_dev->miscdev); + + return 0; +} + +static void pfru_put_idx(void *data) +{ + struct pfru_device *pfru_dev = data; + + ida_free(&pfru_ida, pfru_dev->index); +} + +static int acpi_pfru_probe(struct platform_device *pdev) +{ + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + struct pfru_device *pfru_dev; + int ret; + + if (!acpi_has_method(handle, "_DSM")) { + dev_dbg(&pdev->dev, "Missing _DSM\n"); + return -ENODEV; + } + + pfru_dev = devm_kzalloc(&pdev->dev, sizeof(*pfru_dev), GFP_KERNEL); + if (!pfru_dev) + return -ENOMEM; + + ret = ida_alloc(&pfru_ida, GFP_KERNEL); + if (ret < 0) + return ret; + + pfru_dev->index = ret; + ret = devm_add_action_or_reset(&pdev->dev, pfru_put_idx, pfru_dev); + if (ret) + return ret; + + pfru_dev->rev_id = PFRU_DEFAULT_REV_ID; + pfru_dev->parent_dev = &pdev->dev; + + pfru_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + pfru_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pfru%d", pfru_dev->index); + if (!pfru_dev->miscdev.name) + return -ENOMEM; + + pfru_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "acpi_pfr_update%d", pfru_dev->index); + if (!pfru_dev->miscdev.nodename) + return -ENOMEM; + + pfru_dev->miscdev.fops = &acpi_pfru_fops; + pfru_dev->miscdev.parent = &pdev->dev; + + ret = misc_register(&pfru_dev->miscdev); + if (ret) + return ret; + + platform_set_drvdata(pdev, pfru_dev); + + return 0; +} + +static const struct acpi_device_id acpi_pfru_ids[] = { + {"INTC1080"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_pfru_ids); + +static struct platform_driver acpi_pfru_driver = { + .driver = { + .name = "pfr_update", + .acpi_match_table = acpi_pfru_ids, + }, + .probe = acpi_pfru_probe, + .remove = acpi_pfru_remove, +}; +module_platform_driver(acpi_pfru_driver); + +MODULE_DESCRIPTION("Platform Firmware Runtime Update device driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b0013e037a8b07772c74ce24f1ae4743b30fc3cf Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 22 Dec 2021 12:32:02 +0800 Subject: ACPI: Introduce Platform Firmware Runtime Telemetry driver This driver allows user space to fetch telemetry data from the firmware with the help of the Platform Firmware Runtime Telemetry interface. Both PFRU and PFRT are based on ACPI _DSM interfaces located under special device objects in the ACPI Namespace, but these interfaces are different from each other, so it is better to provide a separate driver from each of them, even though they share some common definitions and naming conventions. Tested-by: Hongyu Ning Signed-off-by: Chen Yu [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 8 +- drivers/acpi/Makefile | 2 +- drivers/acpi/pfr_telemetry.c | 434 +++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/pfrut.h | 88 +++++++++ 4 files changed, 529 insertions(+), 3 deletions(-) create mode 100644 drivers/acpi/pfr_telemetry.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index d0b3ca9d4a97..91f1da16934d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -532,8 +532,12 @@ config ACPI_PFRUT The existing firmware code can be modified (driver update) or extended by adding new code to the firmware (code injection). - To compile this driver as module, choose M here: - the module will be called pfr_update. + Besides, the telemetry driver allows user space to fetch telemetry + data from the firmware with the help of the Platform Firmware Runtime + Telemetry interface. + + To compile the drivers as modules, choose M here: + the modules will be called pfr_update and pfr_telemetry. if ARM64 source "drivers/acpi/arm64/Kconfig" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 2ad2e821cc08..d3dc79298ce3 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -102,7 +102,7 @@ obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o obj-$(CONFIG_ACPI_PPTT) += pptt.o -obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o +obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o pfr_telemetry.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c new file mode 100644 index 000000000000..da50dd80192c --- /dev/null +++ b/drivers/acpi/pfr_telemetry.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI Platform Firmware Runtime Telemetry driver + * + * Copyright (C) 2021 Intel Corporation + * Author: Chen Yu + * + * This driver allows user space to fetch telemetry data from the + * firmware with the help of the Platform Firmware Runtime Telemetry + * interface. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PFRT_LOG_EXEC_IDX 0 +#define PFRT_LOG_HISTORY_IDX 1 + +#define PFRT_LOG_ERR 0 +#define PFRT_LOG_WARN 1 +#define PFRT_LOG_INFO 2 +#define PFRT_LOG_VERB 4 + +#define PFRT_FUNC_SET_LEV 1 +#define PFRT_FUNC_GET_LEV 2 +#define PFRT_FUNC_GET_DATA 3 + +#define PFRT_REVID_1 1 +#define PFRT_REVID_2 2 +#define PFRT_DEFAULT_REV_ID PFRT_REVID_1 + +enum log_index { + LOG_STATUS_IDX = 0, + LOG_EXT_STATUS_IDX = 1, + LOG_MAX_SZ_IDX = 2, + LOG_CHUNK1_LO_IDX = 3, + LOG_CHUNK1_HI_IDX = 4, + LOG_CHUNK1_SZ_IDX = 5, + LOG_CHUNK2_LO_IDX = 6, + LOG_CHUNK2_HI_IDX = 7, + LOG_CHUNK2_SZ_IDX = 8, + LOG_ROLLOVER_CNT_IDX = 9, + LOG_RESET_CNT_IDX = 10, + LOG_NR_IDX +}; + +struct pfrt_log_device { + int index; + struct pfrt_log_info info; + struct device *parent_dev; + struct miscdevice miscdev; +}; + +/* pfrt_guid is the parameter for _DSM method */ +static const guid_t pfrt_log_guid = + GUID_INIT(0x75191659, 0x8178, 0x4D9D, 0xB8, 0x8F, 0xAC, 0x5E, + 0x5E, 0x93, 0xE8, 0xBF); + +static DEFINE_IDA(pfrt_log_ida); + +static inline struct pfrt_log_device *to_pfrt_log_dev(struct file *file) +{ + return container_of(file->private_data, struct pfrt_log_device, miscdev); +} + +static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info, + struct pfrt_log_device *pfrt_log_dev) +{ + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); + union acpi_object *out_obj, in_obj, in_buf; + int ret = -EBUSY; + + memset(&in_obj, 0, sizeof(in_obj)); + memset(&in_buf, 0, sizeof(in_buf)); + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = 1; + in_obj.package.elements = &in_buf; + in_buf.type = ACPI_TYPE_INTEGER; + in_buf.integer.value = pfrt_log_dev->info.log_type; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, + pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_DATA, + &in_obj, ACPI_TYPE_PACKAGE); + if (!out_obj) + return -EINVAL; + + if (out_obj->package.count < LOG_NR_IDX || + out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER || + out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value; + data_info->ext_status = + out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value; + if (data_info->status != DSM_SUCCEED) { + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", data_info->status); + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", + data_info->ext_status); + goto free_acpi_buffer; + } + + data_info->max_data_size = + out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value; + data_info->chunk1_addr_lo = + out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value; + data_info->chunk1_addr_hi = + out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value; + data_info->chunk1_size = + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value; + data_info->chunk2_addr_lo = + out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value; + data_info->chunk2_addr_hi = + out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value; + data_info->chunk2_size = + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value; + data_info->rollover_cnt = + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value; + data_info->reset_cnt = + out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value; + + ret = 0; + +free_acpi_buffer: + kfree(out_obj); + + return ret; +} + +static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev) +{ + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); + union acpi_object *out_obj, *obj, in_obj, in_buf; + enum pfru_dsm_status status, ext_status; + int ret = 0; + + memset(&in_obj, 0, sizeof(in_obj)); + memset(&in_buf, 0, sizeof(in_buf)); + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = 1; + in_obj.package.elements = &in_buf; + in_buf.type = ACPI_TYPE_INTEGER; + in_buf.integer.value = level; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, + pfrt_log_dev->info.log_revid, PFRT_FUNC_SET_LEV, + &in_obj, ACPI_TYPE_PACKAGE); + if (!out_obj) + return -EINVAL; + + obj = &out_obj->package.elements[0]; + status = obj->integer.value; + if (status != DSM_SUCCEED) { + obj = &out_obj->package.elements[1]; + ext_status = obj->integer.value; + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); + ret = -EBUSY; + } + + kfree(out_obj); + + return ret; +} + +static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev) +{ + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); + union acpi_object *out_obj, *obj; + enum pfru_dsm_status status, ext_status; + int ret = -EBUSY; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, + pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_LEV, + NULL, ACPI_TYPE_PACKAGE); + if (!out_obj) + return -EINVAL; + + obj = &out_obj->package.elements[0]; + if (obj->type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + status = obj->integer.value; + if (status != DSM_SUCCEED) { + obj = &out_obj->package.elements[1]; + ext_status = obj->integer.value; + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); + goto free_acpi_buffer; + } + + obj = &out_obj->package.elements[2]; + if (obj->type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + ret = obj->integer.value; + +free_acpi_buffer: + kfree(out_obj); + + return ret; +} + +static int valid_log_level(u32 level) +{ + return level == PFRT_LOG_ERR || level == PFRT_LOG_WARN || + level == PFRT_LOG_INFO || level == PFRT_LOG_VERB; +} + +static int valid_log_type(u32 type) +{ + return type == PFRT_LOG_EXEC_IDX || type == PFRT_LOG_HISTORY_IDX; +} + +static inline int valid_log_revid(u32 id) +{ + return id == PFRT_REVID_1 || id == PFRT_REVID_2; +} + +static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pfrt_log_device *pfrt_log_dev = to_pfrt_log_dev(file); + struct pfrt_log_data_info data_info; + struct pfrt_log_info info; + void __user *p; + int ret = 0; + + p = (void __user *)arg; + + switch (cmd) { + case PFRT_LOG_IOC_SET_INFO: + if (copy_from_user(&info, p, sizeof(info))) + return -EFAULT; + + if (valid_log_revid(info.log_revid)) + pfrt_log_dev->info.log_revid = info.log_revid; + + if (valid_log_level(info.log_level)) { + ret = set_pfrt_log_level(info.log_level, pfrt_log_dev); + if (ret < 0) + return ret; + + pfrt_log_dev->info.log_level = info.log_level; + } + + if (valid_log_type(info.log_type)) + pfrt_log_dev->info.log_type = info.log_type; + + return 0; + + case PFRT_LOG_IOC_GET_INFO: + info.log_level = get_pfrt_log_level(pfrt_log_dev); + if (ret < 0) + return ret; + + info.log_type = pfrt_log_dev->info.log_type; + info.log_revid = pfrt_log_dev->info.log_revid; + if (copy_to_user(p, &info, sizeof(info))) + return -EFAULT; + + return 0; + + case PFRT_LOG_IOC_GET_DATA_INFO: + ret = get_pfrt_log_data_info(&data_info, pfrt_log_dev); + if (ret) + return ret; + + if (copy_to_user(p, &data_info, sizeof(struct pfrt_log_data_info))) + return -EFAULT; + + return 0; + + default: + return -ENOTTY; + } +} + +static int +pfrt_log_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct pfrt_log_device *pfrt_log_dev; + struct pfrt_log_data_info info; + unsigned long psize, vsize; + phys_addr_t base_addr; + int ret; + + if (vma->vm_flags & VM_WRITE) + return -EROFS; + + /* changing from read to write with mprotect is not allowed */ + vma->vm_flags &= ~VM_MAYWRITE; + + pfrt_log_dev = to_pfrt_log_dev(file); + + ret = get_pfrt_log_data_info(&info, pfrt_log_dev); + if (ret) + return ret; + + base_addr = (phys_addr_t)((info.chunk2_addr_hi << 32) | info.chunk2_addr_lo); + /* pfrt update has not been launched yet */ + if (!base_addr) + return -ENODEV; + + psize = info.max_data_size; + /* base address and total buffer size must be page aligned */ + if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize)) + return -ENODEV; + + vsize = vma->vm_end - vma->vm_start; + if (vsize > psize) + return -EINVAL; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(base_addr), + vsize, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static const struct file_operations acpi_pfrt_log_fops = { + .owner = THIS_MODULE, + .mmap = pfrt_log_mmap, + .unlocked_ioctl = pfrt_log_ioctl, + .llseek = noop_llseek, +}; + +static int acpi_pfrt_log_remove(struct platform_device *pdev) +{ + struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev); + + misc_deregister(&pfrt_log_dev->miscdev); + + return 0; +} + +static void pfrt_log_put_idx(void *data) +{ + struct pfrt_log_device *pfrt_log_dev = data; + + ida_free(&pfrt_log_ida, pfrt_log_dev->index); +} + +static int acpi_pfrt_log_probe(struct platform_device *pdev) +{ + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + struct pfrt_log_device *pfrt_log_dev; + int ret; + + if (!acpi_has_method(handle, "_DSM")) { + dev_dbg(&pdev->dev, "Missing _DSM\n"); + return -ENODEV; + } + + pfrt_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KERNEL); + if (!pfrt_log_dev) + return -ENOMEM; + + ret = ida_alloc(&pfrt_log_ida, GFP_KERNEL); + if (ret < 0) + return ret; + + pfrt_log_dev->index = ret; + ret = devm_add_action_or_reset(&pdev->dev, pfrt_log_put_idx, pfrt_log_dev); + if (ret) + return ret; + + pfrt_log_dev->info.log_revid = PFRT_DEFAULT_REV_ID; + pfrt_log_dev->parent_dev = &pdev->dev; + + pfrt_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + pfrt_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pfrt%d", + pfrt_log_dev->index); + if (!pfrt_log_dev->miscdev.name) + return -ENOMEM; + + pfrt_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "acpi_pfr_telemetry%d", + pfrt_log_dev->index); + if (!pfrt_log_dev->miscdev.nodename) + return -ENOMEM; + + pfrt_log_dev->miscdev.fops = &acpi_pfrt_log_fops; + pfrt_log_dev->miscdev.parent = &pdev->dev; + + ret = misc_register(&pfrt_log_dev->miscdev); + if (ret) + return ret; + + platform_set_drvdata(pdev, pfrt_log_dev); + + return 0; +} + +static const struct acpi_device_id acpi_pfrt_log_ids[] = { + {"INTC1081"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_pfrt_log_ids); + +static struct platform_driver acpi_pfrt_log_driver = { + .driver = { + .name = "pfr_telemetry", + .acpi_match_table = acpi_pfrt_log_ids, + }, + .probe = acpi_pfrt_log_probe, + .remove = acpi_pfrt_log_remove, +}; +module_platform_driver(acpi_pfrt_log_driver); + +MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/pfrut.h b/include/uapi/linux/pfrut.h index fa97e80a93b7..42fa15f8310d 100644 --- a/include/uapi/linux/pfrut.h +++ b/include/uapi/linux/pfrut.h @@ -171,4 +171,92 @@ struct pfru_updated_result { __u64 high_exec_time; }; +/** + * struct pfrt_log_data_info - Log Data from telemetry service. + * @status: Indicator of whether this update succeed. + * @ext_status: Implementation specific update result. + * @chunk1_addr_lo: Low 32bit physical address of the telemetry data chunk1 + * starting address. + * @chunk1_addr_hi: High 32bit physical address of the telemetry data chunk1 + * starting address. + * @chunk2_addr_lo: Low 32bit physical address of the telemetry data chunk2 + * starting address. + * @chunk2_addr_hi: High 32bit physical address of the telemetry data chunk2 + * starting address. + * @max_data_size: Maximum supported size of data of all data chunks combined. + * @chunk1_size: Data size in bytes of the telemetry data chunk1 buffer. + * @chunk2_size: Data size in bytes of the telemetry data chunk2 buffer. + * @rollover_cnt: Number of times telemetry data buffer is overwritten + * since telemetry buffer reset. + * @reset_cnt: Number of times telemetry services resets that results in + * rollover count and data chunk buffers are reset. + */ +struct pfrt_log_data_info { + __u32 status; + __u32 ext_status; + __u64 chunk1_addr_lo; + __u64 chunk1_addr_hi; + __u64 chunk2_addr_lo; + __u64 chunk2_addr_hi; + __u32 max_data_size; + __u32 chunk1_size; + __u32 chunk2_size; + __u32 rollover_cnt; + __u32 reset_cnt; +}; + +/** + * struct pfrt_log_info - Telemetry log information. + * @log_level: The telemetry log level. + * @log_type: The telemetry log type(history and execution). + * @log_revid: The telemetry log revision id. + */ +struct pfrt_log_info { + __u32 log_level; + __u32 log_type; + __u32 log_revid; +}; + +/** + * PFRT_LOG_IOC_SET_INFO - _IOW(PFRUT_IOCTL_MAGIC, 0x06, + * struct pfrt_log_info) + * + * Return: + * * 0 - success + * * -EFAULT - fail to get the setting parameter + * * -EINVAL - fail to set the log level + * + * Set the PFRT log level and log type. The input information is + * a struct pfrt_log_info. + */ +#define PFRT_LOG_IOC_SET_INFO _IOW(PFRUT_IOCTL_MAGIC, 0x06, struct pfrt_log_info) + +/** + * PFRT_LOG_IOC_GET_INFO - _IOR(PFRUT_IOCTL_MAGIC, 0x07, + * struct pfrt_log_info) + * + * Return: + * * 0 - success + * * -EINVAL - fail to get the log level + * * -EFAULT - fail to copy the result back to userspace + * + * Retrieve log level and log type of the telemetry. The information is + * a struct pfrt_log_info. + */ +#define PFRT_LOG_IOC_GET_INFO _IOR(PFRUT_IOCTL_MAGIC, 0x07, struct pfrt_log_info) + +/** + * PFRT_LOG_IOC_GET_DATA_INFO - _IOR(PFRUT_IOCTL_MAGIC, 0x08, + * struct pfrt_log_data_info) + * + * Return: + * * 0 - success + * * -EINVAL - fail to get the log buffer information + * * -EFAULT - fail to copy the log buffer information to userspace + * + * Retrieve data information about the telemetry. The information + * is a struct pfrt_log_data_info. + */ +#define PFRT_LOG_IOC_GET_DATA_INFO _IOR(PFRUT_IOCTL_MAGIC, 0x08, struct pfrt_log_data_info) + #endif /* __PFRUT_H__ */ -- cgit v1.2.3 From 31834aaa4e2a26d8d1f6b36703bb35cfdb8fc98c Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 6 Jan 2022 15:54:48 +0800 Subject: ACPI: pfr_update: Fix return value check in pfru_write() In case of error, memremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 0db89fa243e5 ("ACPI: Introduce Platform Firmware Runtime Update device driver") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Acked-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pfr_update.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 149b5b2530b9..6bb0b778b5da 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -460,8 +460,8 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, /* map the communication buffer */ phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); - if (IS_ERR(buf_ptr)) - return PTR_ERR(buf_ptr); + if (!buf_ptr) + return -ENOMEM; if (!copy_from_iter_full(buf_ptr, len, &iter)) { ret = -EINVAL; -- cgit v1.2.3 From 7bf2e4d5ca1c94a9b0f730498b3d01768a72dcbd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Jan 2022 10:34:07 +0300 Subject: ACPI: pfr_telemetry: Fix info leak in pfrt_log_ioctl() The "data_info" struct is copied to the user. It has a 4 byte struct hole after the last struct member so we need to memset that to avoid copying uninitialized stack data to the user. Fixes: b0013e037a8b ("ACPI: Introduce Platform Firmware Runtime Telemetry driver") Signed-off-by: Dan Carpenter Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pfr_telemetry.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c index da50dd80192c..9abf350bd7a5 100644 --- a/drivers/acpi/pfr_telemetry.c +++ b/drivers/acpi/pfr_telemetry.c @@ -83,6 +83,7 @@ static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info, union acpi_object *out_obj, in_obj, in_buf; int ret = -EBUSY; + memset(data_info, 0, sizeof(*data_info)); memset(&in_obj, 0, sizeof(in_obj)); memset(&in_buf, 0, sizeof(in_buf)); in_obj.type = ACPI_TYPE_PACKAGE; -- cgit v1.2.3 From 2cea3ec5b0099d0e9dd6752aa86e08bce38d6b32 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Fri, 7 Jan 2022 11:35:16 +0800 Subject: ACPI: APD: Check for NULL pointer after calling devm_ioremap() Because devres_alloc() may fail, devm_ioremap() may return NULL. Then, 'clk_data->base' will be assigned to clkdev->data->base in platform_device_register_data(). The PTR_ERR_OR_ZERO() check on clk_data does not cover 'base', so it is better to add an explicit check against NULL after updating it. Fixes: 3f4ba94e3615 ("ACPI: APD: Add AMD misc clock handler support") Signed-off-by: Jiasheng Jiang [ rjw: Changelog rewrite ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_apd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 6e02448d15d9..9db6409ecb47 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -95,6 +95,8 @@ static int fch_misc_setup(struct apd_private_data *pdata) resource_size(rentry->res)); break; } + if (!clk_data->base) + return -ENOMEM; acpi_dev_free_resource_list(&resource_list); -- cgit v1.2.3 From ee3fe99ff0a27108ac38d9766ac0e92f5ec35692 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Wed, 5 Jan 2022 11:47:14 -0600 Subject: ACPI: SPCR: check if table->serial_port.access_width is too wide If table->serial_port.access_width is more than 29, it causes undefined behavior when ACPI_ACCESS_BIT_WIDTH shifts it to (1 << ((size) + 2)): [ 0.000000] UBSAN: Undefined behaviour in drivers/acpi/spcr.c:114:11 [ 0.000000] shift exponent 102 is too large for 32-bit type 'int' Use the new ACPI_ACCESS_ defines to test that serial_port.access_width is less than 30 and set it to 6 if it is not. Signed-off-by: Mark Langsdorf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/spcr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c index 25c2d0be953e..d589543875b8 100644 --- a/drivers/acpi/spcr.c +++ b/drivers/acpi/spcr.c @@ -107,8 +107,13 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console) pr_info("SPCR table version %d\n", table->header.revision); if (table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { - switch (ACPI_ACCESS_BIT_WIDTH(( - table->serial_port.access_width))) { + u32 bit_width = table->serial_port.access_width; + + if (bit_width > ACPI_ACCESS_BIT_MAX) { + pr_err("Unacceptable wide SPCR Access Width. Defaulting to byte size\n"); + bit_width = ACPI_ACCESS_BIT_DEFAULT; + } + switch (ACPI_ACCESS_BIT_WIDTH((bit_width))) { default: pr_err("Unexpected SPCR Access Width. Defaulting to byte size\n"); fallthrough; -- cgit v1.2.3 From b6c55b162bcee62c43c18e59f38a4590be543032 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Jan 2022 17:50:22 +0100 Subject: ACPI: scan: Change acpi_scan_init() return value type to void The only caller of acpi_scan_init(), acpi_init(), doesn't check its return value, so turn it into a void function. This avoids complaints from the Smatch static checker that the function should return a negative error code when it fails, which is not really a problem in this particular case. No intentional functional impact. Link: https://lore.kernel.org/linux-acpi/20220106082317.GA9123@kili/ Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/internal.h | 2 +- drivers/acpi/scan.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 1db3a2f81763..457e11d851b8 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -14,7 +14,7 @@ int early_acpi_osi_init(void); int acpi_osi_init(void); acpi_status acpi_os_initialize1(void); -int acpi_scan_init(void); +void acpi_scan_init(void); #ifdef CONFIG_PCI void acpi_pci_root_init(void); void acpi_pci_link_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1185ecea59d1..7e7417f4df22 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2542,9 +2542,8 @@ static void __init acpi_get_spcr_uart_addr(void) static bool acpi_scan_initialized; -int __init acpi_scan_init(void) +void __init acpi_scan_init(void) { - int result; acpi_status status; struct acpi_table_stao *stao_ptr; @@ -2594,8 +2593,7 @@ int __init acpi_scan_init(void) /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan(ACPI_ROOT_OBJECT); - if (result) + if (acpi_bus_scan(ACPI_ROOT_OBJECT)) goto out; acpi_root = acpi_fetch_acpi_dev(ACPI_ROOT_OBJECT); @@ -2604,8 +2602,7 @@ int __init acpi_scan_init(void) /* Fixed feature devices do not exist on HW-reduced platform */ if (!acpi_gbl_reduced_hardware) { - result = acpi_bus_scan_fixed(); - if (result) { + if (acpi_bus_scan_fixed()) { acpi_detach_data(acpi_root->handle, acpi_scan_drop_device); acpi_device_del(acpi_root); @@ -2620,7 +2617,6 @@ int __init acpi_scan_init(void) out: mutex_unlock(&acpi_scan_lock); - return result; } static struct acpi_probe_entry *ape; -- cgit v1.2.3 From 681e7187aef46f8d4e0fd0ddd2f888e3e5533cb2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Jan 2022 17:52:00 +0100 Subject: ACPI: scan: Simplify initialization of power and sleep buttons It should be perfectly fine to use ACPI if the "fixed" power or sleep buttons cannot be initialized. Moreover, running acpi_bus_scan() successfully on ACPI_ROOT_OBJECT generally causes many devices to be enumerated and probed, possibly including the entire PCI bus, so unregistering acpi_root if the registration of the "fixed" buttons fails is rather unhelpful. For this reason, do not fail acpi_scan_init() when acpi_bus_scan_fixed() fails and turn the latter into a void function. While at it, drop the outdated and misleading comment from acpi_bus_scan_fixed(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/scan.c | 62 ++++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7e7417f4df22..81ee565f47cd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2486,42 +2486,33 @@ int acpi_bus_register_early_device(int type) } EXPORT_SYMBOL_GPL(acpi_bus_register_early_device); -static int acpi_bus_scan_fixed(void) +static void acpi_bus_scan_fixed(void) { - int result = 0; - - /* - * Enumerate all fixed-feature devices. - */ if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { - struct acpi_device *device = NULL; - - result = acpi_add_single_object(&device, NULL, - ACPI_BUS_TYPE_POWER_BUTTON, false); - if (result) - return result; - - device->flags.match_driver = true; - result = device_attach(&device->dev); - if (result < 0) - return result; - - device_init_wakeup(&device->dev, true); + struct acpi_device *adev = NULL; + + acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_POWER_BUTTON, + false); + if (adev) { + adev->flags.match_driver = true; + if (device_attach(&adev->dev) >= 0) + device_init_wakeup(&adev->dev, true); + else + dev_dbg(&adev->dev, "No driver\n"); + } } if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { - struct acpi_device *device = NULL; - - result = acpi_add_single_object(&device, NULL, - ACPI_BUS_TYPE_SLEEP_BUTTON, false); - if (result) - return result; - - device->flags.match_driver = true; - result = device_attach(&device->dev); + struct acpi_device *adev = NULL; + + acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, + false); + if (adev) { + adev->flags.match_driver = true; + if (device_attach(&adev->dev) < 0) + dev_dbg(&adev->dev, "No driver\n"); + } } - - return result < 0 ? result : 0; } static void __init acpi_get_spcr_uart_addr(void) @@ -2601,15 +2592,8 @@ void __init acpi_scan_init(void) goto out; /* Fixed feature devices do not exist on HW-reduced platform */ - if (!acpi_gbl_reduced_hardware) { - if (acpi_bus_scan_fixed()) { - acpi_detach_data(acpi_root->handle, - acpi_scan_drop_device); - acpi_device_del(acpi_root); - acpi_bus_put_acpi_device(acpi_root); - goto out; - } - } + if (!acpi_gbl_reduced_hardware) + acpi_bus_scan_fixed(); acpi_turn_off_unused_power_resources(); -- cgit v1.2.3 From c96f195deeefecd8ef95c5aa508671dd246bf119 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Jan 2022 17:53:29 +0100 Subject: ACPI: scan: Rename label in acpi_scan_init() Rename the "out" label in acpi_scan_init() to "unlock", which is a better match for its purpose, and fix up its alignment. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/scan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 81ee565f47cd..75fab11f9188 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2585,11 +2585,11 @@ void __init acpi_scan_init(void) * Enumerate devices in the ACPI namespace. */ if (acpi_bus_scan(ACPI_ROOT_OBJECT)) - goto out; + goto unlock; acpi_root = acpi_fetch_acpi_dev(ACPI_ROOT_OBJECT); if (!acpi_root) - goto out; + goto unlock; /* Fixed feature devices do not exist on HW-reduced platform */ if (!acpi_gbl_reduced_hardware) @@ -2599,7 +2599,7 @@ void __init acpi_scan_init(void) acpi_scan_initialized = true; - out: +unlock: mutex_unlock(&acpi_scan_lock); } -- cgit v1.2.3 From 415b4b6c447ae03cb1d9cfc91df39616c92f15e2 Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Thu, 6 Jan 2022 01:45:56 +0800 Subject: ACPI: PCC: pcc_ctx can be static drivers/acpi/acpi_pcc.c:34:22: warning: symbol 'pcc_ctx' was not declared. Should it be static? Reported-by: kernel test robot Signed-off-by: kernel test robot Reviewed-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_pcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_pcc.c b/drivers/acpi/acpi_pcc.c index 41e3ebd204ff..a12b55d81209 100644 --- a/drivers/acpi/acpi_pcc.c +++ b/drivers/acpi/acpi_pcc.c @@ -31,7 +31,7 @@ struct pcc_data { struct acpi_pcc_info ctx; }; -struct acpi_pcc_info pcc_ctx; +static struct acpi_pcc_info pcc_ctx; static void pcc_rx_callback(struct mbox_client *cl, void *m) { -- cgit v1.2.3