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