1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2013 The Chromium OS Authors.
4 * Coypright (c) 2013 Guntermann & Drunck GmbH
5 */
6
7#define LOG_CATEGORY UCLASS_TPM
8
9#include <dm.h>
10#include <log.h>
11#include <asm/unaligned.h>
12#include <tpm-common.h>
13#include "tpm-utils.h"
14
15enum tpm_version tpm_get_version(struct udevice *dev)
16{
17	struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
18
19	return priv->version;
20}
21
22int pack_byte_string(u8 *str, size_t size, const char *format, ...)
23{
24	va_list args;
25	size_t offset = 0, length = 0;
26	u8 *data = NULL;
27	u32 value = 0;
28
29	va_start(args, format);
30	for (; *format; format++) {
31		switch (*format) {
32		case 'b':
33			offset = va_arg(args, size_t);
34			value = va_arg(args, int);
35			length = 1;
36			break;
37		case 'w':
38			offset = va_arg(args, size_t);
39			value = va_arg(args, int);
40			length = 2;
41			break;
42		case 'd':
43			offset = va_arg(args, size_t);
44			value = va_arg(args, u32);
45			length = 4;
46			break;
47		case 's':
48			offset = va_arg(args, size_t);
49			data = va_arg(args, u8 *);
50			length = va_arg(args, u32);
51			break;
52		default:
53			debug("Couldn't recognize format string\n");
54			va_end(args);
55			return -1;
56		}
57
58		if (offset + length > size) {
59			va_end(args);
60			return -1;
61		}
62
63		switch (*format) {
64		case 'b':
65			str[offset] = value;
66			break;
67		case 'w':
68			put_unaligned_be16(value, str + offset);
69			break;
70		case 'd':
71			put_unaligned_be32(value, str + offset);
72			break;
73		case 's':
74			memcpy(str + offset, data, length);
75			break;
76		}
77	}
78	va_end(args);
79
80	return 0;
81}
82
83int unpack_byte_string(const u8 *str, size_t size, const char *format, ...)
84{
85	va_list args;
86	size_t offset = 0, length = 0;
87	u8 *ptr8 = NULL;
88	u16 *ptr16 = NULL;
89	u32 *ptr32 = NULL;
90
91	va_start(args, format);
92	for (; *format; format++) {
93		switch (*format) {
94		case 'b':
95			offset = va_arg(args, size_t);
96			ptr8 = va_arg(args, u8 *);
97			length = 1;
98			break;
99		case 'w':
100			offset = va_arg(args, size_t);
101			ptr16 = va_arg(args, u16 *);
102			length = 2;
103			break;
104		case 'd':
105			offset = va_arg(args, size_t);
106			ptr32 = va_arg(args, u32 *);
107			length = 4;
108			break;
109		case 's':
110			offset = va_arg(args, size_t);
111			ptr8 = va_arg(args, u8 *);
112			length = va_arg(args, u32);
113			break;
114		default:
115			va_end(args);
116			debug("Couldn't recognize format string\n");
117			return -1;
118		}
119
120		if (offset + length > size) {
121			va_end(args);
122			log_err("Failed to read: size=%zd, offset=%zx, len=%zx\n",
123				size, offset, length);
124			return -1;
125		}
126
127		switch (*format) {
128		case 'b':
129			*ptr8 = str[offset];
130			break;
131		case 'w':
132			*ptr16 = get_unaligned_be16(str + offset);
133			break;
134		case 'd':
135			*ptr32 = get_unaligned_be32(str + offset);
136			break;
137		case 's':
138			memcpy(ptr8, str + offset, length);
139			break;
140		}
141	}
142	va_end(args);
143
144	return 0;
145}
146
147u32 tpm_command_size(const void *command)
148{
149	const size_t command_size_offset = 2;
150
151	return get_unaligned_be32(command + command_size_offset);
152}
153
154u32 tpm_return_code(const void *response)
155{
156	const size_t return_code_offset = 6;
157
158	return get_unaligned_be32(response + return_code_offset);
159}
160
161u32 tpm_sendrecv_command(struct udevice *dev, const void *command,
162			 void *response, size_t *size_ptr)
163{
164	int err, ret;
165	u8 response_buffer[COMMAND_BUFFER_SIZE];
166	size_t response_length;
167	int i;
168	uint size;
169
170	if (response) {
171		response_length = *size_ptr;
172	} else {
173		response = response_buffer;
174		response_length = sizeof(response_buffer);
175	}
176
177	size = tpm_command_size(command);
178
179	/* sanity check, which also helps coverity */
180	if (size > COMMAND_BUFFER_SIZE)
181		return log_msg_ret("size", -E2BIG);
182
183	log_debug("TPM request [size:%d]: ", size);
184	for (i = 0; i < size; i++)
185		log_debug("%02x ", ((u8 *)command)[i]);
186	log_debug("\n");
187
188	err = tpm_xfer(dev, command, size, response, &response_length);
189
190	if (err < 0)
191		return err;
192
193	if (size_ptr)
194		*size_ptr = response_length;
195
196	ret = tpm_return_code(response);
197
198	log_debug("TPM response [ret:%d]: ", ret);
199	for (i = 0; i < response_length; i++)
200		log_debug("%02x ", ((u8 *)response)[i]);
201	log_debug("\n");
202
203	return ret;
204}
205
206int tpm_init(struct udevice *dev)
207{
208	return tpm_open(dev);
209}
210