1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Platform Firmware Runtime Update tool to do Management
4 * Mode code injection/driver update and telemetry retrieval.
5 *
6 * This tool uses the interfaces provided by pfr_update and
7 * pfr_telemetry drivers. These interfaces are exposed via
8 * /dev/pfr_update and /dev/pfr_telemetry. Write operation
9 * on the /dev/pfr_update is to load the EFI capsule into
10 * kernel space. Mmap/read operations on /dev/pfr_telemetry
11 * could be used to read the telemetry data to user space.
12 */
13#define _GNU_SOURCE
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <fcntl.h>
20#include <unistd.h>
21#include <getopt.h>
22#include <sys/ioctl.h>
23#include <sys/mman.h>
24#include <uuid/uuid.h>
25#include PFRUT_HEADER
26
27char *capsule_name;
28int action, query_cap, log_type, log_level, log_read, log_getinfo,
29	revid, log_revid;
30int set_log_level, set_log_type,
31	set_revid, set_log_revid;
32
33char *progname;
34
35#define LOG_ERR		0
36#define LOG_WARN	1
37#define LOG_INFO	2
38#define LOG_VERB	4
39#define LOG_EXEC_IDX	0
40#define LOG_HISTORY_IDX	1
41#define REVID_1		1
42#define REVID_2		2
43
44static int valid_log_level(int level)
45{
46	return level == LOG_ERR || level == LOG_WARN ||
47	       level == LOG_INFO || level == LOG_VERB;
48}
49
50static int valid_log_type(int type)
51{
52	return type == LOG_EXEC_IDX || type == LOG_HISTORY_IDX;
53}
54
55static inline int valid_log_revid(int id)
56{
57	return id == REVID_1 || id == REVID_2;
58}
59
60static void help(void)
61{
62	fprintf(stderr,
63		"usage: %s [OPTIONS]\n"
64		" code injection:\n"
65		"  -l, --load\n"
66		"  -s, --stage\n"
67		"  -a, --activate\n"
68		"  -u, --update [stage and activate]\n"
69		"  -q, --query\n"
70		"  -d, --revid update\n"
71		" telemetry:\n"
72		"  -G, --getloginfo\n"
73		"  -T, --type(0:execution, 1:history)\n"
74		"  -L, --level(0, 1, 2, 4)\n"
75		"  -R, --read\n"
76		"  -D, --revid log\n",
77		progname);
78}
79
80char *option_string = "l:sauqd:GT:L:RD:h";
81static struct option long_options[] = {
82	{"load", required_argument, 0, 'l'},
83	{"stage", no_argument, 0, 's'},
84	{"activate", no_argument, 0, 'a'},
85	{"update", no_argument, 0, 'u'},
86	{"query", no_argument, 0, 'q'},
87	{"getloginfo", no_argument, 0, 'G'},
88	{"type", required_argument, 0, 'T'},
89	{"level", required_argument, 0, 'L'},
90	{"read", no_argument, 0, 'R'},
91	{"setrev", required_argument, 0, 'd'},
92	{"setrevlog", required_argument, 0, 'D'},
93	{"help", no_argument, 0, 'h'},
94	{}
95};
96
97static void parse_options(int argc, char **argv)
98{
99	int option_index = 0;
100	char *pathname, *endptr;
101	int opt;
102
103	pathname = strdup(argv[0]);
104	progname = basename(pathname);
105
106	while ((opt = getopt_long_only(argc, argv, option_string,
107				       long_options, &option_index)) != -1) {
108		switch (opt) {
109		case 'l':
110			capsule_name = optarg;
111			break;
112		case 's':
113			action = 1;
114			break;
115		case 'a':
116			action = 2;
117			break;
118		case 'u':
119			action = 3;
120			break;
121		case 'q':
122			query_cap = 1;
123			break;
124		case 'G':
125			log_getinfo = 1;
126			break;
127		case 'T':
128			log_type = strtol(optarg, &endptr, 0);
129			if (*endptr || (log_type != 0 && log_type != 1)) {
130				printf("Number expected: type(0:execution, 1:history) - Quit.\n");
131				exit(1);
132			}
133
134			set_log_type = 1;
135			break;
136		case 'L':
137			log_level = strtol(optarg, &endptr, 0);
138			if (*endptr ||
139			    (log_level != 0 && log_level != 1 &&
140			     log_level != 2 && log_level != 4)) {
141				printf("Number expected: level(0, 1, 2, 4) - Quit.\n");
142				exit(1);
143			}
144
145			set_log_level = 1;
146			break;
147		case 'R':
148			log_read = 1;
149			break;
150		case 'd':
151			revid = atoi(optarg);
152			set_revid = 1;
153			break;
154		case 'D':
155			log_revid = atoi(optarg);
156			set_log_revid = 1;
157			break;
158		case 'h':
159			help();
160			exit(0);
161		default:
162			break;
163		}
164	}
165}
166
167void print_cap(struct pfru_update_cap_info *cap)
168{
169	char *uuid;
170
171	uuid = malloc(37);
172	if (!uuid) {
173		perror("Can not allocate uuid buffer\n");
174		exit(1);
175	}
176
177	uuid_unparse(cap->code_type, uuid);
178	printf("code injection image type:%s\n", uuid);
179	printf("fw_version:%d\n", cap->fw_version);
180	printf("code_rt_version:%d\n", cap->code_rt_version);
181
182	uuid_unparse(cap->drv_type, uuid);
183	printf("driver update image type:%s\n", uuid);
184	printf("drv_rt_version:%d\n", cap->drv_rt_version);
185	printf("drv_svn:%d\n", cap->drv_svn);
186
187	uuid_unparse(cap->platform_id, uuid);
188	printf("platform id:%s\n", uuid);
189	uuid_unparse(cap->oem_id, uuid);
190	printf("oem id:%s\n", uuid);
191	printf("oem information length:%d\n", cap->oem_info_len);
192
193	free(uuid);
194}
195
196int main(int argc, char *argv[])
197{
198	int fd_update, fd_update_log, fd_capsule;
199	struct pfrt_log_data_info data_info;
200	struct pfrt_log_info info;
201	struct pfru_update_cap_info cap;
202	void *addr_map_capsule;
203	struct stat st;
204	char *log_buf;
205	int ret;
206
207	if (getuid() != 0) {
208		printf("Please run the tool as root - Exiting.\n");
209		return 1;
210	}
211
212	parse_options(argc, argv);
213
214	fd_update = open("/dev/acpi_pfr_update0", O_RDWR);
215	if (fd_update < 0) {
216		printf("PFRU device not supported - Quit...\n");
217		return 1;
218	}
219
220	fd_update_log = open("/dev/acpi_pfr_telemetry0", O_RDWR);
221	if (fd_update_log < 0) {
222		printf("PFRT device not supported - Quit...\n");
223		return 1;
224	}
225
226	if (query_cap) {
227		ret = ioctl(fd_update, PFRU_IOC_QUERY_CAP, &cap);
228		if (ret)
229			perror("Query Update Capability info failed.");
230		else
231			print_cap(&cap);
232
233		close(fd_update);
234		close(fd_update_log);
235
236		return ret;
237	}
238
239	if (log_getinfo) {
240		ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_DATA_INFO, &data_info);
241		if (ret) {
242			perror("Get telemetry data info failed.");
243			close(fd_update);
244			close(fd_update_log);
245
246			return 1;
247		}
248
249		ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_INFO, &info);
250		if (ret) {
251			perror("Get telemetry info failed.");
252			close(fd_update);
253			close(fd_update_log);
254
255			return 1;
256		}
257
258		printf("log_level:%d\n", info.log_level);
259		printf("log_type:%d\n", info.log_type);
260		printf("log_revid:%d\n", info.log_revid);
261		printf("max_data_size:%d\n", data_info.max_data_size);
262		printf("chunk1_size:%d\n", data_info.chunk1_size);
263		printf("chunk2_size:%d\n", data_info.chunk2_size);
264		printf("rollover_cnt:%d\n", data_info.rollover_cnt);
265		printf("reset_cnt:%d\n", data_info.reset_cnt);
266
267		return 0;
268	}
269
270	info.log_level = -1;
271	info.log_type = -1;
272	info.log_revid = -1;
273
274	if (set_log_level) {
275		if (!valid_log_level(log_level)) {
276			printf("Invalid log level %d\n",
277			       log_level);
278		} else {
279			info.log_level = log_level;
280		}
281	}
282
283	if (set_log_type) {
284		if (!valid_log_type(log_type)) {
285			printf("Invalid log type %d\n",
286			       log_type);
287		} else {
288			info.log_type = log_type;
289		}
290	}
291
292	if (set_log_revid) {
293		if (!valid_log_revid(log_revid)) {
294			printf("Invalid log revid %d, unchanged.\n",
295			       log_revid);
296		} else {
297			info.log_revid = log_revid;
298		}
299	}
300
301	ret = ioctl(fd_update_log, PFRT_LOG_IOC_SET_INFO, &info);
302	if (ret) {
303		perror("Log information set failed.(log_level, log_type, log_revid)");
304		close(fd_update);
305		close(fd_update_log);
306
307		return 1;
308	}
309
310	if (set_revid) {
311		ret = ioctl(fd_update, PFRU_IOC_SET_REV, &revid);
312		if (ret) {
313			perror("pfru update revid set failed");
314			close(fd_update);
315			close(fd_update_log);
316
317			return 1;
318		}
319
320		printf("pfru update revid set to %d\n", revid);
321	}
322
323	if (capsule_name) {
324		fd_capsule = open(capsule_name, O_RDONLY);
325		if (fd_capsule < 0) {
326			perror("Can not open capsule file...");
327			close(fd_update);
328			close(fd_update_log);
329
330			return 1;
331		}
332
333		if (fstat(fd_capsule, &st) < 0) {
334			perror("Can not fstat capsule file...");
335			close(fd_capsule);
336			close(fd_update);
337			close(fd_update_log);
338
339			return 1;
340		}
341
342		addr_map_capsule = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
343					fd_capsule, 0);
344		if (addr_map_capsule == MAP_FAILED) {
345			perror("Failed to mmap capsule file.");
346			close(fd_capsule);
347			close(fd_update);
348			close(fd_update_log);
349
350			return 1;
351		}
352
353		ret = write(fd_update, (char *)addr_map_capsule, st.st_size);
354		printf("Load %d bytes of capsule file into the system\n",
355		       ret);
356
357		if (ret == -1) {
358			perror("Failed to load capsule file");
359			close(fd_capsule);
360			close(fd_update);
361			close(fd_update_log);
362
363			return 1;
364		}
365
366		munmap(addr_map_capsule, st.st_size);
367		close(fd_capsule);
368		printf("Load done.\n");
369	}
370
371	if (action) {
372		if (action == 1) {
373			ret = ioctl(fd_update, PFRU_IOC_STAGE, NULL);
374		} else if (action == 2) {
375			ret = ioctl(fd_update, PFRU_IOC_ACTIVATE, NULL);
376		} else if (action == 3) {
377			ret = ioctl(fd_update, PFRU_IOC_STAGE_ACTIVATE, NULL);
378		} else {
379			close(fd_update);
380			close(fd_update_log);
381
382			return 1;
383		}
384		printf("Update finished, return %d\n", ret);
385	}
386
387	close(fd_update);
388
389	if (log_read) {
390		void *p_mmap;
391		int max_data_sz;
392
393		ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_DATA_INFO, &data_info);
394		if (ret) {
395			perror("Get telemetry data info failed.");
396			close(fd_update_log);
397
398			return 1;
399		}
400
401		max_data_sz = data_info.max_data_size;
402		if (!max_data_sz) {
403			printf("No telemetry data available.\n");
404			close(fd_update_log);
405
406			return 1;
407		}
408
409		log_buf = malloc(max_data_sz + 1);
410		if (!log_buf) {
411			perror("log_buf allocate failed.");
412			close(fd_update_log);
413
414			return 1;
415		}
416
417		p_mmap = mmap(NULL, max_data_sz, PROT_READ, MAP_SHARED, fd_update_log, 0);
418		if (p_mmap == MAP_FAILED) {
419			perror("mmap error.");
420			close(fd_update_log);
421
422			return 1;
423		}
424
425		memcpy(log_buf, p_mmap, max_data_sz);
426		log_buf[max_data_sz] = '\0';
427		printf("%s\n", log_buf);
428		free(log_buf);
429
430		munmap(p_mmap, max_data_sz);
431	}
432
433	close(fd_update_log);
434
435	return 0;
436}
437