From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.4 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58434C4332F for ; Wed, 8 Sep 2021 09:08:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3EF7061163 for ; Wed, 8 Sep 2021 09:08:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1348697AbhIHJJ5 (ORCPT ); Wed, 8 Sep 2021 05:09:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:57288 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348674AbhIHJJ4 (ORCPT ); Wed, 8 Sep 2021 05:09:56 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 54CE361051; Wed, 8 Sep 2021 09:08:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1631092129; bh=1tQl/M3SZBqXboswaI30QN31S2XQar2x5BTazNGhO0I=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=lrECNc+GkD6qPrcUwpdJoIJjsvgLTAv7vfrxcGUGKLNoJIT9f/1VR3VDjSspCt/Y6 P7U2XSWNSs0S45iPSJlqnRCSs69imFVEg6g1ZBM1qre/HH6zcFko1Ny/wyU8lvSTQW d2rX2vbKRzVNZscBNGdh51m1QI2mgl0NtYyM1fw3GDxOvdwtTGgyT6NRMcJrJPLKVu ACJkrVZvpoYvNgEXjBbYqunK9j4PAbHAq2caZKOjOf2RS4dm23SFgR3G+VPU6gDGrx OI3lD0D4MfsB24+UdAv5UKlrU+Nu/g1WSyKEBzADjKU/kb5rvnZYlq6lKapOU4+SoF QxxGSQrBdQwnw== Date: Wed, 8 Sep 2021 12:08:40 +0300 From: Mike Rapoport To: Chen Yu Cc: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org, "Rafael J. Wysocki" , Len Brown , Dan Williams , Andy Shevchenko , Aubrey Li , Ashok Raj , Shuah Khan , linux-kselftest@vger.kernel.org, Dou Shengnan Subject: Re: [PATCH 5/5][RFC] selftests/pfru: add test for Platform Firmware Runtime Update and Telemetry Message-ID: References: <1cef405de3484eef108251562fbf461bad4294c7.1631025237.git.yu.c.chen@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1cef405de3484eef108251562fbf461bad4294c7.1631025237.git.yu.c.chen@intel.com> Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org 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 > Signed-off-by: Chen Yu > --- > 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 > +#include > + > +#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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#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.