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