summaryrefslogtreecommitdiff
path: root/cmd/tpm-common.c
blob: 88c9e08e318d129316dd2d206d4049fd0060b1e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2013 The Chromium OS Authors.
 */

#include <common.h>
#include <command.h>
#include <dm.h>
#include <env.h>
#include <malloc.h>
#include <asm/unaligned.h>
#include <linux/string.h>
#include <tpm-common.h>
#include "tpm-user-utils.h"

static struct udevice *tpm_dev;

/**
 * Print a byte string in hexdecimal format, 16-bytes per line.
 *
 * @param data		byte string to be printed
 * @param count		number of bytes to be printed
 */
void print_byte_string(u8 *data, size_t count)
{
	int i, print_newline = 0;

	for (i = 0; i < count; i++) {
		printf(" %02x", data[i]);
		print_newline = (i % 16 == 15);
		if (print_newline)
			putc('\n');
	}
	/* Avoid duplicated newline at the end */
	if (!print_newline)
		putc('\n');
}

/**
 * Convert a text string of hexdecimal values into a byte string.
 *
 * @param bytes		text string of hexdecimal values with no space
 *			between them
 * @param data		output buffer for byte string.  The caller has to make
 *			sure it is large enough for storing the output.  If
 *			NULL is passed, a large enough buffer will be allocated,
 *			and the caller must free it.
 * @param count_ptr	output variable for the length of byte string
 * @return pointer to output buffer
 */
void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr)
{
	char byte[3];
	size_t count, length;
	int i;

	if (!bytes)
		return NULL;
	length = strlen(bytes);
	count = length / 2;

	if (!data)
		data = malloc(count);
	if (!data)
		return NULL;

	byte[2] = '\0';
	for (i = 0; i < length; i += 2) {
		byte[0] = bytes[i];
		byte[1] = bytes[i + 1];
		data[i / 2] = (u8)simple_strtoul(byte, NULL, 16);
	}

	if (count_ptr)
		*count_ptr = count;

	return data;
}

/**
 * report_return_code() - Report any error and return failure or success
 *
 * @param return_code	TPM command return code
 * @return value of enum command_ret_t
 */
int report_return_code(int return_code)
{
	if (return_code) {
		printf("Error: %d\n", return_code);
		return CMD_RET_FAILURE;
	} else {
		return CMD_RET_SUCCESS;
	}
}

/**
 * Return number of values defined by a type string.
 *
 * @param type_str	type string
 * @return number of values of type string
 */
int type_string_get_num_values(const char *type_str)
{
	return strlen(type_str);
}

/**
 * Return total size of values defined by a type string.
 *
 * @param type_str	type string
 * @return total size of values of type string, or 0 if type string
 *  contains illegal type character.
 */
size_t type_string_get_space_size(const char *type_str)
{
	size_t size;

	for (size = 0; *type_str; type_str++) {
		switch (*type_str) {
		case 'b':
			size += 1;
			break;
		case 'w':
			size += 2;
			break;
		case 'd':
			size += 4;
			break;
		default:
			return 0;
		}
	}

	return size;
}

/**
 * Allocate a buffer large enough to hold values defined by a type
 * string.  The caller has to free the buffer.
 *
 * @param type_str	type string
 * @param count		pointer for storing size of buffer
 * @return pointer to buffer or NULL on error
 */
void *type_string_alloc(const char *type_str, u32 *count)
{
	void *data;
	size_t size;

	size = type_string_get_space_size(type_str);
	if (!size)
		return NULL;
	data = malloc(size);
	if (data)
		*count = size;

	return data;
}

/**
 * Pack values defined by a type string into a buffer.  The buffer must have
 * large enough space.
 *
 * @param type_str	type string
 * @param values	text strings of values to be packed
 * @param data		output buffer of values
 * @return 0 on success, non-0 on error
 */
int type_string_pack(const char *type_str, char * const values[],
		     u8 *data)
{
	size_t offset;
	u32 value;

	for (offset = 0; *type_str; type_str++, values++) {
		value = simple_strtoul(values[0], NULL, 0);
		switch (*type_str) {
		case 'b':
			data[offset] = value;
			offset += 1;
			break;
		case 'w':
			put_unaligned_be16(value, data + offset);
			offset += 2;
			break;
		case 'd':
			put_unaligned_be32(value, data + offset);
			offset += 4;
			break;
		default:
			return -1;
		}
	}

	return 0;
}

/**
 * Read values defined by a type string from a buffer, and write these values
 * to environment variables.
 *
 * @param type_str	type string
 * @param data		input buffer of values
 * @param vars		names of environment variables
 * @return 0 on success, non-0 on error
 */
int type_string_write_vars(const char *type_str, u8 *data,
			   char * const vars[])
{
	size_t offset;
	u32 value;

	for (offset = 0; *type_str; type_str++, vars++) {
		switch (*type_str) {
		case 'b':
			value = data[offset];
			offset += 1;
			break;
		case 'w':
			value = get_unaligned_be16(data + offset);
			offset += 2;
			break;
		case 'd':
			value = get_unaligned_be32(data + offset);
			offset += 4;
			break;
		default:
			return -1;
		}
		if (env_set_ulong(*vars, value))
			return -1;
	}

	return 0;
}

static int tpm_show_device(void)
{
	struct udevice *dev;
	char buf[80];
	int n = 0, rc;

	for_each_tpm_device(dev) {
		rc = tpm_get_desc(dev, buf, sizeof(buf));
		if (rc < 0)
			printf("device %d: can't get info\n", n);
		else
			printf("device %d: %s\n", n, buf);

		n++;
	};

	return 0;
}

static int tpm_set_device(unsigned long num)
{
	struct udevice *dev;
	unsigned long n = 0;
	int rc = CMD_RET_FAILURE;

	for_each_tpm_device(dev) {
		if (n == num) {
			rc = 0;
			break;
		}

		n++;
	}

	if (!rc)
		tpm_dev = dev;

	return rc;
}

int get_tpm(struct udevice **devp)
{
	int rc;

	/*
	 * To keep a backward compatibility with previous code,
	 * if a tpm device is not explicitly set, we set the first one.
	 */
	if (!tpm_dev) {
		rc = tpm_set_device(0);
		if (rc) {
			printf("Couldn't set TPM 0 (rc = %d)\n", rc);
			return CMD_RET_FAILURE;
		}
	}

	if (devp)
		*devp = tpm_dev;

	return 0;
}

int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	unsigned long num;
	int rc;

	if (argc == 2) {
		num = simple_strtoul(argv[1], NULL, 10);

		rc = tpm_set_device(num);
		if (rc)
			printf("Couldn't set TPM %lu (rc = %d)\n", num, rc);
	} else {
		rc = tpm_show_device();
	}

	return rc;
}

int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	struct udevice *dev;
	char buf[80];
	int rc;

	rc = get_tpm(&dev);
	if (rc)
		return rc;
	rc = tpm_get_desc(dev, buf, sizeof(buf));
	if (rc < 0) {
		printf("Couldn't get TPM info (%d)\n", rc);
		return CMD_RET_FAILURE;
	}
	printf("%s\n", buf);

	return 0;
}

int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	struct udevice *dev;
	int rc;

	if (argc != 1)
		return CMD_RET_USAGE;
	rc = get_tpm(&dev);
	if (rc)
		return rc;

	return report_return_code(tpm_init(dev));
}

int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	struct cmd_tbl *tpm_commands, *cmd;
	struct tpm_chip_priv *priv;
	struct udevice *dev;
	unsigned int size;
	int ret;

	if (argc < 2)
		return CMD_RET_USAGE;

	ret = get_tpm(&dev);
	if (ret)
		return ret;

	priv = dev_get_uclass_priv(dev);

	/* Below getters return NULL if the desired stack is not built */
	switch (priv->version) {
	case TPM_V1:
		tpm_commands = get_tpm1_commands(&size);
		break;
	case TPM_V2:
		tpm_commands = get_tpm2_commands(&size);
		break;
	default:
		tpm_commands = NULL;
	}

	if (!tpm_commands)
		return CMD_RET_USAGE;

	cmd = find_cmd_tbl(argv[1], tpm_commands, size);
	if (!cmd)
		return CMD_RET_USAGE;

	return cmd->cmd(cmdtp, flag, argc - 1, argv + 1);
}