1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2018 Bootlin
4 * Author: Miquel Raynal <miquel.raynal@bootlin.com>
5 */
6
7#include <common.h>
8#include <command.h>
9#include <dm.h>
10#include <log.h>
11#include <mapmem.h>
12#include <tpm-common.h>
13#include <tpm-v2.h>
14#include "tpm-user-utils.h"
15
16static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc,
17			   char *const argv[])
18{
19	enum tpm2_startup_types mode;
20	struct udevice *dev;
21	int ret;
22
23	ret = get_tpm(&dev);
24	if (ret)
25		return ret;
26	if (argc != 2)
27		return CMD_RET_USAGE;
28
29	if (!strcasecmp("TPM2_SU_CLEAR", argv[1])) {
30		mode = TPM2_SU_CLEAR;
31	} else if (!strcasecmp("TPM2_SU_STATE", argv[1])) {
32		mode = TPM2_SU_STATE;
33	} else {
34		printf("Couldn't recognize mode string: %s\n", argv[1]);
35		return CMD_RET_FAILURE;
36	}
37
38	return report_return_code(tpm2_startup(dev, mode));
39}
40
41static int do_tpm2_self_test(struct cmd_tbl *cmdtp, int flag, int argc,
42			     char *const argv[])
43{
44	enum tpm2_yes_no full_test;
45	struct udevice *dev;
46	int ret;
47
48	ret = get_tpm(&dev);
49	if (ret)
50		return ret;
51	if (argc != 2)
52		return CMD_RET_USAGE;
53
54	if (!strcasecmp("full", argv[1])) {
55		full_test = TPMI_YES;
56	} else if (!strcasecmp("continue", argv[1])) {
57		full_test = TPMI_NO;
58	} else {
59		printf("Couldn't recognize test mode: %s\n", argv[1]);
60		return CMD_RET_FAILURE;
61	}
62
63	return report_return_code(tpm2_self_test(dev, full_test));
64}
65
66static int do_tpm2_clear(struct cmd_tbl *cmdtp, int flag, int argc,
67			 char *const argv[])
68{
69	u32 handle = 0;
70	const char *pw = (argc < 3) ? NULL : argv[2];
71	const ssize_t pw_sz = pw ? strlen(pw) : 0;
72	struct udevice *dev;
73	int ret;
74
75	ret = get_tpm(&dev);
76	if (ret)
77		return ret;
78
79	if (argc < 2 || argc > 3)
80		return CMD_RET_USAGE;
81
82	if (pw_sz > TPM2_DIGEST_LEN)
83		return -EINVAL;
84
85	if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1]))
86		handle = TPM2_RH_LOCKOUT;
87	else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1]))
88		handle = TPM2_RH_PLATFORM;
89	else
90		return CMD_RET_USAGE;
91
92	return report_return_code(tpm2_clear(dev, handle, pw, pw_sz));
93}
94
95static int do_tpm2_pcr_extend(struct cmd_tbl *cmdtp, int flag, int argc,
96			      char *const argv[])
97{
98	struct udevice *dev;
99	struct tpm_chip_priv *priv;
100	u32 index = simple_strtoul(argv[1], NULL, 0);
101	void *digest = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0);
102	int algo = TPM2_ALG_SHA256;
103	int algo_len;
104	int ret;
105	u32 rc;
106
107	if (argc < 3 || argc > 4)
108		return CMD_RET_USAGE;
109	if (argc == 4) {
110		algo = tpm2_name_to_algorithm(argv[3]);
111		if (algo < 0)
112			return CMD_RET_FAILURE;
113	}
114	algo_len = tpm2_algorithm_to_len(algo);
115
116	ret = get_tpm(&dev);
117	if (ret)
118		return ret;
119
120	priv = dev_get_uclass_priv(dev);
121	if (!priv)
122		return -EINVAL;
123
124	if (index >= priv->pcr_count)
125		return -EINVAL;
126
127	rc = tpm2_pcr_extend(dev, index, algo, digest, algo_len);
128	if (!rc) {
129		printf("PCR #%u extended with %d byte %s digest\n", index,
130		       algo_len, tpm2_algorithm_name(algo));
131		print_byte_string(digest, algo_len);
132	}
133
134	unmap_sysmem(digest);
135
136	return report_return_code(rc);
137}
138
139static int do_tpm_pcr_read(struct cmd_tbl *cmdtp, int flag, int argc,
140			   char *const argv[])
141{
142	enum tpm2_algorithms algo = TPM2_ALG_SHA256;
143	struct udevice *dev;
144	struct tpm_chip_priv *priv;
145	u32 index, rc;
146	int algo_len;
147	unsigned int updates;
148	void *data;
149	int ret;
150
151	if (argc < 3 || argc > 4)
152		return CMD_RET_USAGE;
153	if (argc == 4) {
154		algo = tpm2_name_to_algorithm(argv[3]);
155		if (algo < 0)
156			return CMD_RET_FAILURE;
157	}
158	algo_len = tpm2_algorithm_to_len(algo);
159
160	ret = get_tpm(&dev);
161	if (ret)
162		return ret;
163
164	priv = dev_get_uclass_priv(dev);
165	if (!priv)
166		return -EINVAL;
167
168	index = simple_strtoul(argv[1], NULL, 0);
169	if (index >= priv->pcr_count)
170		return -EINVAL;
171
172	data = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0);
173
174	rc = tpm2_pcr_read(dev, index, priv->pcr_select_min, algo,
175			   data, algo_len, &updates);
176	if (!rc) {
177		printf("PCR #%u %s %d byte content (%u known updates):\n", index,
178		       tpm2_algorithm_name(algo), algo_len, updates);
179		print_byte_string(data, algo_len);
180	}
181
182	unmap_sysmem(data);
183
184	return report_return_code(rc);
185}
186
187static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc,
188				 char *const argv[])
189{
190	u32 capability, property, rc;
191	u8 *data;
192	size_t count;
193	int i, j;
194	struct udevice *dev;
195	int ret;
196
197	ret = get_tpm(&dev);
198	if (ret)
199		return ret;
200
201	if (argc != 5)
202		return CMD_RET_USAGE;
203
204	capability = simple_strtoul(argv[1], NULL, 0);
205	property = simple_strtoul(argv[2], NULL, 0);
206	data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0);
207	count = simple_strtoul(argv[4], NULL, 0);
208
209	rc = tpm2_get_capability(dev, capability, property, data, count);
210	if (rc)
211		goto unmap_data;
212
213	printf("Capabilities read from TPM:\n");
214	for (i = 0; i < count; i++) {
215		printf("Property 0x");
216		for (j = 0; j < 4; j++)
217			printf("%02x", data[(i * 8) + j + sizeof(u32)]);
218		printf(": 0x");
219		for (j = 4; j < 8; j++)
220			printf("%02x", data[(i * 8) + j + sizeof(u32)]);
221		printf("\n");
222	}
223
224unmap_data:
225	unmap_sysmem(data);
226
227	return report_return_code(rc);
228}
229
230static int do_tpm_dam_reset(struct cmd_tbl *cmdtp, int flag, int argc,
231			    char *const argv[])
232{
233	const char *pw = (argc < 2) ? NULL : argv[1];
234	const ssize_t pw_sz = pw ? strlen(pw) : 0;
235	struct udevice *dev;
236	int ret;
237
238	ret = get_tpm(&dev);
239	if (ret)
240		return ret;
241
242	if (argc > 2)
243		return CMD_RET_USAGE;
244
245	if (pw_sz > TPM2_DIGEST_LEN)
246		return -EINVAL;
247
248	return report_return_code(tpm2_dam_reset(dev, pw, pw_sz));
249}
250
251static int do_tpm_dam_parameters(struct cmd_tbl *cmdtp, int flag, int argc,
252				 char *const argv[])
253{
254	const char *pw = (argc < 5) ? NULL : argv[4];
255	const ssize_t pw_sz = pw ? strlen(pw) : 0;
256	/*
257	 * No Dictionary Attack Mitigation (DAM) means:
258	 * maxtries = 0xFFFFFFFF, recovery_time = 1, lockout_recovery = 0
259	 */
260	unsigned long int max_tries;
261	unsigned long int recovery_time;
262	unsigned long int lockout_recovery;
263	struct udevice *dev;
264	int ret;
265
266	ret = get_tpm(&dev);
267	if (ret)
268		return ret;
269
270	if (argc < 4 || argc > 5)
271		return CMD_RET_USAGE;
272
273	if (pw_sz > TPM2_DIGEST_LEN)
274		return -EINVAL;
275
276	if (strict_strtoul(argv[1], 0, &max_tries))
277		return CMD_RET_USAGE;
278
279	if (strict_strtoul(argv[2], 0, &recovery_time))
280		return CMD_RET_USAGE;
281
282	if (strict_strtoul(argv[3], 0, &lockout_recovery))
283		return CMD_RET_USAGE;
284
285	log(LOGC_NONE, LOGL_INFO, "Changing dictionary attack parameters:\n");
286	log(LOGC_NONE, LOGL_INFO, "- maxTries: %lu", max_tries);
287	log(LOGC_NONE, LOGL_INFO, "- recoveryTime: %lu\n", recovery_time);
288	log(LOGC_NONE, LOGL_INFO, "- lockoutRecovery: %lu\n", lockout_recovery);
289
290	return report_return_code(tpm2_dam_parameters(dev, pw, pw_sz, max_tries,
291						      recovery_time,
292						      lockout_recovery));
293}
294
295static int do_tpm_change_auth(struct cmd_tbl *cmdtp, int flag, int argc,
296			      char *const argv[])
297{
298	u32 handle;
299	const char *newpw = argv[2];
300	const char *oldpw = (argc == 3) ? NULL : argv[3];
301	const ssize_t newpw_sz = strlen(newpw);
302	const ssize_t oldpw_sz = oldpw ? strlen(oldpw) : 0;
303	struct udevice *dev;
304	int ret;
305
306	ret = get_tpm(&dev);
307	if (ret)
308		return ret;
309
310	if (argc < 3 || argc > 4)
311		return CMD_RET_USAGE;
312
313	if (newpw_sz > TPM2_DIGEST_LEN || oldpw_sz > TPM2_DIGEST_LEN)
314		return -EINVAL;
315
316	if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1]))
317		handle = TPM2_RH_LOCKOUT;
318	else if (!strcasecmp("TPM2_RH_ENDORSEMENT", argv[1]))
319		handle = TPM2_RH_ENDORSEMENT;
320	else if (!strcasecmp("TPM2_RH_OWNER", argv[1]))
321		handle = TPM2_RH_OWNER;
322	else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1]))
323		handle = TPM2_RH_PLATFORM;
324	else
325		return CMD_RET_USAGE;
326
327	return report_return_code(tpm2_change_auth(dev, handle, newpw, newpw_sz,
328						   oldpw, oldpw_sz));
329}
330
331static int do_tpm_pcr_setauthpolicy(struct cmd_tbl *cmdtp, int flag, int argc,
332				    char *const argv[])
333{
334	u32 index = simple_strtoul(argv[1], NULL, 0);
335	char *key = argv[2];
336	const char *pw = (argc < 4) ? NULL : argv[3];
337	const ssize_t pw_sz = pw ? strlen(pw) : 0;
338	struct udevice *dev;
339	int ret;
340
341	ret = get_tpm(&dev);
342	if (ret)
343		return ret;
344
345	if (strlen(key) != TPM2_DIGEST_LEN)
346		return -EINVAL;
347
348	if (argc < 3 || argc > 4)
349		return CMD_RET_USAGE;
350
351	return report_return_code(tpm2_pcr_setauthpolicy(dev, pw, pw_sz, index,
352							 key));
353}
354
355static int do_tpm_pcr_setauthvalue(struct cmd_tbl *cmdtp, int flag,
356				   int argc, char *const argv[])
357{
358	u32 index = simple_strtoul(argv[1], NULL, 0);
359	char *key = argv[2];
360	const ssize_t key_sz = strlen(key);
361	const char *pw = (argc < 4) ? NULL : argv[3];
362	const ssize_t pw_sz = pw ? strlen(pw) : 0;
363	struct udevice *dev;
364	int ret;
365
366	ret = get_tpm(&dev);
367	if (ret)
368		return ret;
369
370	if (strlen(key) != TPM2_DIGEST_LEN)
371		return -EINVAL;
372
373	if (argc < 3 || argc > 4)
374		return CMD_RET_USAGE;
375
376	return report_return_code(tpm2_pcr_setauthvalue(dev, pw, pw_sz, index,
377							key, key_sz));
378}
379
380static struct cmd_tbl tpm2_commands[] = {
381	U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""),
382	U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""),
383	U_BOOT_CMD_MKENT(state, 0, 1, do_tpm_report_state, "", ""),
384	U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""),
385	U_BOOT_CMD_MKENT(startup, 0, 1, do_tpm2_startup, "", ""),
386	U_BOOT_CMD_MKENT(self_test, 0, 1, do_tpm2_self_test, "", ""),
387	U_BOOT_CMD_MKENT(clear, 0, 1, do_tpm2_clear, "", ""),
388	U_BOOT_CMD_MKENT(pcr_extend, 0, 1, do_tpm2_pcr_extend, "", ""),
389	U_BOOT_CMD_MKENT(pcr_read, 0, 1, do_tpm_pcr_read, "", ""),
390	U_BOOT_CMD_MKENT(get_capability, 0, 1, do_tpm_get_capability, "", ""),
391	U_BOOT_CMD_MKENT(dam_reset, 0, 1, do_tpm_dam_reset, "", ""),
392	U_BOOT_CMD_MKENT(dam_parameters, 0, 1, do_tpm_dam_parameters, "", ""),
393	U_BOOT_CMD_MKENT(change_auth, 0, 1, do_tpm_change_auth, "", ""),
394	U_BOOT_CMD_MKENT(autostart, 0, 1, do_tpm_autostart, "", ""),
395	U_BOOT_CMD_MKENT(pcr_setauthpolicy, 0, 1,
396			 do_tpm_pcr_setauthpolicy, "", ""),
397	U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1,
398			 do_tpm_pcr_setauthvalue, "", ""),
399};
400
401struct cmd_tbl *get_tpm2_commands(unsigned int *size)
402{
403	*size = ARRAY_SIZE(tpm2_commands);
404
405	return tpm2_commands;
406}
407
408U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command",
409"<command> [<arguments>]\n"
410"\n"
411"device [num device]\n"
412"    Show all devices or set the specified device\n"
413"info\n"
414"    Show information about the TPM.\n"
415"state\n"
416"    Show internal state from the TPM (if available)\n"
417"autostart\n"
418"    Initalize the tpm, perform a Startup(clear) and run a full selftest\n"
419"    sequence\n"
420"init\n"
421"    Initialize the software stack. Always the first command to issue.\n"
422"    'tpm startup' is the only acceptable command after a 'tpm init' has been\n"
423"    issued\n"
424"startup <mode>\n"
425"    Issue a TPM2_Startup command.\n"
426"    <mode> is one of:\n"
427"        * TPM2_SU_CLEAR (reset state)\n"
428"        * TPM2_SU_STATE (preserved state)\n"
429"self_test <type>\n"
430"    Test the TPM capabilities.\n"
431"    <type> is one of:\n"
432"        * full (perform all tests)\n"
433"        * continue (only check untested tests)\n"
434"clear <hierarchy>\n"
435"    Issue a TPM2_Clear command.\n"
436"    <hierarchy> is one of:\n"
437"        * TPM2_RH_LOCKOUT\n"
438"        * TPM2_RH_PLATFORM\n"
439"pcr_extend <pcr> <digest_addr> [<digest_algo>]\n"
440"    Extend PCR #<pcr> with digest at <digest_addr> with digest_algo.\n"
441"    <pcr>: index of the PCR\n"
442"    <digest_addr>: address of digest of digest_algo type (defaults to SHA256)\n"
443"pcr_read <pcr> <digest_addr> [<digest_algo>]\n"
444"    Read PCR #<pcr> to memory address <digest_addr> with <digest_algo>.\n"
445"    <pcr>: index of the PCR\n"
446"    <digest_addr>: address of digest of digest_algo type (defaults to SHA256)\n"
447"get_capability <capability> <property> <addr> <count>\n"
448"    Read and display <count> entries indexed by <capability>/<property>.\n"
449"    Values are 4 bytes long and are written at <addr>.\n"
450"    <capability>: capability\n"
451"    <property>: property\n"
452"    <addr>: address to store <count> entries of 4 bytes\n"
453"    <count>: number of entries to retrieve\n"
454"dam_reset [<password>]\n"
455"    If the TPM is not in a LOCKOUT state, reset the internal error counter.\n"
456"    <password>: optional password\n"
457"dam_parameters <max_tries> <recovery_time> <lockout_recovery> [<password>]\n"
458"    If the TPM is not in a LOCKOUT state, set the DAM parameters\n"
459"    <maxTries>: maximum number of failures before lockout,\n"
460"                0 means always locking\n"
461"    <recoveryTime>: time before decrement of the error counter,\n"
462"                    0 means no lockout\n"
463"    <lockoutRecovery>: time of a lockout (before the next try),\n"
464"                       0 means a reboot is needed\n"
465"    <password>: optional password of the LOCKOUT hierarchy\n"
466"change_auth <hierarchy> <new_pw> [<old_pw>]\n"
467"    <hierarchy>: the hierarchy\n"
468"    <new_pw>: new password for <hierarchy>\n"
469"    <old_pw>: optional previous password of <hierarchy>\n"
470"pcr_setauthpolicy|pcr_setauthvalue <pcr> <key> [<password>]\n"
471"    Change the <key> to access PCR #<pcr>.\n"
472"    hierarchy and may be empty.\n"
473"    /!\\WARNING: untested function, use at your own risks !\n"
474"    <pcr>: index of the PCR\n"
475"    <key>: secret to protect the access of PCR #<pcr>\n"
476"    <password>: optional password of the PLATFORM hierarchy\n"
477);
478