@@ -56,6 +56,7 @@ TARGETS += seccomp
TARGETS += sgx
TARGETS += sigaltstack
TARGETS += size
+TARGETS += pfru
TARGETS += sparc64
TARGETS += splice
TARGETS += static_keys
new file mode 100644
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+CFLAGS += -Wall -O2
+LDLIBS := -luuid
+
+TEST_GEN_PROGS := pfru_test
+include ../lib.mk
new file mode 100644
@@ -0,0 +1,2 @@
+CONFIG_ACPI_PFRU=m
+CONFIG_ACPI_PFRU_TELEMETRY=m
new file mode 100644
@@ -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__ */
new file mode 100644
@@ -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;
+}