Skip to contentSkip to conversation

Introduce Platform Firmware Runtime Update and Telemetry drivers

PATCH Superseded Chen Yu 2 participants 10 messages Oct 22, 2021 – Oct 23, 2021

Subsystems (4)

v5 of 13 Superseded Latest is v13 →

lore
Share & tools
lore

Chen Yu

author
#0 lore raw

v5 cover Introduce Platform Firmware Runtime Update and Telemetry drivers

The PFRU (Platform Firmware Runtime Update) kernel interface is designed to interact with the platform firmware interface defined in the `Management Mode Firmware Runtime Update
<https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf>`
specification. The primary function of PFRU is to carry out runtime updates of the platform firmware, which doesn't require the system to be restarted. It also allows telemetry data to be retrieved from the platform firmware.

=============
- Change from v4 to v5:
- Remove Documentation/ABI/pfru, and move the content to kernel doc in include/uapi/linux/pfru.h (Greg Kroah-Hartman)
Shrink the range of ioctl numbers declared in Documentation/userspace-api/ioctl/ioctl-number.rst
from 16 to 8. (Greg Kroah-Hartman)
Change global variable struct pfru_device *pfru_dev to
per PFRU device. (Greg Kroah-Hartman)
Unregister the misc device in acpi_pfru_remove().
(Greg Kroah-Hartman)
Convert the kzalloc() to devm_kzalloc() in the driver so as to avoid freeing the memory. (Greg Kroah-Hartman)
Fix the compile warning by declaring the pfru_log_ioctl() as static. (kernel test robot LKP)
Change to global variable misc_device to per PFRU device. (Greg Kroah-Hartman)
Remove the telemetry output in commit log. (Greg Kroah-Hartman) Add link for corresponding userspace tool in the commit log. (Greg Kroah-Hartman)
Replace the telemetry .read() with .mmap() so that the userspace could mmap once, and read multiple times. (Greg Kroah-Hartman)
- Change from v3 to v4:
- Add Documentation/ABI/testing/pfru to document the ABI and remove Documentation/x86/pfru.rst (Rafael J. Wysocki)
- Replace all pr_err() with dev_dbg() (Greg Kroah-Hartman, Rafael J. Wysocki)
- returns ENOTTY rather than ENOIOCTLCMD if invalid ioctl command is provided. (Greg Kroah-Hartman)
- Remove compat ioctl. (Greg Kroah-Hartman)
- Rename /dev/pfru/pfru_update to /dev/acpi_pfru (Greg Kroah-Hartman)
- Simplify the check for element of the package in query_capability() (Rafael J. Wysocki)
- Remove the loop in query_capability(), query_buffer() and query the package elemenet directly. (Rafael J. Wysocki)
- Check the number of elements in case the number of package elements is too small. (Rafael J. Wysocki)
- Doing the assignment as initialization in get_image_type(). Meanwhile, returns the type or a negative error code in get_image_type(). (Rafael J. Wysocki)
- Put the comments inside the function. (Rafael J. Wysocki)
- Returns the size or a negative error code in adjust_efi_size() (Rafael J. Wysocki)
- Fix the return value from EFAULT to EINVAL if pfru_valid_revid() does not pass. (Rafael J. Wysocki)
- Change the write() to be the code injection/update, the read() to be telemetry retrieval and all of the rest to be ioctl()s under one special device file.(Rafael J. Wysocki)
- Remove redundant parens. (Rafael J. Wysocki)
- Putting empty code lines after an if () statement that is not followed by a block. (Rafael J. Wysocki)
- Remove "goto" tags to make the code more readable. (Rafael J. Wysocki)
- Change from v2 to v3:
- Use valid types for structures that cross the user/kernel boundary in the uapi header. (Greg Kroah-Hartman)
- Rename the structure in uapi to start with a prefix pfru so as to avoid confusing in the global namespace. (Greg Kroah-Hartman)
- Change from v1 to v2:
- Add a spot in index.rst so it becomes part of the docs build (Jonathan Corbet).
- Sticking to the 80-column limit(Jonathan Corbet).
- Underline lengths should match the title text(Jonathan Corbet).
- Use literal blocks for the code samples(Jonathan Corbet).
- Add sanity check for duplicated instance of ACPI device.
- Update the driver to work with allocated pfru_device objects. (Mike Rapoport)
- For each switch case pair, get rid of the magic case numbers and add a default clause with the error handling.(Mike Rapoport)
- Move the obj->type checks outside the switch to reduce redundancy. (Mike Rapoport)
- Parse the code_inj_id and drv_update_id at driver initialization time to reduce the re-parsing at runtime. (Mike Rapoport)
- Explain in detail how the size needs to be adjusted when doing version check. (Mike Rapoport)
- Rename parse_update_result() to dump_update_result()
(Mike Rapoport)
- Remove redundant return.(Mike Rapoport)
- Do not expose struct capsulate_buf_info to uapi, since it is not needed in userspace. (Mike Rapoport)
- Do not allow non-root user to run this test.(Shuah Khan)
- Test runs on platform without pfru_telemetry should skip instead of reporting failure/error.(Shuah Khan)
- Reuse uapi/linux/pfru.h instead of copying it into the test directory. (Mike Rapoport)

Chen Yu (4):
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
tools: Introduce power/acpi/pfru/pfru

 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 drivers/acpi/Kconfig                          |    1 +
 drivers/acpi/Makefile                         |    1 +
 drivers/acpi/pfru/Kconfig                     |   16 +
 drivers/acpi/pfru/Makefile                    |    2 +
 drivers/acpi/pfru/pfru_update.c               | 1010 +++++++++++++++++
 include/linux/efi.h                           |   50 +
 include/uapi/linux/pfru.h                     |  283 +++++
 tools/power/acpi/pfru/Makefile                |   25 +
 tools/power/acpi/pfru/pfru.8                  |  137 +++
 tools/power/acpi/pfru/pfru.c                  |  407 +++++++
 11 files changed, 1933 insertions(+)
 create mode 100644 drivers/acpi/pfru/Kconfig
 create mode 100644 drivers/acpi/pfru/Makefile
 create mode 100644 drivers/acpi/pfru/pfru_update.c
 create mode 100644 include/uapi/linux/pfru.h
 create mode 100644 tools/power/acpi/pfru/Makefile
 create mode 100644 tools/power/acpi/pfru/pfru.8
 create mode 100644 tools/power/acpi/pfru/pfru.c
Signature · 1 line
2.25.1
author
#1 lore raw

PATCH v5 1/4 efi: Introduce EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and corresponding structures

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. Besides, EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER need not be aligned and so the corresponding data types should be packed.

---
v5: No change since v4.
v4: Revise the commit log to make it more clear. (Rafael J. Wysocki)

---
 include/linux/efi.h | 50 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
include/linux/efi.h +50 -0
index 6b5d36babfcc..19ff834e1388 100644
@@ -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
  */
Signature · 1 line
2.25.1
author
#2 lore raw

PATCH v5 2/4 drivers/acpi: Introduce Platform Firmware Runtime Update device driver

In reply to Chen Yu

Introduce the pfru_update driver which can be used for Platform Firmware Runtime code injection and driver update[1]. The user is expected to provide the update firmware in the form of capsule file, and pass it to the driver via ioctl. Then the driver would hand this capsule file to the Platform Firmware Runtime Update via the ACPI device _DSM method. At last the low level Management Mode would do the firmware update.

The corresponding userspace tool and man page will be introduced at tools/power/acpi/pfru.

[1] https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf

---
v5: Remove Documentation/ABI/pfru, and move the content to kernel doc
in include/uapi/linux/pfru.h (Greg Kroah-Hartman)
Shrink the range of ioctl numbers declared in Documentation/userspace-api/ioctl/ioctl-number.rst
from 16 to 8. (Greg Kroah-Hartman)
Change global variable struct pfru_device *pfru_dev to
per ACPI device. (Greg Kroah-Hartman)
Unregister the misc device in acpi_pfru_remove().
(Greg Kroah-Hartman)
Convert the kzalloc() to devm_kzalloc() in the driver so as to avoid freeing the memory. (Greg Kroah-Hartman)
Fix the compile error by declaring the pfru_log_ioctl() as static. (kernel test robot LKP)
Change to global variable misc_device to per ACPI device. (Greg Kroah-Hartman)
v4: Replace all pr_err() with dev_dbg() (Greg Kroah-Hartman,
Rafael J. Wysocki)
Returns ENOTTY rather than ENOIOCTLCMD if invalid ioctl command is provided. (Greg Kroah-Hartman)
Remove compat ioctl. (Greg Kroah-Hartman)
Rename /dev/pfru/pfru_update to /dev/acpi_pfru (Greg Kroah-Hartman) Simplify the check for element of the package in query_capability() (Rafael J. Wysocki)
Remove the loop in query_capability(), query_buffer() and query the package elemenet directly. (Rafael J. Wysocki)
Check the the number of elements in case the number of package elements is too small. (Rafael J. Wysocki)
Doing the assignment as initialization in get_image_type(). Meanwhile, returns the type or a negative error code in get_image_type(). (Rafael J. Wysocki)
Put the comments inside the function. (Rafael J. Wysocki) Returns the size or a negative error code in adjust_efi_size() (Rafael J. Wysocki)
Fix the return value from EFAULT to EINVAL if pfru_valid_revid() does not pass. (Rafael J. Wysocki)
Change the write() to be the code injection/update, the read() to be telemetry retrieval and all of the rest to be ioctl()s under one special device file.(Rafael J. Wysocki)
Putting empty code lines after an if () statement that is not followed by a block. (Rafael J. Wysocki)
Remove "goto" tags to make the code more readable. (Rafael J. Wysocki)
v3: Use __u32 instead of int and __64 instead of unsigned long
in include/uapi/linux/pfru.h (Greg Kroah-Hartman)
Rename the structure in uapi to start with a prefix pfru so as to avoid confusing in the global namespace. (Greg Kroah-Hartman)
v2: Add sanity check for duplicated instance of ACPI device.
Update the driver to work with allocated pfru_device objects. (Mike Rapoport)
For each switch case pair, get rid of the magic case numbers and add a default clause with the error handling.
(Mike Rapoport)
Move the obj->type checks outside the switch to reduce redundancy. (Mike Rapoport)
Parse the code_inj_id and drv_update_id at driver initialization time to reduce the re-parsing at runtime.(Mike Rapoport) Explain in detail how the size needs to be adjusted when doing version check.(Mike Rapoport)
Rename parse_update_result() to dump_update_result()(Mike Rapoport) Remove redundant return.(Mike Rapoport)
Do not expose struct capsulate_buf_info to uapi, since it is not needed in userspace.(Mike Rapoport)

---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 drivers/acpi/Kconfig                          |   1 +
 drivers/acpi/Makefile                         |   1 +
 drivers/acpi/pfru/Kconfig                     |  13 +
 drivers/acpi/pfru/Makefile                    |   2 +
 drivers/acpi/pfru/pfru_update.c               | 584 ++++++++++++++++++
 include/uapi/linux/pfru.h                     | 184 ++++++
 7 files changed, 789 insertions(+)
 create mode 100644 drivers/acpi/pfru/Kconfig
 create mode 100644 drivers/acpi/pfru/Makefile
 create mode 100644 drivers/acpi/pfru/pfru_update.c
 create mode 100644 include/uapi/linux/pfru.h
Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
index 2e8134059c87..6f7c86b6deb7 100644
@@ -365,6 +365,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <mailto:[email protected]>
 0xE5  00-3F  linux/fuse.h
 0xEC  00-01  drivers/platform/chrome/cros_ec_dev.h                   ChromeOS EC driver
+0xEE  00-08  uapi/linux/pfru.h                                       Platform Firmware Runtime Update and Telemetry
 0xF3  00-3F  drivers/usb/misc/sisusbvga/sisusb.h                     sisfb (in development)
                                                                      <mailto:[email protected]>
 0xF6  all                                                            LTTng Linux Trace Toolkit Next Generation
drivers/acpi/Kconfig +1 -0
index 1da360c51d66..1d8d2e2cefac 100644
@@ -482,6 +482,7 @@ source "drivers/acpi/nfit/Kconfig"
 source "drivers/acpi/numa/Kconfig"
 source "drivers/acpi/apei/Kconfig"
 source "drivers/acpi/dptf/Kconfig"
+source "drivers/acpi/pfru/Kconfig"
 
 config ACPI_WATCHDOG
 	bool
drivers/acpi/Makefile +1 -0
index 3018714e87d9..9c2c5ddff6ec 100644
@@ -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_PFRU)		+= pfru/
 
 # processor has its own "processor." module_param namespace
 processor-y			:= processor_driver.o
drivers/acpi/pfru/Kconfig new file +13 -0
new file mode 100644
index 000000000000..87388a46e760
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+config ACPI_PFRU
+	tristate "ACPI Platform Firmware Runtime Update (PFRU)"
+	depends on 64BIT
+	help
+	  In order to reduce the system reboot times and update the platform firmware
+	  in time, Platform Firmware Runtime Update is leveraged to patch the system
+	  without reboot. This driver supports Platform Firmware Runtime Update,
+	  which is composed of two parts: code injection and driver update. It also
+	  allows telemetry data to be retrieved from the platform firmware.
+
+	  To compile this driver as a module, choose M here:
+	  the module will be called pfru_update.
drivers/acpi/pfru/Makefile new file +2 -0
new file mode 100644
index 000000000000..098cbe80cf3d
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ACPI_PFRU) += pfru_update.o
drivers/acpi/pfru/pfru_update.c new file +584 -0

584 lines not on this page · raw message

include/uapi/linux/pfru.h new file +184 -0

184 lines not on this page · raw message

Signature · 1 line
2.25.1
#3 lore raw

Re: [PATCH v5 2/4] drivers/acpi: Introduce Platform Firmware Runtime Update device driver

Quoted 11 lines · Chen Yu

Introduce the pfru_update driver which can be used for Platform Firmware Runtime code injection and driver update[1]. The user is expected to provide the update firmware in the form of capsule file, and pass it to the driver via ioctl. Then the driver would hand this capsule file to the Platform Firmware Runtime Update via the ACPI device _DSM method. At last the low level Management Mode would do the firmware update.

The corresponding userspace tool and man page will be introduced at tools/power/acpi/pfru.

[1] https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf

Instead use Link: tag ?

...

Quoted 4 lines
drivers/acpi/pfru/pfru_update.c new file +4 -0
+enum cap_index {
+	CAP_STATUS_IDX,
+	CAP_UPDATE_IDX,
+	CAP_CODE_TYPE_IDX,

drivers/acpi/pfru/pfru_update.c line 36

Quoted 10 lines
drivers/acpi/pfru/pfru_update.c new file +9 -0
+	CAP_FW_VER_IDX,
+	CAP_CODE_RT_VER_IDX,
+	CAP_DRV_TYPE_IDX,
+	CAP_DRV_RT_VER_IDX,
+	CAP_DRV_SVN_IDX,
+	CAP_PLAT_ID_IDX,
+	CAP_OEM_ID_IDX,
+	CAP_OEM_INFO_IDX,
 
+	CAP_NR_IDX,

Is it terminator entry? Drop comma then. Same for all other similar cases.

Quoted 1 line

+};

...

drivers/acpi/pfru/pfru_update.c line 219

Quoted 13 lines
drivers/acpi/pfru/pfru_update.c new file +12 -0
+static int get_image_type(efi_manage_capsule_image_header_t *img_hdr,
+			  struct pfru_device *pfru_dev)
+{
+	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_dev->code_uuid))
+		return CODE_INJECT_TYPE;
 
+	else if (guid_equal(image_type_id, &pfru_dev->drv_uuid))
+		return DRIVER_UPDATE_TYPE;
+	else
+		return -EINVAL;

In both cases redundant 'else'.

Quoted 1 line

+}

...

Quoted 14 lines
drivers/acpi/pfru/pfru_update.c new file +14 -0
+static int adjust_efi_size(efi_manage_capsule_image_header_t *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(efi_manage_capsule_image_header_t);
+	switch (img_hdr->ver) {
+	case 1:
+		size -= 2 * sizeof(u64);

drivers/acpi/pfru/pfru_update.c line 245

Quoted 10 lines
drivers/acpi/pfru/pfru_update.c new file +9 -0
+		break;
+	case 2:
+		size -= sizeof(u64);
+		break;
+	default:
+		/* only support version 1 and 2 */
+		return -EINVAL;
+	}
 
+	return size;

Perhaps directly return this from the cases?

Quoted 1 line

+}

...

drivers/acpi/pfru/pfru_update.c line 383

Quoted 7 lines
drivers/acpi/pfru/pfru_update.c new file +6 -0
+static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct pfru_update_cap_info cap;
+	struct pfru_device *pfru_dev;
+	void __user *p;
 
+	int ret = 0, rev;

return 0; (see below)?

Quoted 27 lines
drivers/acpi/pfru/pfru_update.c new file +27 -0
+	pfru_dev = to_pfru_dev(file);
+	p = (void __user *)arg;
+
+	switch (cmd) {
+	case PFRU_IOC_QUERY_CAP:
+		ret = query_capability(&cap, pfru_dev);
+		if (ret)
+			return ret;
+
+		if (copy_to_user(p, &cap, sizeof(cap)))
+			return -EFAULT;
+
+		break;
+	case PFRU_IOC_SET_REV:
+		if (copy_from_user(&rev, p, sizeof(unsigned int)))
+			return -EFAULT;
+
+		if (!pfru_valid_revid(rev))
+			return -EINVAL;
+
+		pfru_dev->rev_id = rev;
+		break;
+	case PFRU_IOC_STAGE:
+		ret = start_acpi_update(START_STAGE, pfru_dev);
+		break;
+	case PFRU_IOC_ACTIVATE:
+		ret = start_acpi_update(START_ACTIVATE, pfru_dev);

drivers/acpi/pfru/pfru_update.c line 421

Quoted 10 lines
drivers/acpi/pfru/pfru_update.c new file +9 -0
+		break;
+	case PFRU_IOC_STAGE_ACTIVATE:
+		ret = start_acpi_update(START_STAGE_ACTIVATE, pfru_dev);
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
 
+	return ret;

You may return 0 here and directly return from the cases above.

Quoted 1 line

+}

...

drivers/acpi/pfru/pfru_update.c line 449

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	/* map the communication buffer */

Comment style inconsistency. Compare to...

drivers/acpi/pfru/pfru_update.c line 460

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	/* Check if the capsule header has a valid version number. */

...this one. However, here, since it's one line, no period is needed.

...

drivers/acpi/pfru/pfru_update.c line 550

Quoted 2 lines
drivers/acpi/pfru/pfru_update.c new file +2 -0
+out:
+	devm_kfree(&pdev->dev, pfru_dev);

What is this? WHy?!

Quoted 1 line

+}

...

drivers/acpi/pfru/pfru_update.c line 581

Quoted 12 lines
drivers/acpi/pfru/pfru_update.c new file +12 -0
+static int __init pfru_init(void)
+{
+	return platform_driver_register(&acpi_pfru_driver);
+}
+
+static void __exit pfru_exit(void)
+{
+	platform_driver_unregister(&acpi_pfru_driver);
+}
+
+module_init(pfru_init);
+module_exit(pfru_exit);

NIH module_platform_driver().

...

Quoted 1 line

+#include <linux/uuid.h>

This is wrong. Please, use raw buffers.

...

Missed types.h.

...

include/uapi/linux/pfru.h line 95

Quoted 8 lines
include/uapi/linux/pfru.h new file +7 -0
+struct pfru_payload_hdr {
+	__u32	sig;
+	__u32	hdr_version;
+	__u32	hdr_size;
+	__u32	hw_ver;
+	__u32	rt_ver;
 
+	uuid_t	platform_id;

No way. Use __u8[16].
uuid_t is internal type for kernel.

Quoted 1 line

+};

...

include/uapi/linux/pfru.h line 133

Quoted 5 lines
include/uapi/linux/pfru.h new file +5 -0
+struct pfru_update_cap_info {
+	enum pfru_dsm_status status;
+	__u32 update_cap;
+
+	uuid_t code_type;

Ditto.

include/uapi/linux/pfru.h line 137

Quoted 4 lines
include/uapi/linux/pfru.h new file +3 -0
+	__u32 fw_version;
+	__u32 code_rt_version;
 
+	uuid_t drv_type;

Ditto.

include/uapi/linux/pfru.h line 142

Quoted 5 lines
include/uapi/linux/pfru.h new file +4 -0
+	__u32 drv_rt_version;
+	__u32 drv_svn;
 
+	uuid_t platform_id;
+	uuid_t oem_id;

Ditto.

Quoted 2 lines
(quoted diff) +2 -0
+	char oem_info[];
+};

...

include/uapi/linux/pfru.h line 177

Quoted 4 lines
include/uapi/linux/pfru.h new file +3 -0
+struct pfru_updated_result {
 
+	enum pfru_dsm_status status;
+	enum pfru_dsm_status ext_status;

What?! Are you sure enum is the same size on all possible architectures?

Quoted 5 lines
include/uapi/linux/pfru.h new file +5 -0
+	__u64 low_auth_time;
+	__u64 high_auth_time;
+	__u64 low_exec_time;
+	__u64 high_exec_time;
+};
Signature · 2 lines
With Best Regards,
Andy Shevchenko
author
#4 lore raw

Re: [PATCH v5 2/4] drivers/acpi: Introduce Platform Firmware Runtime Update device driver

Hi Andy,

Quoted 8 lines · Andy Shevchenko
Quoted 7 lines · Chen Yu

Introduce the pfru_update driver which can be used for Platform Firmware Runtime code injection and driver update[1]. The user is expected to provide the update firmware in the form of capsule file, and pass it to the driver via ioctl. Then the driver would hand this capsule file to the Platform Firmware Runtime Update via the ACPI device _DSM method. At last the low level Management Mode would do the firmware update.

Quoted 7 lines
Quoted 4 lines

The corresponding userspace tool and man page will be introduced at tools/power/acpi/pfru.

[1] https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf

Instead use Link: tag ?

Yes, will fix it.

Quoted 13 lines
Quoted 12 lines
drivers/acpi/pfru/pfru_update.c new file +12 -0
+enum cap_index {
+	CAP_STATUS_IDX,
+	CAP_UPDATE_IDX,
+	CAP_CODE_TYPE_IDX,
+	CAP_FW_VER_IDX,
+	CAP_CODE_RT_VER_IDX,
+	CAP_DRV_TYPE_IDX,
+	CAP_DRV_RT_VER_IDX,
+	CAP_DRV_SVN_IDX,
+	CAP_PLAT_ID_IDX,
+	CAP_OEM_ID_IDX,
+	CAP_OEM_INFO_IDX,
Quoted 4 lines

drivers/acpi/pfru/pfru_update.c line 36

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	CAP_NR_IDX,

Is it terminator entry? Drop comma then. Same for all other similar cases.

Ok, will remove the comma.

Quoted 13 lines
Quoted 1 line

+};

...

Quoted 8 lines
drivers/acpi/pfru/pfru_update.c new file +8 -0
+static int get_image_type(efi_manage_capsule_image_header_t *img_hdr,
+			  struct pfru_device *pfru_dev)
+{
+	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_dev->code_uuid))
+		return CODE_INJECT_TYPE;
Quoted 7 lines

drivers/acpi/pfru/pfru_update.c line 219

Quoted 4 lines
drivers/acpi/pfru/pfru_update.c new file +4 -0
+	else if (guid_equal(image_type_id, &pfru_dev->drv_uuid))
+		return DRIVER_UPDATE_TYPE;
+	else
+		return -EINVAL;

In both cases redundant 'else'.

Ok.

Quoted 27 lines
Quoted 1 line

+}

...

Quoted 22 lines
drivers/acpi/pfru/pfru_update.c new file +22 -0
+static int adjust_efi_size(efi_manage_capsule_image_header_t *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(efi_manage_capsule_image_header_t);
+	switch (img_hdr->ver) {
+	case 1:
+		size -= 2 * sizeof(u64);
+		break;
+	case 2:
+		size -= sizeof(u64);
+		break;
+	default:
+		/* only support version 1 and 2 */
+		return -EINVAL;
+	}
Quoted 4 lines

drivers/acpi/pfru/pfru_update.c line 245

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	return size;

Perhaps directly return this from the cases?

Ok, will do.

Quoted 50 lines
Quoted 1 line

+}

...

drivers/acpi/pfru/pfru_update.c line 383

Quoted 7 lines
drivers/acpi/pfru/pfru_update.c new file +6 -0
+static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct pfru_update_cap_info cap;
+	struct pfru_device *pfru_dev;
+	void __user *p;
 
+	int ret = 0, rev;

return 0; (see below)?

Quoted 35 lines
drivers/acpi/pfru/pfru_update.c new file +35 -0
+	pfru_dev = to_pfru_dev(file);
+	p = (void __user *)arg;
+
+	switch (cmd) {
+	case PFRU_IOC_QUERY_CAP:
+		ret = query_capability(&cap, pfru_dev);
+		if (ret)
+			return ret;
+
+		if (copy_to_user(p, &cap, sizeof(cap)))
+			return -EFAULT;
+
+		break;
+	case PFRU_IOC_SET_REV:
+		if (copy_from_user(&rev, p, sizeof(unsigned int)))
+			return -EFAULT;
+
+		if (!pfru_valid_revid(rev))
+			return -EINVAL;
+
+		pfru_dev->rev_id = rev;
+		break;
+	case PFRU_IOC_STAGE:
+		ret = start_acpi_update(START_STAGE, pfru_dev);
+		break;
+	case PFRU_IOC_ACTIVATE:
+		ret = start_acpi_update(START_ACTIVATE, pfru_dev);
+		break;
+	case PFRU_IOC_STAGE_ACTIVATE:
+		ret = start_acpi_update(START_STAGE_ACTIVATE, pfru_dev);
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
Quoted 4 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	return ret;

You may return 0 here and directly return from the cases above.

I think we can remove the 'return ret' completely, by returning from the cases above.

Quoted 4 lines
Quoted 1 line

+}

...

Quoted 9 lines

drivers/acpi/pfru/pfru_update.c line 449

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	/* map the communication buffer */

Comment style inconsistency. Compare to...

drivers/acpi/pfru/pfru_update.c line 460

Quoted 1 line
drivers/acpi/pfru/pfru_update.c new file +1 -0
+	/* Check if the capsule header has a valid version number. */

...this one. However, here, since it's one line, no period is needed.

Ok, will fix it.

Quoted 7 lines

...

drivers/acpi/pfru/pfru_update.c line 550

Quoted 2 lines
drivers/acpi/pfru/pfru_update.c new file +2 -0
+out:
+	devm_kfree(&pdev->dev, pfru_dev);

What is this? WHy?!

The devm resource is expected to be released during driver de-attach. devm_kfree() is used to free the resource explicitly at the earliest time. But it is ok to remove the devm_kfree() and leverage the devm to deal with it.

Quoted 5 lines
Quoted 1 line

+}

...

Quoted 15 lines

drivers/acpi/pfru/pfru_update.c line 581

Quoted 12 lines
drivers/acpi/pfru/pfru_update.c new file +12 -0
+static int __init pfru_init(void)
+{
+	return platform_driver_register(&acpi_pfru_driver);
+}
+
+static void __exit pfru_exit(void)
+{
+	platform_driver_unregister(&acpi_pfru_driver);
+}
+
+module_init(pfru_init);
+module_exit(pfru_exit);

NIH module_platform_driver().

There would be more than one platform driver to be loaded in this file, so module_platform_driver() might not work here.

Quoted 6 lines

...

Quoted 1 line

+#include <linux/uuid.h>

This is wrong. Please, use raw buffers.

Ok, will remove this.

Quoted 4 lines

...

Missed types.h.

Ok, will add it.

Quoted 9 lines

...

Quoted 6 lines
include/uapi/linux/pfru.h new file +6 -0
+struct pfru_payload_hdr {
+	__u32	sig;
+	__u32	hdr_version;
+	__u32	hdr_size;
+	__u32	hw_ver;
+	__u32	rt_ver;
Quoted 5 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	uuid_t	platform_id;

No way. Use __u8[16].
uuid_t is internal type for kernel.

Ok, will change it.

Quoted 32 lines
Quoted 1 line

+};

...

include/uapi/linux/pfru.h line 133

Quoted 5 lines
include/uapi/linux/pfru.h new file +5 -0
+struct pfru_update_cap_info {
+	enum pfru_dsm_status status;
+	__u32 update_cap;
+
+	uuid_t code_type;

Ditto.

include/uapi/linux/pfru.h line 137

Quoted 4 lines
include/uapi/linux/pfru.h new file +3 -0
+	__u32 fw_version;
+	__u32 code_rt_version;
 
+	uuid_t drv_type;

Ditto.

include/uapi/linux/pfru.h line 142

Quoted 5 lines
include/uapi/linux/pfru.h new file +4 -0
+	__u32 drv_rt_version;
+	__u32 drv_svn;
 
+	uuid_t platform_id;
+	uuid_t oem_id;

Ditto.

Quoted 2 lines
(quoted diff) +2 -0
+	char oem_info[];
+};

...

Quoted 7 lines

include/uapi/linux/pfru.h line 177

Quoted 4 lines
include/uapi/linux/pfru.h new file +3 -0
+struct pfru_updated_result {
 
+	enum pfru_dsm_status status;
+	enum pfru_dsm_status ext_status;

What?! Are you sure enum is the same size on all possible architectures?

Ok, changed then to __u32 in next version.

thanks,
Chenyu

author
#5 lore raw

PATCH v5 3/4 drivers/acpi: Introduce Platform Firmware Runtime Update Telemetry

In reply to Chen Yu

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.

The corresponding userspace tool and man page will be introduced at tools/power/acpi/pfru.

---
v5: Remove the log output sample in commit log. (Greg Kroah-Hartman)
Add link for corresponding userspace tool in the commit log. (Greg Kroah-Hartman)
Replace the telemetry .read() with .mmap() so that the userspace could mmap once, and read multiple times. (Greg Kroah-Hartman) Fix the compile warning by declaring the pfru_log_ioctl() as static. (kernel test robot LKP)
v4: Change the write() to be the code injection/update, the read() to
be telemetry retrieval and all of the rest to be ioctl()s under one special device file.(Rafael J. Wysocki)
Remove redundant parens. (Rafael J. Wysocki)
Putting empty code lines after an if () statement that is not followed by a block. (Rafael J. Wysocki)
Remove "goto" tags to make the code more readable. (Rafael J. Wysocki)
v3: Use __u32 instead of int and __64 instead of unsigned long
in include/uapi/linux/pfru.h (Greg Kroah-Hartman)
Rename the structure in uapi to start with a prefix pfru so as to avoid confusing in the global namespace. (Greg Kroah-Hartman)
v2: Do similar clean up as pfru_update driver:
Add sanity check for duplicated instance of ACPI device. Update the driver to work with allocated telem_device objects. (Mike Rapoport)
For each switch case pair, get rid of the magic case numbers and add a default clause with the error handling.
(Mike Rapoport)

---
 drivers/acpi/pfru/pfru_update.c | 428 +++++++++++++++++++++++++++++++-
 include/uapi/linux/pfru.h       |  94 +++++++
 2 files changed, 521 insertions(+), 1 deletion(-)
drivers/acpi/pfru/pfru_update.c +427 -1
index 5f7b27486255..97931e0668fc 100644
@@ -14,6 +14,7 @@
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
+#include <linux/mm.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
@@ -55,6 +56,28 @@ enum update_index {
 	UPDATE_NR_IDX,
 };
 
+enum log_index {
+	LOG_STATUS_IDX,
+	LOG_EXT_STATUS_IDX,
+	LOG_MAX_SZ_IDX,
+	LOG_CHUNK1_LO_IDX,
+	LOG_CHUNK1_HI_IDX,
+	LOG_CHUNK1_SZ_IDX,
+	LOG_CHUNK2_LO_IDX,
+	LOG_CHUNK2_HI_IDX,
+	LOG_CHUNK2_SZ_IDX,
+	LOG_ROLLOVER_CNT_IDX,
+	LOG_RESET_CNT_IDX,
+	LOG_NR_IDX,
+};
+
+struct pfru_log_device {
+	struct device *dev;
+	guid_t uuid;
+	struct pfru_log_info info;
+	struct miscdevice miscdev;
+};
+
 struct pfru_device {
 	guid_t uuid, code_uuid, drv_uuid;
 	int rev_id;
@@ -67,6 +90,397 @@ static inline struct pfru_device *to_pfru_dev(struct file *file)
 	return container_of(file->private_data, struct pfru_device, miscdev);
 }
 
+/************************** telemetry begin ************************/
+
+static inline struct pfru_log_device *to_pfru_log_dev(struct file *file)
+{
+	return container_of(file->private_data, struct pfru_log_device, miscdev);
+}
+
+static int get_pfru_log_data_info(struct pfru_log_data_info *data_info,
+				  struct pfru_log_device *pfru_log_dev)
+{
+	union acpi_object *out_obj, in_obj, in_buf;
+	acpi_handle handle;
+	int ret = -EINVAL;
+
+	handle = ACPI_HANDLE(pfru_log_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 = pfru_log_dev->info.log_type;
+
+	out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid,
+					  pfru_log_dev->info.log_revid, FUNC_GET_DATA,
+					  &in_obj, ACPI_TYPE_PACKAGE);
+	if (!out_obj)
+		return ret;
+
+	if (out_obj->package.count < LOG_NR_IDX)
+		goto free_acpi_buffer;
+
+	if (out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->ext_status =
+		out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->max_data_size =
+		out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk1_addr_lo =
+		out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk1_addr_hi =
+		out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk1_size =
+		out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk2_addr_lo =
+		out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk2_addr_hi =
+		out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->chunk2_size =
+		out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->rollover_cnt =
+		out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value;
+
+	if (out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER)
+		goto free_acpi_buffer;
+
+	data_info->reset_cnt =
+		out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value;
+
+	ret = 0;
+free_acpi_buffer:
+	ACPI_FREE(out_obj);
+
+	return ret;
+}
+
+static int set_pfru_log_level(int level, struct pfru_log_device *pfru_log_dev)
+{
+	union acpi_object *out_obj, *obj, in_obj, in_buf;
+	enum pfru_dsm_status status;
+	acpi_handle handle;
+	int ret = -EINVAL;
+
+	handle = ACPI_HANDLE(pfru_log_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, &pfru_log_dev->uuid,
+					  pfru_log_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)
+		goto free_acpi_buffer;
+
+	obj = &out_obj->package.elements[1];
+	status = obj->integer.value;
+	if (status)
+		goto free_acpi_buffer;
+
+	ret = 0;
+
+free_acpi_buffer:
+	ACPI_FREE(out_obj);
+
+	return ret;
+}
+
+static int get_pfru_log_level(struct pfru_log_device *pfru_log_dev)
+{
+	union acpi_object *out_obj, *obj;
+	enum pfru_dsm_status status;
+	acpi_handle handle;
+	int ret = -EINVAL;
+
+	handle = ACPI_HANDLE(pfru_log_dev->dev);
+	out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid,
+					  pfru_log_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)
+		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)
+		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:
+	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_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct pfru_log_device *pfru_log_dev;
+	struct pfru_log_data_info data_info;
+	struct pfru_log_info info;
+	void __user *p;
+	int ret = 0;
+
+	pfru_log_dev = to_pfru_log_dev(file);
+	p = (void __user *)arg;
+
+	switch (cmd) {
+	case PFRU_LOG_IOC_SET_INFO:
+		if (copy_from_user(&info, p, sizeof(info)))
+			return -EFAULT;
+
+		if (pfru_valid_revid(info.log_revid))
+			pfru_log_dev->info.log_revid = info.log_revid;
+
+		if (valid_log_level(info.log_level)) {
+			ret = set_pfru_log_level(info.log_level, pfru_log_dev);
+			if (ret < 0)
+				return ret;
+
+			pfru_log_dev->info.log_level = info.log_level;
+		}
+
+		if (valid_log_type(info.log_type))
+			pfru_log_dev->info.log_type = info.log_type;
+
+		break;
+	case PFRU_LOG_IOC_GET_INFO:
+		info.log_level = get_pfru_log_level(pfru_log_dev);
+		if (ret < 0)
+			return ret;
+
+		info.log_type = pfru_log_dev->info.log_type;
+		info.log_revid = pfru_log_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_log_data_info(&data_info, pfru_log_dev);
+		if (ret)
+			return ret;
+
+		if (copy_to_user(p, &data_info, sizeof(struct pfru_log_data_info)))
+			ret = -EFAULT;
+
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+	return ret;
+}
+
+static int
+pfru_log_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct pfru_log_device *pfru_log_dev;
+	struct pfru_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;
+
+	pfru_log_dev = to_pfru_log_dev(file);
+
+	ret = get_pfru_log_data_info(&info, pfru_log_dev);
+	if (ret)
+		return ret;
+
+	base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32));
+	/* pfru update has not been launched yet.*/
+	if (!base_addr)
+		return -EBUSY;
+
+	psize = info.max_data_size;
+	/* Base address and total buffer size must be page aligned. */
+	if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize))
+		return -EINVAL;
+
+	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_pfru_log_fops = {
+	.owner		= THIS_MODULE,
+	.mmap		= pfru_log_mmap,
+	.unlocked_ioctl = pfru_log_ioctl,
+	.llseek		= noop_llseek,
+};
+
+static int acpi_pfru_log_remove(struct platform_device *pdev)
+{
+	struct pfru_log_device *pfru_log_dev = platform_get_drvdata(pdev);
+
+	misc_deregister(&pfru_log_dev->miscdev);
+
+	return 0;
+}
+
+static int acpi_pfru_log_probe(struct platform_device *pdev)
+{
+	struct pfru_log_device *pfru_log_dev;
+	acpi_handle handle;
+	static int pfru_log_idx;
+	int ret;
+
+	pfru_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfru_log_dev), GFP_KERNEL);
+	if (!pfru_log_dev)
+		return -ENOMEM;
+
+	ret = guid_parse(PFRU_LOG_UUID, &pfru_log_dev->uuid);
+	if (ret)
+		goto out;
+
+	/* default rev id is 1 */
+	pfru_log_dev->info.log_revid = 1;
+	pfru_log_dev->dev = &pdev->dev;
+	handle = ACPI_HANDLE(pfru_log_dev->dev);
+	if (!acpi_has_method(handle, "_DSM")) {
+		dev_dbg(&pdev->dev, "Missing _DSM\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	pfru_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+	pfru_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+						    "pfru_telemetry%d",
+						    pfru_log_idx);
+	if (!pfru_log_dev->miscdev.name) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pfru_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+							"acpi_pfru_telemetry%d",
+							pfru_log_idx);
+	if (!pfru_log_dev->miscdev.nodename) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	pfru_log_idx++;
+	pfru_log_dev->miscdev.fops = &acpi_pfru_log_fops;
+
+	ret = misc_register(&pfru_log_dev->miscdev);
+	if (ret)
+		goto out;
+
+	platform_set_drvdata(pdev, pfru_log_dev);
+
+	return 0;
+out:
+	devm_kfree(&pdev->dev, pfru_log_dev);
+
+	return ret;
+}
+
+static const struct acpi_device_id acpi_pfru_log_ids[] = {
+	{"INTC1081", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_pfru_log_ids);
+
+static struct platform_driver acpi_pfru_log_driver = {
+	.driver = {
+		.name = "pfru_telemetry",
+		.acpi_match_table = acpi_pfru_log_ids,
+	},
+	.probe = acpi_pfru_log_probe,
+	.remove = acpi_pfru_log_remove,
+};
+
+/************************** telemetry end   *************************/
+
 static int query_capability(struct pfru_update_cap_info *cap,
 			    struct pfru_device *pfru_dev)
 {
@@ -569,11 +983,23 @@ static struct platform_driver acpi_pfru_driver = {
 
 static int __init pfru_init(void)
 {
-	return platform_driver_register(&acpi_pfru_driver);
+	int ret = platform_driver_register(&acpi_pfru_driver);
+
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&acpi_pfru_log_driver);
+	if (ret) {
+		platform_driver_unregister(&acpi_pfru_driver);
+		return ret;
+	}
+
+	return 0;
 }
 
 static void __exit pfru_exit(void)
 {
+	platform_driver_unregister(&acpi_pfru_log_driver);
 	platform_driver_unregister(&acpi_pfru_driver);
 }
 
include/uapi/linux/pfru.h +94 -0

98 lines not on this page · raw message

Signature · 1 line
2.25.1
#6 lore raw

Re: [PATCH v5 3/4] drivers/acpi: Introduce Platform Firmware Runtime Update Telemetry

Quoted 10 lines · Chen Yu

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.

The corresponding userspace tool and man page will be introduced at tools/power/acpi/pfru.

Some of the comments from previous patch are applicable here. Hence, I'm not going to give a full review.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author
#7 lore raw

PATCH v5 4/4 tools: Introduce power/acpi/pfru/pfru

In reply to Chen Yu

Introduce a user space tool to make use of the interface exposed by Platform Firmware Runtime Update and Telemetry drivers. The users can use this tool to do firmware code injection, driver update and to retrieve the telemetry data.

---
v5: Replace the read() with mmap() so that the userspace
could mmap once, and read multiple times. (Greg Kroah-Hartman)

---
 include/uapi/linux/pfru.h      |   5 +
 tools/power/acpi/pfru/Makefile |  25 ++
 tools/power/acpi/pfru/pfru.8   | 137 +++++++++++
 tools/power/acpi/pfru/pfru.c   | 407 +++++++++++++++++++++++++++++++++
 4 files changed, 574 insertions(+)
 create mode 100644 tools/power/acpi/pfru/Makefile
 create mode 100644 tools/power/acpi/pfru/pfru.8
 create mode 100644 tools/power/acpi/pfru/pfru.c
include/uapi/linux/pfru.h +5 -0
index 01f0224f3d0a..7c31c3f7870a 100644
@@ -8,7 +8,12 @@
 #define __PFRU_H__
 
 #include <linux/ioctl.h>
+#ifdef __KERNEL__
 #include <linux/uuid.h>
+#else
+#include <uuid/uuid.h>
+#include <linux/types.h>
+#endif
 
 #define PFRU_UUID		"ECF9533B-4A3C-4E89-939E-C77112601C6D"
 #define PFRU_CODE_INJ_UUID		"B2F84B79-7B6E-4E45-885F-3FB9BB185402"
tools/power/acpi/pfru/Makefile new file +25 -0
new file mode 100644
index 000000000000..54bf913b2a09
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+CFLAGS += -Wall -O2
+CFLAGS += -DPFRU_HEADER='"../../../../include/uapi/linux/pfru.h"'
+BUILD_OUTPUT	:= $(CURDIR)
+
+ifeq ("$(origin O)", "command line")
+	BUILD_OUTPUT := $(O)
+endif
+
+pfru : pfru.c
+
+%: %.c
+	@mkdir -p $(BUILD_OUTPUT)
+	$(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@ $(LDFLAGS) -luuid
+
+.PHONY : clean
+clean :
+	@rm -f $(BUILD_OUTPUT)/pfru
+
+install : pfru
+	install -d  $(DESTDIR)$(PREFIX)/bin
+	install $(BUILD_OUTPUT)/pfru $(DESTDIR)$(PREFIX)/bin/pfru
+	install -d  $(DESTDIR)$(PREFIX)/share/man/man8
+	install -m 644 pfru.8 $(DESTDIR)$(PREFIX)/share/man/man8
tools/power/acpi/pfru/pfru.8 new file +137 -0

137 lines not on this page · raw message

tools/power/acpi/pfru/pfru.c new file +407 -0

407 lines not on this page · raw message

Signature · 1 line
2.25.1
#8 lore raw

Re: [PATCH v5 4/4] tools: Introduce power/acpi/pfru/pfru

Quoted 4 lines · Chen Yu

Introduce a user space tool to make use of the interface exposed by Platform Firmware Runtime Update and Telemetry drivers. The users can use this tool to do firmware code injection, driver update and to retrieve the telemetry data.

...

include/uapi/linux/pfru.h line 16

Quoted 7 lines

#include <linux/ioctl.h>

include/uapi/linux/pfru.h +5 -0
+#ifdef __KERNEL__
 #include <linux/uuid.h>
+#else
+#include <uuid/uuid.h>
+#include <linux/types.h>
+#endif

This is exactly my point why you mustn't use uuid.h in uAPI headers.

...

tools/power/acpi/pfru/pfru.c line 83

Quoted 1 line
tools/power/acpi/pfru/pfru.c new file +1 -0
+	while (1) {

Why not while ((c = getopt_long(...)) != -1) ?

Quoted 10 lines
tools/power/acpi/pfru/pfru.c new file +9 -0
+		int option_index = 0;
+
+		c = getopt_long(argc, argv, option_string,
+				long_options, &option_index);
+		if (c == -1)
+			break;
 
+		}
+	}
+}

...

tools/power/acpi/pfru/pfru.c line 140

Quoted 1 line
tools/power/acpi/pfru/pfru.c new file +1 -0
+	char *uuid = malloc(37);

libuuid doesn't have helpers for this?

Split assignment so if will be closer to its user.

Quoted 4 lines
tools/power/acpi/pfru/pfru.c new file +4 -0
+	if (!uuid) {
+		perror("Can not allocate uuid buffer\n");
+		exit(1);
+	}
Signature · 2 lines
With Best Regards,
Andy Shevchenko
author
#9 lore raw

Re: [PATCH v5 4/4] tools: Introduce power/acpi/pfru/pfru

Quoted 8 lines · Andy Shevchenko
Quoted 4 lines · Chen Yu

Introduce a user space tool to make use of the interface exposed by Platform Firmware Runtime Update and Telemetry drivers. The users can use this tool to do firmware code injection, driver update and to retrieve the telemetry data.

...

Quoted 10 lines

include/uapi/linux/pfru.h line 16

Quoted 7 lines

#include <linux/ioctl.h>

include/uapi/linux/pfru.h +5 -0
+#ifdef __KERNEL__
 #include <linux/uuid.h>
+#else
+#include <uuid/uuid.h>
+#include <linux/types.h>
+#endif

This is exactly my point why you mustn't use uuid.h in uAPI headers.

Ok, will remove uuid.h.

Quoted 4 lines

tools/power/acpi/pfru/pfru.c line 83

Quoted 1 line
tools/power/acpi/pfru/pfru.c new file +1 -0
+	while (1) {

Why not while ((c = getopt_long(...)) != -1) ?

Ok, changed it to this form.

Quoted 13 lines
Quoted 10 lines
tools/power/acpi/pfru/pfru.c new file +9 -0
+		int option_index = 0;
+
+		c = getopt_long(argc, argv, option_string,
+				long_options, &option_index);
+		if (c == -1)
+			break;
 
+		}
+	}
+}

...

Quoted 4 lines

tools/power/acpi/pfru/pfru.c line 140

Quoted 1 line
tools/power/acpi/pfru/pfru.c new file +1 -0
+	char *uuid = malloc(37);

libuuid doesn't have helpers for this?

There's no helper for uuid allocation AFAIK.

Quoted 2 lines

Split assignment so if will be closer to its user.

Ok, will do.

thanks,
Chenyu