High Service Level Agreements (SLAs) requires that the system runs without
service interruptions. Generally, system firmware provides runtime services
such as RAS(Reliability, Availability and Serviceability) features, UEFI runtime
services and ACPI services. Currently if there is any firmware code changes in
these code area, the system firmware update and reboot is required. Example of
bug fix could be wrong register size or location of the register. This means
customer services are not available during the firmware upgrade, which could
approach several minutes, resulting in not able to meet SLAs.
Intel provides a mechanism named Management Mode Runtime Update to help the users
update the firmware without having to reboot[1].
This series provides the following facilities.
1. Perform a runtime firmware driver update and activate.
2. Ability to inject firmware code at runtime, for dynamic instrumentation.
3. Facility to retrieve logs from runtime firmware update and activate telemetry.
(The telemetry is based on runtime firmware update: it records the logs during
runtime update(code injection and driver update).
The Management Mode Runtime Update OS Interface Specification[1] provides two ACPI
device objects to interface with system firmware to perform these updates. This patch
series introduces the drivers for those ACPI devices.
[1] https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf
Chen Yu (5):
Documentation: Introduce Platform Firmware Runtime Update
documentation
efi: Introduce EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and
corresponding structures
drivers/acpi: Introduce Platform Firmware Runtime Update device driver
drivers/acpi: Introduce Platform Firmware Runtime Update Telemetry
selftests/pfru: add test for Platform Firmware Runtime Update and
Telemetry
.../userspace-api/ioctl/ioctl-number.rst | 1 +
Documentation/x86/pfru.rst | 98 ++++
drivers/acpi/Kconfig | 1 +
drivers/acpi/Makefile | 1 +
drivers/acpi/pfru/Kconfig | 29 +
drivers/acpi/pfru/Makefile | 3 +
drivers/acpi/pfru/pfru_telemetry.c | 412 +++++++++++++
drivers/acpi/pfru/pfru_update.c | 544 ++++++++++++++++++
include/linux/efi.h | 50 ++
include/uapi/linux/pfru.h | 152 +++++
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/pfru/Makefile | 7 +
tools/testing/selftests/pfru/config | 2 +
tools/testing/selftests/pfru/pfru.h | 152 +++++
tools/testing/selftests/pfru/pfru_test.c | 324 +++++++++++
15 files changed, 1777 insertions(+)
create mode 100644 Documentation/x86/pfru.rst
create mode 100644 drivers/acpi/pfru/Kconfig
create mode 100644 drivers/acpi/pfru/Makefile
create mode 100644 drivers/acpi/pfru/pfru_telemetry.c
create mode 100644 drivers/acpi/pfru/pfru_update.c
create mode 100644 include/uapi/linux/pfru.h
create mode 100644 tools/testing/selftests/pfru/Makefile
create mode 100644 tools/testing/selftests/pfru/config
create mode 100644 tools/testing/selftests/pfru/pfru.h
create mode 100644 tools/testing/selftests/pfru/pfru_test.c
--
2.25.1
Platform Firmware Runtime Update(PFRU) Telemetry Service is part of RoT
(Root of Trust), which allows PFRU handler and other PFRU drivers to produce
telemetry data to upper layer OS consumer at runtime.
The linux provides interfaces for the user to query the parameters of
telemetry data, and the user could read out the telemetry data accordingly.
Typical log looks like:
[SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] ProcessSmmRuntimeUpdate = START, Action = 2
[SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] FwVersion = 0, CodeInjectionVersion = 1
[ShadowSmmRuntimeUpdateImage] Image = 0x74D9B000, ImageSize = 0x1172
[ProcessSmmRuntimeUpdate] ShadowSmmRuntimeUpdateImage.Status = Success
[ValidateSmmRuntimeUpdateImage] CapsuleHeader.CapsuleGuid = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A
[ValidateSmmRuntimeUpdateImage] FmpCapHeader.Version = 1
[ValidateSmmRuntimeUpdateImage] FmpCapImageHeader.UpdateImageTypeId = B2F84B79-7B6E-4E45-885F-3FB9BB185402
[ValidateSmmRuntimeUpdateImage] SmmRuntimeUpdateVerifyImageWithDenylist.Status = Success
[ValidateSmmRuntimeUpdateImage] SmmRuntimeUpdateVerifyImageWithAllowlist.Status = Success
[SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.Signature = 0x31494353
[SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.PlatformId = 63462139-A8B1-AA4E-9024-F2BB53EA4723
[SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.SupportedSmmFirmwareVersion = 0, PayloadHeader.SmmCodeInjectionRuntimeVersion = 1
[ProcessSmmRuntimeUpdate] ValidateSmmRuntimeUpdateImage.Status = Success
CPU CSR[0B102D28] Before = 7FBF830E
CPU CSR[0B102D28] After = 7FBF8310
[ProcessSmmRuntimeUpdate] ProcessSmmCodeInjection.Status = Success
[SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] ProcessSmmRuntimeUpdate = End, Status = Success
Signed-off-by: Chen Yu <[email protected]>
---
drivers/acpi/pfru/Kconfig | 14 +
drivers/acpi/pfru/Makefile | 1 +
drivers/acpi/pfru/pfru_telemetry.c | 412 +++++++++++++++++++++++++++++
include/uapi/linux/pfru.h | 46 ++++
4 files changed, 473 insertions(+)
create mode 100644 drivers/acpi/pfru/pfru_telemetry.c
diff --git a/drivers/acpi/pfru/Kconfig b/drivers/acpi/pfru/Kconfig
index 3f31b7d95f3b..e2934058884e 100644
--- a/drivers/acpi/pfru/Kconfig
+++ b/drivers/acpi/pfru/Kconfig
@@ -13,3 +13,17 @@ config ACPI_PFRU
To compile this driver as a module, choose M here:
the module will be called pfru_update.
+
+config ACPI_PFRU_TELEMETRY
+ tristate "ACPI Platform Firmware Runtime Update Telemetry Service"
+ depends on ACPI_PFRU
+ help
+ PFRU(Platform Firmware Runtime Update) Telemetry Service is part of
+ RoT(Root of Trust), which allows Platform Firmware Runtime Update handler
+ and other PFRU drivers to produce telemetry data to upper layer OS consumer
+ at runtime.
+
+ For more information, see:
+ <file:Documentation/x86/pfru_update.rst>
+
+ If unsure, please say N.
diff --git a/drivers/acpi/pfru/Makefile b/drivers/acpi/pfru/Makefile
index 098cbe80cf3d..30060ba320ea 100644
--- a/drivers/acpi/pfru/Makefile
+++ b/drivers/acpi/pfru/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ACPI_PFRU) += pfru_update.o
+obj-$(CONFIG_ACPI_PFRU_TELEMETRY) += pfru_telemetry.o
diff --git a/drivers/acpi/pfru/pfru_telemetry.c b/drivers/acpi/pfru/pfru_telemetry.c
new file mode 100644
index 000000000000..0873eb8bb69e
--- /dev/null
+++ b/drivers/acpi/pfru/pfru_telemetry.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Platform Firmware Runtime Update
+ * Telemetry Service Device Driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ * Author: Chen Yu <[email protected]>
+ */
+#include <linux/acpi.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/minmax.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/uuid.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/pfru.h>
+
+struct pfru_telem_device {
+ struct device *dev;
+ guid_t uuid;
+ struct telem_info info;
+};
+
+static struct pfru_telem_device telem_dev;
+static struct pfru_telem_device *get_pfru_telem_dev(void)
+{
+ return &telem_dev;
+}
+
+static int get_pfru_data_info(struct telem_data_info *data_info,
+ int log_type)
+{
+ struct pfru_telem_device *pf_telem_dev;
+ union acpi_object *out_obj, in_obj, in_buf;
+ acpi_handle handle;
+ int i, ret = -EINVAL;
+
+ pf_telem_dev = get_pfru_telem_dev();
+ handle = ACPI_HANDLE(pf_telem_dev->dev);
+
+ 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 = log_type;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pf_telem_dev->uuid,
+ pf_telem_dev->info.log_revid, FUNC_GET_DATA,
+ &in_obj, ACPI_TYPE_PACKAGE);
+ if (!out_obj) {
+ pr_err("Failed to invoke _DSM\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < out_obj->package.count; i++) {
+ union acpi_object *obj = &out_obj->package.elements[i];
+
+ switch (i) {
+ case 0:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->status = obj->integer.value;
+ break;
+ case 1:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->ext_status = obj->integer.value;
+ break;
+ case 2:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->max_data_size = obj->integer.value;
+ break;
+ case 3:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk1_addr_lo = obj->integer.value;
+ break;
+ case 4:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk1_addr_hi = obj->integer.value;
+ break;
+ case 5:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk1_size = obj->integer.value;
+ break;
+ case 6:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk2_addr_lo = obj->integer.value;
+ break;
+ case 7:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk2_addr_hi = obj->integer.value;
+ break;
+ case 8:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->chunk2_size = obj->integer.value;
+ break;
+ case 9:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->rollover_cnt = obj->integer.value;
+ break;
+ case 10:
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ data_info->reset_cnt = obj->integer.value;
+ break;
+ }
+ }
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int set_pfru_log_level(int level)
+{
+ union acpi_object *out_obj, *obj, in_obj, in_buf;
+ struct pfru_telem_device *pf_telem_dev;
+ enum dsm_status status;
+ acpi_handle handle;
+ int ret = -EINVAL;
+
+ pf_telem_dev = get_pfru_telem_dev();
+ handle = ACPI_HANDLE(pf_telem_dev->dev);
+
+ 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, &pf_telem_dev->uuid,
+ pf_telem_dev->info.log_revid, 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) {
+ pr_err("get MM telemetry level error status %d\n",
+ status);
+ goto free_acpi_buffer;
+ }
+
+ obj = &out_obj->package.elements[1];
+ status = obj->integer.value;
+ if (status) {
+ pr_err("get MM telemetry level error extend status %d\n",
+ status);
+ goto free_acpi_buffer;
+ }
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int get_pfru_log_level(int *level)
+{
+ struct pfru_telem_device *pf_telem_dev;
+ union acpi_object *out_obj, *obj;
+ enum dsm_status status;
+ acpi_handle handle;
+ int ret = -EINVAL;
+
+ pf_telem_dev = get_pfru_telem_dev();
+ handle = ACPI_HANDLE(pf_telem_dev->dev);
+ out_obj = acpi_evaluate_dsm_typed(handle, &pf_telem_dev->uuid,
+ pf_telem_dev->info.log_revid, 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) {
+ pr_err("get MM telemetry level error status %d\n",
+ status);
+ goto free_acpi_buffer;
+ }
+
+ obj = &out_obj->package.elements[1];
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ status = obj->integer.value;
+ if (status) {
+ pr_err("get MM telemetry level error status %d\n",
+ status);
+ goto free_acpi_buffer;
+ }
+
+ obj = &out_obj->package.elements[2];
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+ *level = obj->integer.value;
+
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int valid_log_level(int level)
+{
+ return (level == LOG_ERR) || (level == LOG_WARN) ||
+ (level == LOG_INFO) || (level == LOG_VERB);
+}
+
+static int valid_log_type(int type)
+{
+ return (type == LOG_EXEC_IDX) || (type == LOG_HISTORY_IDX);
+}
+
+static long pfru_telemetry_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+
+{
+ struct pfru_telem_device *pf_telem_dev;
+ struct telem_data_info data_info;
+ struct telem_info info;
+ void __user *p;
+ int ret = 0;
+
+ pf_telem_dev = get_pfru_telem_dev();
+ p = (void __user *)arg;
+
+ switch (cmd) {
+ case PFRU_LOG_IOC_SET_INFO:
+ if (copy_from_user(&info, p, sizeof(info)))
+ return -EFAULT;
+ if (valid_revid(info.log_revid))
+ pf_telem_dev->info.log_revid = info.log_revid;
+
+ if (valid_log_level(info.log_level)) {
+ ret = set_pfru_log_level(info.log_level);
+ if (ret)
+ return ret;
+ pf_telem_dev->info.log_level = info.log_level;
+ }
+ if (valid_log_type(info.log_type))
+ pf_telem_dev->info.log_type = info.log_type;
+ break;
+ case PFRU_LOG_IOC_GET_INFO:
+ ret = get_pfru_log_level(&info.log_level);
+ if (ret)
+ return ret;
+ info.log_type = pf_telem_dev->info.log_type;
+ info.log_revid = pf_telem_dev->info.log_revid;
+ if (copy_to_user(p, &info, sizeof(info)))
+ ret = -EFAULT;
+ break;
+ case PFRU_LOG_IOC_GET_DATA_INFO:
+ ret = get_pfru_data_info(&data_info, pf_telem_dev->info.log_type);
+ if (ret)
+ return ret;
+ if (copy_to_user(p, &data_info, sizeof(struct telem_data_info)))
+ ret = -EFAULT;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ return ret;
+}
+
+static ssize_t pfru_telemetry_read(struct file *filp, char __user *ubuf,
+ size_t size, loff_t *off)
+{
+ struct pfru_telem_device *pf_telem_dev;
+ struct telem_data_info info;
+ phys_addr_t base_addr;
+ int buf_size, ret;
+ char *buf_ptr;
+
+ if (*off < 0)
+ return -EINVAL;
+
+ pf_telem_dev = get_pfru_telem_dev();
+
+ ret = get_pfru_data_info(&info, pf_telem_dev->info.log_type);
+ if (ret) {
+ pr_err("Could not get telemetry data info %d\n", ret);
+ return ret;
+ }
+
+ base_addr = (phys_addr_t)(info.chunk2_addr_lo |
+ (info.chunk2_addr_hi << 32));
+ if (!base_addr) {
+ pr_err("Telemetry data not ready\n");
+ return -EBUSY;
+ }
+
+ buf_size = info.max_data_size;
+ if (*off >= buf_size)
+ return 0;
+
+ buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB);
+ if (IS_ERR(buf_ptr))
+ return PTR_ERR(buf_ptr);
+
+ size = min_t(size_t, size, buf_size - *off);
+
+ ret = -EFAULT;
+ if (copy_to_user(ubuf, buf_ptr + *off, size))
+ goto out;
+ ret = 0;
+out:
+ memunmap(buf_ptr);
+
+ return ret ? ret : size;
+}
+
+#ifdef CONFIG_COMPAT
+static long compat_pfru_telemetry_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return pfru_telemetry_ioctl(filep, cmd, arg);
+}
+#endif
+
+static const struct file_operations acpi_pfru_telemetry_fops = {
+ .owner = THIS_MODULE,
+ .read = pfru_telemetry_read,
+ .unlocked_ioctl = pfru_telemetry_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = compat_pfru_telemetry_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice pfru_telemetry_misc_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "pfru_telemetry",
+ .nodename = "pfru/telemetry",
+ .fops = &acpi_pfru_telemetry_fops,
+};
+
+static int acpi_pfru_telemetry_remove(struct platform_device *pdev)
+{
+ misc_deregister(&pfru_telemetry_misc_dev);
+
+ return 0;
+}
+
+static int acpi_pfru_telemetry_probe(struct platform_device *pdev)
+{
+ struct pfru_telem_device *pf_telem_dev;
+ acpi_handle handle;
+ int ret;
+
+ pf_telem_dev = get_pfru_telem_dev();
+
+ ret = guid_parse(PFRU_TELEMETRY_UUID, &pf_telem_dev->uuid);
+ if (ret)
+ return ret;
+
+ pf_telem_dev->info.log_revid = 1;
+ pf_telem_dev->dev = &pdev->dev;
+ handle = ACPI_HANDLE(pf_telem_dev->dev);
+ if (!acpi_has_method(handle, "_DSM")) {
+ pr_err("Missing _DSM\n");
+ return -ENODEV;
+ }
+
+ ret = misc_register(&pfru_telemetry_misc_dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct acpi_device_id acpi_pfru_telemetry_ids[] = {
+ {"INTC1081", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_pfru_telemetry_ids);
+
+static struct platform_driver acpi_pfru_telemetry_driver = {
+ .driver = {
+ .name = "pfru_telemetry",
+ .acpi_match_table = acpi_pfru_telemetry_ids,
+ },
+ .probe = acpi_pfru_telemetry_probe,
+ .remove = acpi_pfru_telemetry_remove,
+};
+module_platform_driver(acpi_pfru_telemetry_driver);
+
+MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry Service device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/pfru.h b/include/uapi/linux/pfru.h
index 81eb8ad5a57e..b4d5c0078cfb 100644
--- a/include/uapi/linux/pfru.h
+++ b/include/uapi/linux/pfru.h
@@ -103,4 +103,50 @@ struct updated_result {
unsigned long high_exec_time;
};
+#define PFRU_TELEMETRY_UUID "75191659-8178-4D9D-B88F-AC5E5E93E8BF"
+
+/* Telemetry structures. */
+struct telem_data_info {
+ enum dsm_status status;
+ enum dsm_status ext_status;
+ /* Maximum supported size of data of
+ * all Data Chunks combined.
+ */
+ unsigned long chunk1_addr_lo;
+ unsigned long chunk1_addr_hi;
+ unsigned long chunk2_addr_lo;
+ unsigned long chunk2_addr_hi;
+ int max_data_size;
+ int chunk1_size;
+ int chunk2_size;
+ int rollover_cnt;
+ int reset_cnt;
+};
+
+struct telem_info {
+ int log_level;
+ int log_type;
+ int log_revid;
+};
+
+/* Two logs: history and execution log */
+#define LOG_EXEC_IDX 0
+#define LOG_HISTORY_IDX 1
+#define NR_LOG_TYPE 2
+
+#define LOG_ERR 0
+#define LOG_WARN 1
+#define LOG_INFO 2
+#define LOG_VERB 4
+
+#define FUNC_SET_LEV 1
+#define FUNC_GET_LEV 2
+#define FUNC_GET_DATA 3
+
+#define LOG_NAME_SIZE 10
+
+#define PFRU_LOG_IOC_SET_INFO _IOW(PFRU_MAGIC, 0x05, struct telem_info)
+#define PFRU_LOG_IOC_GET_INFO _IOR(PFRU_MAGIC, 0x06, struct telem_info)
+#define PFRU_LOG_IOC_GET_DATA_INFO _IOR(PFRU_MAGIC, 0x07, struct telem_data_info)
+
#endif /* __PFRU_H__ */
--
2.25.1
Platform Firmware Runtime Update image starts with UEFI headers, and the headers
are defined in UEFI specification, but some of them have not been defined in the
kernel yet.
For example, the header layout of a capsule file looks like this:
EFI_CAPSULE_HEADER
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
EFI_FIRMWARE_IMAGE_AUTHENTICATION
These structures would be used by the Platform Firmware Runtime Update
driver to parse the format of capsule file to verify if the corresponding
version number is valid. The EFI_CAPSULE_HEADER has been defined in the
kernel, however the rest are not, thus introduce corresponding UEFI structures
accordingly.
The reason why efi_manage_capsule_header_t and efi_manage_capsule_image_header_t
are packedi might be that:
According to the uefi spec,
[Figure 23-6 Firmware Management and Firmware Image Management headers]
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER is located at the lowest offset within
the body of the capsule. And this structure is designed to be unaligned to save
space, because in this way the adjacent drivers and binary payload elements could
start on byte boundary with no padding. And the
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is at the head of each payload, so
packing this structure also makes room for more data.
Signed-off-by: Chen Yu <[email protected]>
---
include/linux/efi.h | 50 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 6b5d36babfcc..19ff834e1388 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -148,6 +148,56 @@ typedef struct {
u32 imagesize;
} efi_capsule_header_t;
+#pragma pack(1)
+
+/* EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER */
+typedef struct {
+ u32 ver;
+ u16 emb_drv_cnt;
+ u16 payload_cnt;
+ /*
+ * Variable array indicated by number of
+ * (emb_drv_cnt + payload_cnt)
+ */
+ u64 offset_list[];
+} efi_manage_capsule_header_t;
+
+/* EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER */
+typedef struct {
+ u32 ver;
+ guid_t image_type_id;
+ u8 image_index;
+ u8 reserved_bytes[3];
+ u32 image_size;
+ u32 vendor_code_size;
+ /* ver = 2. */
+ u64 hw_ins;
+ /* ver = v3. */
+ u64 capsule_support;
+} efi_manage_capsule_image_header_t;
+
+#pragma pack()
+
+/* WIN_CERTIFICATE */
+typedef struct {
+ u32 len;
+ u16 rev;
+ u16 cert_type;
+} win_cert_t;
+
+/* WIN_CERTIFICATE_UEFI_GUID */
+typedef struct {
+ win_cert_t hdr;
+ guid_t cert_type;
+ u8 cert_data[];
+} win_cert_uefi_guid_t;
+
+/* EFI_FIRMWARE_IMAGE_AUTHENTICATIO */
+typedef struct {
+ u64 mon_count;
+ win_cert_uefi_guid_t auth_info;
+} efi_image_auth_t;
+
/*
* EFI capsule flags
*/
--
2.25.1
On Tue, 7 Sept 2021 at 17:12, Chen Yu <[email protected]> wrote:
>
> Platform Firmware Runtime Update image starts with UEFI headers, and the headers
> are defined in UEFI specification, but some of them have not been defined in the
> kernel yet.
>
> For example, the header layout of a capsule file looks like this:
>
> EFI_CAPSULE_HEADER
> EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
> EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
> EFI_FIRMWARE_IMAGE_AUTHENTICATION
>
> These structures would be used by the Platform Firmware Runtime Update
> driver to parse the format of capsule file to verify if the corresponding
> version number is valid. The EFI_CAPSULE_HEADER has been defined in the
> kernel, however the rest are not, thus introduce corresponding UEFI structures
> accordingly.
>
> The reason why efi_manage_capsule_header_t and efi_manage_capsule_image_header_t
> are packedi might be that:
> According to the uefi spec,
> [Figure 23-6 Firmware Management and Firmware Image Management headers]
> EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER is located at the lowest offset within
> the body of the capsule. And this structure is designed to be unaligned to save
> space, because in this way the adjacent drivers and binary payload elements could
> start on byte boundary with no padding. And the
> EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is at the head of each payload, so
> packing this structure also makes room for more data.
>
> Signed-off-by: Chen Yu <[email protected]>
Who is going to use these definitions? I only see this patch, where is
the rest of the series?
> ---
> include/linux/efi.h | 50 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 50 insertions(+)
>
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 6b5d36babfcc..19ff834e1388 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -148,6 +148,56 @@ typedef struct {
> u32 imagesize;
> } efi_capsule_header_t;
>
> +#pragma pack(1)
> +
> +/* EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER */
> +typedef struct {
> + u32 ver;
> + u16 emb_drv_cnt;
> + u16 payload_cnt;
> + /*
> + * Variable array indicated by number of
> + * (emb_drv_cnt + payload_cnt)
> + */
> + u64 offset_list[];
> +} efi_manage_capsule_header_t;
> +
> +/* EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER */
> +typedef struct {
> + u32 ver;
> + guid_t image_type_id;
> + u8 image_index;
> + u8 reserved_bytes[3];
> + u32 image_size;
> + u32 vendor_code_size;
> + /* ver = 2. */
> + u64 hw_ins;
> + /* ver = v3. */
> + u64 capsule_support;
> +} efi_manage_capsule_image_header_t;
> +
> +#pragma pack()
> +
> +/* WIN_CERTIFICATE */
> +typedef struct {
> + u32 len;
> + u16 rev;
> + u16 cert_type;
> +} win_cert_t;
> +
> +/* WIN_CERTIFICATE_UEFI_GUID */
> +typedef struct {
> + win_cert_t hdr;
> + guid_t cert_type;
> + u8 cert_data[];
> +} win_cert_uefi_guid_t;
> +
> +/* EFI_FIRMWARE_IMAGE_AUTHENTICATIO */
> +typedef struct {
> + u64 mon_count;
> + win_cert_uefi_guid_t auth_info;
> +} efi_image_auth_t;
> +
> /*
> * EFI capsule flags
> */
> --
> 2.25.1
>
Introduce a simple test for Platform Firmware Runtime Update and Telemetry
drivers. It is based on ioctl to either update firmware driver or code injection,
and read corresponding PFRU Telemetry log into user space.
For example:
./pfru_test -h
usage: pfru_test [OPTIONS]
code injection:
-l, --load
-s, --stage
-a, --activate
-u, --update [stage and activate]
-q, --query
-d, --revid update
telemetry:
-G, --getloginfo
-T, --type(0:execution, 1:history)
-L, --level(0, 1, 2, 4)
-R, --read
-D, --revid log
./pfru_test -G
log_level:4
log_type:0
log_revid:2
max_data_size:65536
chunk1_size:0
chunk2_size:1401
rollover_cnt:0
reset_cnt:4
./pfru_test -q
code injection image type:794bf8b2-6e7b-454e-885f-3fb9bb185402
fw_version:0
code_rt_version:1
driver update image type:0e5f0b14-f849-7945-ad81-bc7b6d2bb245
drv_rt_version:0
drv_svn:0
platform id:39214663-b1a8-4eaa-9024-f2bb53ea4723
oem id:a36db54f-ea2a-e14e-b7c4-b5780e51ba3d
Tested-by: Dou Shengnan <[email protected]>
Signed-off-by: Chen Yu <[email protected]>
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/pfru/Makefile | 7 +
tools/testing/selftests/pfru/config | 2 +
tools/testing/selftests/pfru/pfru.h | 152 +++++++++++
tools/testing/selftests/pfru/pfru_test.c | 324 +++++++++++++++++++++++
5 files changed, 486 insertions(+)
create mode 100644 tools/testing/selftests/pfru/Makefile
create mode 100644 tools/testing/selftests/pfru/config
create mode 100644 tools/testing/selftests/pfru/pfru.h
create mode 100644 tools/testing/selftests/pfru/pfru_test.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index fb010a35d61a..c8b53a2c4450 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -56,6 +56,7 @@ TARGETS += seccomp
TARGETS += sgx
TARGETS += sigaltstack
TARGETS += size
+TARGETS += pfru
TARGETS += sparc64
TARGETS += splice
TARGETS += static_keys
diff --git a/tools/testing/selftests/pfru/Makefile b/tools/testing/selftests/pfru/Makefile
new file mode 100644
index 000000000000..c61916ccf637
--- /dev/null
+++ b/tools/testing/selftests/pfru/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+CFLAGS += -Wall -O2
+LDLIBS := -luuid
+
+TEST_GEN_PROGS := pfru_test
+include ../lib.mk
diff --git a/tools/testing/selftests/pfru/config b/tools/testing/selftests/pfru/config
new file mode 100644
index 000000000000..37f53609acbd
--- /dev/null
+++ b/tools/testing/selftests/pfru/config
@@ -0,0 +1,2 @@
+CONFIG_ACPI_PFRU=m
+CONFIG_ACPI_PFRU_TELEMETRY=m
diff --git a/tools/testing/selftests/pfru/pfru.h b/tools/testing/selftests/pfru/pfru.h
new file mode 100644
index 000000000000..8cd4ed80b161
--- /dev/null
+++ b/tools/testing/selftests/pfru/pfru.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Platform Firmware Runtime Update header
+ *
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ */
+#ifndef __PFRU_H__
+#define __PFRU_H__
+
+#include <linux/ioctl.h>
+#include <uuid/uuid.h>
+
+#define PFRU_UUID "ECF9533B-4A3C-4E89-939E-C77112601C6D"
+#define PFRU_CODE_INJ_UUID "B2F84B79-7B6E-4E45-885F-3FB9BB185402"
+#define PFRU_DRV_UPDATE_UUID "4569DD8C-75F1-429A-A3D6-24DE8097A0DF"
+
+#define FUNC_STANDARD_QUERY 0
+#define FUNC_QUERY_UPDATE_CAP 1
+#define FUNC_QUERY_BUF 2
+#define FUNC_START 3
+
+#define CODE_INJECT_TYPE 1
+#define DRIVER_UPDATE_TYPE 2
+
+#define REVID_1 1
+#define REVID_2 2
+
+#define PFRU_MAGIC 0xEE
+
+#define PFRU_IOC_SET_REV _IOW(PFRU_MAGIC, 0x01, unsigned int)
+#define PFRU_IOC_STAGE _IOW(PFRU_MAGIC, 0x02, unsigned int)
+#define PFRU_IOC_ACTIVATE _IOW(PFRU_MAGIC, 0x03, unsigned int)
+#define PFRU_IOC_STAGE_ACTIVATE _IOW(PFRU_MAGIC, 0x04, unsigned int)
+
+static inline int valid_revid(int id)
+{
+ return (id == REVID_1) || (id == REVID_2);
+}
+
+/* Capsule file payload header */
+struct payload_hdr {
+ __u32 sig;
+ __u32 hdr_version;
+ __u32 hdr_size;
+ __u32 hw_ver;
+ __u32 rt_ver;
+ uuid_t platform_id;
+};
+
+enum start_action {
+ START_STAGE,
+ START_ACTIVATE,
+ START_STAGE_ACTIVATE,
+};
+
+enum dsm_status {
+ DSM_SUCCEED,
+ DSM_FUNC_NOT_SUPPORT,
+ DSM_INVAL_INPUT,
+ DSM_HARDWARE_ERR,
+ DSM_RETRY_SUGGESTED,
+ DSM_UNKNOWN,
+ DSM_FUNC_SPEC_ERR,
+};
+
+struct update_cap_info {
+ enum dsm_status status;
+ int update_cap;
+
+ uuid_t code_type;
+ int fw_version;
+ int code_rt_version;
+
+ uuid_t drv_type;
+ int drv_rt_version;
+ int drv_svn;
+
+ uuid_t platform_id;
+ uuid_t oem_id;
+
+ char oem_info[];
+};
+
+struct com_buf_info {
+ enum dsm_status status;
+ enum dsm_status ext_status;
+ unsigned long addr_lo;
+ unsigned long addr_hi;
+ int buf_size;
+};
+
+struct capsulate_buf_info {
+ unsigned long src;
+ int size;
+};
+
+struct updated_result {
+ enum dsm_status status;
+ enum dsm_status ext_status;
+ unsigned long low_auth_time;
+ unsigned long high_auth_time;
+ unsigned long low_exec_time;
+ unsigned long high_exec_time;
+};
+
+#define PFRU_TELEMETRY_UUID "75191659-8178-4D9D-B88F-AC5E5E93E8BF"
+
+/* Telemetry structures. */
+struct telem_data_info {
+ enum dsm_status status;
+ enum dsm_status ext_status;
+ /* Maximum supported size of data of
+ * all Data Chunks combined.
+ */
+ unsigned long chunk1_addr_lo;
+ unsigned long chunk1_addr_hi;
+ unsigned long chunk2_addr_lo;
+ unsigned long chunk2_addr_hi;
+ int max_data_size;
+ int chunk1_size;
+ int chunk2_size;
+ int rollover_cnt;
+ int reset_cnt;
+};
+
+struct telem_info {
+ int log_level;
+ int log_type;
+ int log_revid;
+};
+
+/* Two logs: history and execution log */
+#define LOG_EXEC_IDX 0
+#define LOG_HISTORY_IDX 1
+#define NR_LOG_TYPE 2
+
+#define LOG_ERR 0
+#define LOG_WARN 1
+#define LOG_INFO 2
+#define LOG_VERB 4
+
+#define FUNC_SET_LEV 1
+#define FUNC_GET_LEV 2
+#define FUNC_GET_DATA 3
+
+#define LOG_NAME_SIZE 10
+
+#define PFRU_LOG_IOC_SET_INFO _IOW(PFRU_MAGIC, 0x05, struct telem_info)
+#define PFRU_LOG_IOC_GET_INFO _IOR(PFRU_MAGIC, 0x06, struct telem_info)
+#define PFRU_LOG_IOC_GET_DATA_INFO _IOR(PFRU_MAGIC, 0x07, struct telem_data_info)
+
+#endif /* __PFRU_H__ */
diff --git a/tools/testing/selftests/pfru/pfru_test.c b/tools/testing/selftests/pfru/pfru_test.c
new file mode 100644
index 000000000000..d24d79d3836e
--- /dev/null
+++ b/tools/testing/selftests/pfru/pfru_test.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Tests Runtime Update/Telemetry (see Documentation/x86/pfru_update.rst)
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include "pfru.h"
+
+#define MAX_LOG_SIZE 65536
+
+struct update_cap_info cap_info;
+struct com_buf_info buf_info;
+struct capsulate_buf_info image_info;
+struct telem_data_info data_info;
+char *capsule_name;
+int action, query_cap, log_type, log_level, log_read, log_getinfo,
+ revid, log_revid;
+int set_log_level, set_log_type,
+ set_revid, set_log_revid;
+
+char *progname;
+
+static int valid_log_level(int level)
+{
+ return (level == LOG_ERR) || (level == LOG_WARN) ||
+ (level == LOG_INFO) || (level == LOG_VERB);
+}
+
+static int valid_log_type(int type)
+{
+ return (type == LOG_EXEC_IDX) || (type == LOG_HISTORY_IDX);
+}
+
+static void help(void)
+{
+ fprintf(stderr,
+ "usage: %s [OPTIONS]\n"
+ " code injection:\n"
+ " -l, --load\n"
+ " -s, --stage\n"
+ " -a, --activate\n"
+ " -u, --update [stage and activate]\n"
+ " -q, --query\n"
+ " -d, --revid update\n"
+ " telemetry:\n"
+ " -G, --getloginfo\n"
+ " -T, --type(0:execution, 1:history)\n"
+ " -L, --level(0, 1, 2, 4)\n"
+ " -R, --read\n"
+ " -D, --revid log\n",
+ progname);
+}
+
+char *option_string = "l:sauqd:GT:L:RD:h";
+static struct option long_options[] = {
+ {"load", required_argument, 0, 'l'},
+ {"stage", no_argument, 0, 's'},
+ {"activate", no_argument, 0, 'a'},
+ {"update", no_argument, 0, 'u'},
+ {"query", no_argument, 0, 'q'},
+ {"getloginfo", no_argument, 0, 'G'},
+ {"type", required_argument, 0, 'T'},
+ {"level", required_argument, 0, 'L'},
+ {"read", no_argument, 0, 'R'},
+ {"setrev", required_argument, 0, 'd'},
+ {"setrevlog", required_argument, 0, 'D'},
+ {"help", no_argument, 0, 'h'},
+ {}
+};
+
+static void parse_options(int argc, char **argv)
+{
+ char *pathname;
+ int c;
+
+ pathname = strdup(argv[0]);
+ progname = basename(pathname);
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, option_string,
+ long_options, &option_index);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'l':
+ capsule_name = optarg;
+ break;
+ case 's':
+ action = 1;
+ break;
+ case 'a':
+ action = 2;
+ break;
+ case 'u':
+ action = 3;
+ break;
+ case 'q':
+ query_cap = 1;
+ break;
+ case 'G':
+ log_getinfo = 1;
+ break;
+ case 'T':
+ log_type = atoi(optarg);
+ set_log_type = 1;
+ break;
+ case 'L':
+ log_level = atoi(optarg);
+ set_log_level = 1;
+ break;
+ case 'R':
+ log_read = 1;
+ break;
+ case 'd':
+ revid = atoi(optarg);
+ set_revid = 1;
+ break;
+ case 'D':
+ log_revid = atoi(optarg);
+ set_log_revid = 1;
+ break;
+ case 'h':
+ help();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void print_cap(struct update_cap_info *cap)
+{
+ char *uuid = malloc(37);
+
+ if (!uuid) {
+ perror("Can not allocate uuid buffer\n");
+ exit(1);
+ }
+ uuid_unparse(cap->code_type, uuid);
+ printf("code injection image type:%s\n", uuid);
+ printf("fw_version:%d\n", cap->fw_version);
+ printf("code_rt_version:%d\n", cap->code_rt_version);
+
+ uuid_unparse(cap->drv_type, uuid);
+ printf("driver update image type:%s\n", uuid);
+ printf("drv_rt_version:%d\n", cap->drv_rt_version);
+ printf("drv_svn:%d\n", cap->drv_svn);
+
+ uuid_unparse(cap->platform_id, uuid);
+ printf("platform id:%s\n", uuid);
+ uuid_unparse(cap->oem_id, uuid);
+ printf("oem id:%s\n", uuid);
+
+ free(uuid);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd_update, fd_log, fd_capsule;
+ struct telem_data_info data_info;
+ struct telem_info info;
+ struct update_cap_info cap;
+ void *addr_map_capsule;
+ struct stat st;
+ char *log_buf;
+ int ret = 0;
+
+ parse_options(argc, argv);
+
+ fd_log = open("/dev/pfru/telemetry", O_RDWR);
+ if (fd_log < 0) {
+ perror("Cannot open telemetry device...");
+ return 1;
+ }
+ fd_update = open("/dev/pfru/update", O_RDWR);
+ if (fd_update < 0) {
+ perror("Cannot open code injection device...");
+ return 1;
+ }
+
+ if (query_cap) {
+ ret = read(fd_update, &cap, sizeof(cap));
+ if (ret == -1) {
+ perror("Read error.");
+ return 1;
+ }
+ print_cap(&cap);
+ }
+
+ if (log_getinfo) {
+ ret = ioctl(fd_log, PFRU_LOG_IOC_GET_DATA_INFO, &data_info);
+ if (ret) {
+ perror("Get log data info failed.");
+ return 1;
+ }
+ ret = ioctl(fd_log, PFRU_LOG_IOC_GET_INFO, &info);
+ if (ret) {
+ perror("Get log info failed.");
+ return 1;
+ }
+ printf("log_level:%d\n", info.log_level);
+ printf("log_type:%d\n", info.log_type);
+ printf("log_revid:%d\n", info.log_revid);
+ printf("max_data_size:%d\n", data_info.max_data_size);
+ printf("chunk1_size:%d\n", data_info.chunk1_size);
+ printf("chunk2_size:%d\n", data_info.chunk2_size);
+ printf("rollover_cnt:%d\n", data_info.rollover_cnt);
+ printf("reset_cnt:%d\n", data_info.reset_cnt);
+
+ return 0;
+ }
+
+ info.log_level = -1;
+ info.log_type = -1;
+ info.log_revid = -1;
+
+ if (set_log_level) {
+ if (!valid_log_level(log_level)) {
+ printf("Invalid log level %d\n",
+ log_level);
+ } else {
+ info.log_level = log_level;
+ }
+ }
+ if (set_log_type) {
+ if (!valid_log_type(log_type)) {
+ printf("Invalid log type %d\n",
+ log_type);
+ } else {
+ info.log_type = log_type;
+ }
+ }
+ if (set_log_revid) {
+ if (!valid_revid(log_revid)) {
+ printf("Invalid log revid %d\n",
+ log_revid);
+ } else {
+ info.log_revid = log_revid;
+ }
+ }
+
+ ret = ioctl(fd_log, PFRU_LOG_IOC_SET_INFO, &info);
+ if (ret) {
+ perror("Log information set failed.(log_level, log_type, log_revid)");
+ return 1;
+ }
+
+ if (set_revid) {
+ ret = ioctl(fd_update, PFRU_IOC_SET_REV, &revid);
+ if (ret) {
+ perror("mru update revid set failed");
+ return 1;
+ }
+ printf("mru update revid set to %d\n", revid);
+ }
+
+ if (capsule_name) {
+ fd_capsule = open(capsule_name, O_RDONLY);
+ if (fd_capsule < 0) {
+ perror("Can not open capsule file...");
+ return 1;
+ }
+ if (fstat(fd_capsule, &st) < 0) {
+ perror("Can not fstat capsule file...");
+ return 1;
+ }
+ addr_map_capsule = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
+ fd_capsule, 0);
+ if (addr_map_capsule == MAP_FAILED) {
+ perror("Failed to mmap capsule file.");
+ return 1;
+ }
+ ret = write(fd_update, (char *)addr_map_capsule, st.st_size);
+ printf("Load %d bytes of capsule file into the system\n",
+ ret);
+ if (ret == -1) {
+ perror("Failed to load capsule file");
+ return 1;
+ }
+ munmap(addr_map_capsule, st.st_size);
+ printf("Load done.\n");
+ }
+
+ if (action) {
+ if (action == 1)
+ ret = ioctl(fd_update, PFRU_IOC_STAGE, NULL);
+ else if (action == 2)
+ ret = ioctl(fd_update, PFRU_IOC_ACTIVATE, NULL);
+ else if (action == 3)
+ ret = ioctl(fd_update, PFRU_IOC_STAGE_ACTIVATE, NULL);
+ else
+ return 1;
+ printf("Update finished, return %d\n", ret);
+ }
+
+ if (log_read) {
+ log_buf = malloc(MAX_LOG_SIZE + 1);
+ if (!log_buf) {
+ perror("log_buf allocate failed.");
+ return 1;
+ }
+ ret = read(fd_log, log_buf, MAX_LOG_SIZE);
+ if (ret == -1) {
+ perror("Read error.");
+ return 1;
+ }
+ log_buf[ret] = '\0';
+ printf("%s\n", log_buf);
+ free(log_buf);
+ }
+
+ return 0;
+}
--
2.25.1
On 9/7/21 9:40 AM, Chen Yu wrote:
> Introduce a simple test for Platform Firmware Runtime Update and Telemetry
> drivers. It is based on ioctl to either update firmware driver or code injection,
> and read corresponding PFRU Telemetry log into user space.
>
A few things to consider and add handling for them in the
test.
What happens when non-root user runs this test?
What happens when the pfru device doesn't exist?
[snip]
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int fd_update, fd_log, fd_capsule;
> + struct telem_data_info data_info;
> + struct telem_info info;
> + struct update_cap_info cap;
> + void *addr_map_capsule;
> + struct stat st;
> + char *log_buf;
> + int ret = 0;
> +
> + parse_options(argc, argv);
> +
> + fd_log = open("/dev/pfru/telemetry", O_RDWR);
> + if (fd_log < 0) {
> + perror("Cannot open telemetry device...");
> + return 1;
> + }
Is this considered an error or unsupported?
> + fd_update = open("/dev/pfru/update", O_RDWR);
> + if (fd_update < 0) {
> + perror("Cannot open code injection device...");
> + return 1;
> + }
> +
Same here. If test is run on platform with pfru test should skip
instead of reporting failure/error.
thanks,
-- Shuah
On Tue, Sep 07, 2021 at 06:06:28PM +0200, Ard Biesheuvel wrote:
> On Tue, 7 Sept 2021 at 17:12, Chen Yu <[email protected]> wrote:
> >
> > Platform Firmware Runtime Update image starts with UEFI headers, and the headers
> > are defined in UEFI specification, but some of them have not been defined in the
> > kernel yet.
> >
> > For example, the header layout of a capsule file looks like this:
> >
> > EFI_CAPSULE_HEADER
> > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
> > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
> > EFI_FIRMWARE_IMAGE_AUTHENTICATION
> >
> > These structures would be used by the Platform Firmware Runtime Update
> > driver to parse the format of capsule file to verify if the corresponding
> > version number is valid. The EFI_CAPSULE_HEADER has been defined in the
> > kernel, however the rest are not, thus introduce corresponding UEFI structures
> > accordingly.
> >
> > The reason why efi_manage_capsule_header_t and efi_manage_capsule_image_header_t
> > are packedi might be that:
> > According to the uefi spec,
> > [Figure 23-6 Firmware Management and Firmware Image Management headers]
> > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER is located at the lowest offset within
> > the body of the capsule. And this structure is designed to be unaligned to save
> > space, because in this way the adjacent drivers and binary payload elements could
> > start on byte boundary with no padding. And the
> > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is at the head of each payload, so
> > packing this structure also makes room for more data.
> >
> > Signed-off-by: Chen Yu <[email protected]>
>
> Who is going to use these definitions? I only see this patch, where is
> the rest of the series?
>
Sorry, Ard, and thanks for taking a look at this patch.
The user of this definitions is an ACPI device driver, named Platform Firmware
Runtime Update driver. This driver is designed to do firmware update via SMI.
This driver will do sanity check of the capsule file using these structures before
the file is passed to SMI. We put this check in kernel space rather than leaving
it to SMI because the latter is very costly.
The series patch is at:
https://patchwork.kernel.org/project/linux-acpi/list/?series=543211
and the user is mainly PATCH 3/5:
https://patchwork.kernel.org/project/linux-acpi/patch/9b2bd7d1e40633ce6f4845fb5c9e30a3faad5e7a.1631025237.git.yu.c.chen@intel.com/
in valid_version().
thanks,
Chenyu
On Tue, Sep 07, 2021 at 11:40:30PM +0800, Chen Yu wrote:
> Introduce a simple test for Platform Firmware Runtime Update and Telemetry
> drivers. It is based on ioctl to either update firmware driver or code injection,
> and read corresponding PFRU Telemetry log into user space.
>
> For example:
>
> ./pfru_test -h
> usage: pfru_test [OPTIONS]
> code injection:
> -l, --load
> -s, --stage
> -a, --activate
> -u, --update [stage and activate]
> -q, --query
> -d, --revid update
> telemetry:
> -G, --getloginfo
> -T, --type(0:execution, 1:history)
> -L, --level(0, 1, 2, 4)
> -R, --read
> -D, --revid log
>
> ./pfru_test -G
> log_level:4
> log_type:0
> log_revid:2
> max_data_size:65536
> chunk1_size:0
> chunk2_size:1401
> rollover_cnt:0
> reset_cnt:4
>
> ./pfru_test -q
> code injection image type:794bf8b2-6e7b-454e-885f-3fb9bb185402
> fw_version:0
> code_rt_version:1
> driver update image type:0e5f0b14-f849-7945-ad81-bc7b6d2bb245
> drv_rt_version:0
> drv_svn:0
> platform id:39214663-b1a8-4eaa-9024-f2bb53ea4723
> oem id:a36db54f-ea2a-e14e-b7c4-b5780e51ba3d
>
> Tested-by: Dou Shengnan <[email protected]>
> Signed-off-by: Chen Yu <[email protected]>
> ---
> tools/testing/selftests/Makefile | 1 +
> tools/testing/selftests/pfru/Makefile | 7 +
> tools/testing/selftests/pfru/config | 2 +
> tools/testing/selftests/pfru/pfru.h | 152 +++++++++++
> tools/testing/selftests/pfru/pfru_test.c | 324 +++++++++++++++++++++++
> 5 files changed, 486 insertions(+)
> create mode 100644 tools/testing/selftests/pfru/Makefile
> create mode 100644 tools/testing/selftests/pfru/config
> create mode 100644 tools/testing/selftests/pfru/pfru.h
> create mode 100644 tools/testing/selftests/pfru/pfru_test.c
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index fb010a35d61a..c8b53a2c4450 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -56,6 +56,7 @@ TARGETS += seccomp
> TARGETS += sgx
> TARGETS += sigaltstack
> TARGETS += size
> +TARGETS += pfru
> TARGETS += sparc64
> TARGETS += splice
> TARGETS += static_keys
> diff --git a/tools/testing/selftests/pfru/Makefile b/tools/testing/selftests/pfru/Makefile
> new file mode 100644
> index 000000000000..c61916ccf637
> --- /dev/null
> +++ b/tools/testing/selftests/pfru/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +
> +CFLAGS += -Wall -O2
> +LDLIBS := -luuid
> +
> +TEST_GEN_PROGS := pfru_test
> +include ../lib.mk
> diff --git a/tools/testing/selftests/pfru/config b/tools/testing/selftests/pfru/config
> new file mode 100644
> index 000000000000..37f53609acbd
> --- /dev/null
> +++ b/tools/testing/selftests/pfru/config
> @@ -0,0 +1,2 @@
> +CONFIG_ACPI_PFRU=m
> +CONFIG_ACPI_PFRU_TELEMETRY=m
> diff --git a/tools/testing/selftests/pfru/pfru.h b/tools/testing/selftests/pfru/pfru.h
> new file mode 100644
> index 000000000000..8cd4ed80b161
> --- /dev/null
> +++ b/tools/testing/selftests/pfru/pfru.h
> @@ -0,0 +1,152 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Platform Firmware Runtime Update header
> + *
> + * Copyright(c) 2021 Intel Corporation. All rights reserved.
> + */
> +#ifndef __PFRU_H__
> +#define __PFRU_H__
> +
> +#include <linux/ioctl.h>
> +#include <uuid/uuid.h>
> +
> +#define PFRU_UUID "ECF9533B-4A3C-4E89-939E-C77112601C6D"
> +#define PFRU_CODE_INJ_UUID "B2F84B79-7B6E-4E45-885F-3FB9BB185402"
> +#define PFRU_DRV_UPDATE_UUID "4569DD8C-75F1-429A-A3D6-24DE8097A0DF"
> +
> +#define FUNC_STANDARD_QUERY 0
> +#define FUNC_QUERY_UPDATE_CAP 1
> +#define FUNC_QUERY_BUF 2
> +#define FUNC_START 3
> +
> +#define CODE_INJECT_TYPE 1
> +#define DRIVER_UPDATE_TYPE 2
> +
> +#define REVID_1 1
> +#define REVID_2 2
> +
> +#define PFRU_MAGIC 0xEE
> +
> +#define PFRU_IOC_SET_REV _IOW(PFRU_MAGIC, 0x01, unsigned int)
> +#define PFRU_IOC_STAGE _IOW(PFRU_MAGIC, 0x02, unsigned int)
> +#define PFRU_IOC_ACTIVATE _IOW(PFRU_MAGIC, 0x03, unsigned int)
> +#define PFRU_IOC_STAGE_ACTIVATE _IOW(PFRU_MAGIC, 0x04, unsigned int)
> +
> +static inline int valid_revid(int id)
> +{
> + return (id == REVID_1) || (id == REVID_2);
> +}
> +
> +/* Capsule file payload header */
> +struct payload_hdr {
> + __u32 sig;
> + __u32 hdr_version;
> + __u32 hdr_size;
> + __u32 hw_ver;
> + __u32 rt_ver;
> + uuid_t platform_id;
> +};
> +
> +enum start_action {
> + START_STAGE,
> + START_ACTIVATE,
> + START_STAGE_ACTIVATE,
> +};
> +
> +enum dsm_status {
> + DSM_SUCCEED,
> + DSM_FUNC_NOT_SUPPORT,
> + DSM_INVAL_INPUT,
> + DSM_HARDWARE_ERR,
> + DSM_RETRY_SUGGESTED,
> + DSM_UNKNOWN,
> + DSM_FUNC_SPEC_ERR,
> +};
> +
> +struct update_cap_info {
> + enum dsm_status status;
> + int update_cap;
> +
> + uuid_t code_type;
> + int fw_version;
> + int code_rt_version;
> +
> + uuid_t drv_type;
> + int drv_rt_version;
> + int drv_svn;
> +
> + uuid_t platform_id;
> + uuid_t oem_id;
> +
> + char oem_info[];
> +};
> +
> +struct com_buf_info {
> + enum dsm_status status;
> + enum dsm_status ext_status;
> + unsigned long addr_lo;
> + unsigned long addr_hi;
> + int buf_size;
> +};
> +
> +struct capsulate_buf_info {
> + unsigned long src;
> + int size;
> +};
> +
> +struct updated_result {
> + enum dsm_status status;
> + enum dsm_status ext_status;
> + unsigned long low_auth_time;
> + unsigned long high_auth_time;
> + unsigned long low_exec_time;
> + unsigned long high_exec_time;
> +};
Most of these types and constants seem to be a copy of uapu/linux/pfru.h.
Shouldn't the test get them from there?
> +
> +#define PFRU_TELEMETRY_UUID "75191659-8178-4D9D-B88F-AC5E5E93E8BF"
> +
> +/* Telemetry structures. */
> +struct telem_data_info {
> + enum dsm_status status;
> + enum dsm_status ext_status;
> + /* Maximum supported size of data of
> + * all Data Chunks combined.
> + */
> + unsigned long chunk1_addr_lo;
> + unsigned long chunk1_addr_hi;
> + unsigned long chunk2_addr_lo;
> + unsigned long chunk2_addr_hi;
> + int max_data_size;
> + int chunk1_size;
> + int chunk2_size;
> + int rollover_cnt;
> + int reset_cnt;
> +};
> +
> +struct telem_info {
> + int log_level;
> + int log_type;
> + int log_revid;
> +};
> +
> +/* Two logs: history and execution log */
> +#define LOG_EXEC_IDX 0
> +#define LOG_HISTORY_IDX 1
> +#define NR_LOG_TYPE 2
> +
> +#define LOG_ERR 0
> +#define LOG_WARN 1
> +#define LOG_INFO 2
> +#define LOG_VERB 4
> +
> +#define FUNC_SET_LEV 1
> +#define FUNC_GET_LEV 2
> +#define FUNC_GET_DATA 3
> +
> +#define LOG_NAME_SIZE 10
> +
> +#define PFRU_LOG_IOC_SET_INFO _IOW(PFRU_MAGIC, 0x05, struct telem_info)
> +#define PFRU_LOG_IOC_GET_INFO _IOR(PFRU_MAGIC, 0x06, struct telem_info)
> +#define PFRU_LOG_IOC_GET_DATA_INFO _IOR(PFRU_MAGIC, 0x07, struct telem_data_info)
> +
> +#endif /* __PFRU_H__ */
> diff --git a/tools/testing/selftests/pfru/pfru_test.c b/tools/testing/selftests/pfru/pfru_test.c
> new file mode 100644
> index 000000000000..d24d79d3836e
> --- /dev/null
> +++ b/tools/testing/selftests/pfru/pfru_test.c
> @@ -0,0 +1,324 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Tests Runtime Update/Telemetry (see Documentation/x86/pfru_update.rst)
> + */
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <getopt.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include "pfru.h"
> +
> +#define MAX_LOG_SIZE 65536
> +
> +struct update_cap_info cap_info;
> +struct com_buf_info buf_info;
> +struct capsulate_buf_info image_info;
> +struct telem_data_info data_info;
> +char *capsule_name;
> +int action, query_cap, log_type, log_level, log_read, log_getinfo,
> + revid, log_revid;
> +int set_log_level, set_log_type,
> + set_revid, set_log_revid;
> +
> +char *progname;
> +
> +static int valid_log_level(int level)
> +{
> + return (level == LOG_ERR) || (level == LOG_WARN) ||
> + (level == LOG_INFO) || (level == LOG_VERB);
> +}
> +
> +static int valid_log_type(int type)
> +{
> + return (type == LOG_EXEC_IDX) || (type == LOG_HISTORY_IDX);
> +}
> +
> +static void help(void)
> +{
> + fprintf(stderr,
> + "usage: %s [OPTIONS]\n"
> + " code injection:\n"
> + " -l, --load\n"
> + " -s, --stage\n"
> + " -a, --activate\n"
> + " -u, --update [stage and activate]\n"
> + " -q, --query\n"
> + " -d, --revid update\n"
> + " telemetry:\n"
> + " -G, --getloginfo\n"
> + " -T, --type(0:execution, 1:history)\n"
> + " -L, --level(0, 1, 2, 4)\n"
> + " -R, --read\n"
> + " -D, --revid log\n",
> + progname);
> +}
> +
> +char *option_string = "l:sauqd:GT:L:RD:h";
> +static struct option long_options[] = {
> + {"load", required_argument, 0, 'l'},
> + {"stage", no_argument, 0, 's'},
> + {"activate", no_argument, 0, 'a'},
> + {"update", no_argument, 0, 'u'},
> + {"query", no_argument, 0, 'q'},
> + {"getloginfo", no_argument, 0, 'G'},
> + {"type", required_argument, 0, 'T'},
> + {"level", required_argument, 0, 'L'},
> + {"read", no_argument, 0, 'R'},
> + {"setrev", required_argument, 0, 'd'},
> + {"setrevlog", required_argument, 0, 'D'},
> + {"help", no_argument, 0, 'h'},
> + {}
> +};
> +
> +static void parse_options(int argc, char **argv)
> +{
> + char *pathname;
> + int c;
> +
> + pathname = strdup(argv[0]);
> + progname = basename(pathname);
> +
> + while (1) {
> + int option_index = 0;
> +
> + c = getopt_long(argc, argv, option_string,
> + long_options, &option_index);
> + if (c == -1)
> + break;
> + switch (c) {
> + case 'l':
> + capsule_name = optarg;
> + break;
> + case 's':
> + action = 1;
> + break;
> + case 'a':
> + action = 2;
> + break;
> + case 'u':
> + action = 3;
> + break;
> + case 'q':
> + query_cap = 1;
> + break;
> + case 'G':
> + log_getinfo = 1;
> + break;
> + case 'T':
> + log_type = atoi(optarg);
> + set_log_type = 1;
> + break;
> + case 'L':
> + log_level = atoi(optarg);
> + set_log_level = 1;
> + break;
> + case 'R':
> + log_read = 1;
> + break;
> + case 'd':
> + revid = atoi(optarg);
> + set_revid = 1;
> + break;
> + case 'D':
> + log_revid = atoi(optarg);
> + set_log_revid = 1;
> + break;
> + case 'h':
> + help();
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +void print_cap(struct update_cap_info *cap)
> +{
> + char *uuid = malloc(37);
> +
> + if (!uuid) {
> + perror("Can not allocate uuid buffer\n");
> + exit(1);
> + }
> + uuid_unparse(cap->code_type, uuid);
> + printf("code injection image type:%s\n", uuid);
> + printf("fw_version:%d\n", cap->fw_version);
> + printf("code_rt_version:%d\n", cap->code_rt_version);
> +
> + uuid_unparse(cap->drv_type, uuid);
> + printf("driver update image type:%s\n", uuid);
> + printf("drv_rt_version:%d\n", cap->drv_rt_version);
> + printf("drv_svn:%d\n", cap->drv_svn);
> +
> + uuid_unparse(cap->platform_id, uuid);
> + printf("platform id:%s\n", uuid);
> + uuid_unparse(cap->oem_id, uuid);
> + printf("oem id:%s\n", uuid);
> +
> + free(uuid);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int fd_update, fd_log, fd_capsule;
> + struct telem_data_info data_info;
> + struct telem_info info;
> + struct update_cap_info cap;
> + void *addr_map_capsule;
> + struct stat st;
> + char *log_buf;
> + int ret = 0;
> +
> + parse_options(argc, argv);
> +
> + fd_log = open("/dev/pfru/telemetry", O_RDWR);
> + if (fd_log < 0) {
> + perror("Cannot open telemetry device...");
> + return 1;
> + }
> + fd_update = open("/dev/pfru/update", O_RDWR);
> + if (fd_update < 0) {
> + perror("Cannot open code injection device...");
> + return 1;
> + }
> +
> + if (query_cap) {
> + ret = read(fd_update, &cap, sizeof(cap));
> + if (ret == -1) {
> + perror("Read error.");
> + return 1;
> + }
> + print_cap(&cap);
> + }
> +
> + if (log_getinfo) {
> + ret = ioctl(fd_log, PFRU_LOG_IOC_GET_DATA_INFO, &data_info);
> + if (ret) {
> + perror("Get log data info failed.");
> + return 1;
> + }
> + ret = ioctl(fd_log, PFRU_LOG_IOC_GET_INFO, &info);
> + if (ret) {
> + perror("Get log info failed.");
> + return 1;
> + }
> + printf("log_level:%d\n", info.log_level);
> + printf("log_type:%d\n", info.log_type);
> + printf("log_revid:%d\n", info.log_revid);
> + printf("max_data_size:%d\n", data_info.max_data_size);
> + printf("chunk1_size:%d\n", data_info.chunk1_size);
> + printf("chunk2_size:%d\n", data_info.chunk2_size);
> + printf("rollover_cnt:%d\n", data_info.rollover_cnt);
> + printf("reset_cnt:%d\n", data_info.reset_cnt);
> +
> + return 0;
> + }
> +
> + info.log_level = -1;
> + info.log_type = -1;
> + info.log_revid = -1;
> +
> + if (set_log_level) {
> + if (!valid_log_level(log_level)) {
> + printf("Invalid log level %d\n",
> + log_level);
> + } else {
> + info.log_level = log_level;
> + }
> + }
> + if (set_log_type) {
> + if (!valid_log_type(log_type)) {
> + printf("Invalid log type %d\n",
> + log_type);
> + } else {
> + info.log_type = log_type;
> + }
> + }
> + if (set_log_revid) {
> + if (!valid_revid(log_revid)) {
> + printf("Invalid log revid %d\n",
> + log_revid);
> + } else {
> + info.log_revid = log_revid;
> + }
> + }
> +
> + ret = ioctl(fd_log, PFRU_LOG_IOC_SET_INFO, &info);
> + if (ret) {
> + perror("Log information set failed.(log_level, log_type, log_revid)");
> + return 1;
> + }
> +
> + if (set_revid) {
> + ret = ioctl(fd_update, PFRU_IOC_SET_REV, &revid);
> + if (ret) {
> + perror("mru update revid set failed");
> + return 1;
> + }
> + printf("mru update revid set to %d\n", revid);
> + }
> +
> + if (capsule_name) {
> + fd_capsule = open(capsule_name, O_RDONLY);
> + if (fd_capsule < 0) {
> + perror("Can not open capsule file...");
> + return 1;
> + }
> + if (fstat(fd_capsule, &st) < 0) {
> + perror("Can not fstat capsule file...");
> + return 1;
> + }
> + addr_map_capsule = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
> + fd_capsule, 0);
> + if (addr_map_capsule == MAP_FAILED) {
> + perror("Failed to mmap capsule file.");
> + return 1;
> + }
> + ret = write(fd_update, (char *)addr_map_capsule, st.st_size);
> + printf("Load %d bytes of capsule file into the system\n",
> + ret);
> + if (ret == -1) {
> + perror("Failed to load capsule file");
> + return 1;
> + }
> + munmap(addr_map_capsule, st.st_size);
> + printf("Load done.\n");
> + }
> +
> + if (action) {
> + if (action == 1)
> + ret = ioctl(fd_update, PFRU_IOC_STAGE, NULL);
> + else if (action == 2)
> + ret = ioctl(fd_update, PFRU_IOC_ACTIVATE, NULL);
> + else if (action == 3)
> + ret = ioctl(fd_update, PFRU_IOC_STAGE_ACTIVATE, NULL);
> + else
> + return 1;
> + printf("Update finished, return %d\n", ret);
> + }
> +
> + if (log_read) {
> + log_buf = malloc(MAX_LOG_SIZE + 1);
> + if (!log_buf) {
> + perror("log_buf allocate failed.");
> + return 1;
> + }
> + ret = read(fd_log, log_buf, MAX_LOG_SIZE);
> + if (ret == -1) {
> + perror("Read error.");
> + return 1;
> + }
> + log_buf[ret] = '\0';
> + printf("%s\n", log_buf);
> + free(log_buf);
> + }
> +
> + return 0;
> +}
> --
> 2.25.1
>
--
Sincerely yours,
Mike.
Hi Shuah, thank you for taking a look at this,
On Tue, Sep 07, 2021 at 03:28:52PM -0600, Shuah Khan wrote:
> On 9/7/21 9:40 AM, Chen Yu wrote:
> > Introduce a simple test for Platform Firmware Runtime Update and Telemetry
> > drivers. It is based on ioctl to either update firmware driver or code injection,
> > and read corresponding PFRU Telemetry log into user space.
> >
>
> A few things to consider and add handling for them in the
> test.
>
> What happens when non-root user runs this test?
Currently the code does not distinguish between root and non-root. The
next version will terminate if the user is non-root.
> What happens when the pfru device doesn't exist?
>
Currently the code terminates if either pfru_update or pfru_telemetry
device was not found.
>
> [snip]
>
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > + int fd_update, fd_log, fd_capsule;
> > + struct telem_data_info data_info;
> > + struct telem_info info;
> > + struct update_cap_info cap;
> > + void *addr_map_capsule;
> > + struct stat st;
> > + char *log_buf;
> > + int ret = 0;
> > +
> > + parse_options(argc, argv);
> > +
> > + fd_log = open("/dev/pfru/telemetry", O_RDWR);
> > + if (fd_log < 0) {
> > + perror("Cannot open telemetry device...");
> > + return 1;
> > + }
>
> Is this considered an error or unsupported?
>
> > + fd_update = open("/dev/pfru/update", O_RDWR);
> > + if (fd_update < 0) {
> > + perror("Cannot open code injection device...");
> > + return 1;
> > + }
> > +
>
> Same here. If test is run on platform with pfru test should skip
> instead of reporting failure/error.
>
Okay, got it. The next version will do the following to fix this:
1. If the pfru_update device is not found, the test will terminate.
This is because the pfru_update driver is the fundamental driver.
If this driver is not detected, there would be no information at all.
2. If the pfru_telemetry device is not found, the test will skip
the log setting/retrieving. Since the pfru_telemetry driver
is optional, the user can still update the firmware without
checking the telemetry log.
Thanks,
Chenyu
Hi Mike,
On Wed, Sep 08, 2021 at 12:08:40PM +0300, Mike Rapoport wrote:
> On Tue, Sep 07, 2021 at 11:40:30PM +0800, Chen Yu wrote:
> > Introduce a simple test for Platform Firmware Runtime Update and Telemetry
> > drivers. It is based on ioctl to either update firmware driver or code injection,
> > and read corresponding PFRU Telemetry log into user space.
> >
[snip..
> > +struct updated_result {
> > + enum dsm_status status;
> > + enum dsm_status ext_status;
> > + unsigned long low_auth_time;
> > + unsigned long high_auth_time;
> > + unsigned long low_exec_time;
> > + unsigned long high_exec_time;
> > +};
>
> Most of these types and constants seem to be a copy of uapu/linux/pfru.h.
> Shouldn't the test get them from there?
>
Yes they have shared structures. The next version will reuse the uapi header.
thanks,
Chenyu