xref: /aosp_15_r20/external/vboot_reference/firmware/stub/tpm_lite_stub.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2011 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  *
5  * Stub implementations of utility functions which call their linux-specific
6  * equivalents.
7  */
8 
9 #include <assert.h>
10 #include <stdint.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <time.h>
21 #include <unistd.h>
22 
23 #include "2common.h"
24 #include "2sysincludes.h"
25 #include "tlcl.h"
26 #include "tlcl_internal.h"
27 #include "tss_constants.h"
28 
29 #define TPM_DEVICE_PATH "/dev/tpm0"
30 /* Retry failed open()s for 5 seconds in 10ms polling intervals. */
31 #define OPEN_RETRY_DELAY_NS (10 * 1000 * 1000)
32 #define OPEN_RETRY_MAX_NUM  500
33 #define COMM_RETRY_MAX_NUM  3
34 
35 /* TODO: these functions should pass errors back rather than returning void */
36 /* TODO: if the only callers to these are just wrappers, should just
37  * remove the wrappers and call us directly. */
38 
39 
40 /* The file descriptor for the TPM device.
41  */
42 static int tpm_fd = -1;
43 /* If the library should exit during an OS-level TPM failure.
44  */
45 static int exit_on_failure = 1;
46 
try_exit(uint32_t result)47 static inline uint32_t try_exit(uint32_t result)
48 {
49 	if (exit_on_failure)
50 		exit(1);
51 	return result;
52 }
53 
54 /* Print |n| bytes from array |a| to stderr, with newlines.
55  */
DbgPrintBytes(const uint8_t * a,int n)56 __attribute__((unused)) static void DbgPrintBytes(const uint8_t* a, int n)
57 {
58 	int i;
59 	VB2_DEBUG_RAW("DEBUG: ");
60 	for (i = 0; i < n; i++) {
61 		if (i && i % 16 == 0)
62 			VB2_DEBUG_RAW("\nDEBUG: ");
63 		VB2_DEBUG_RAW("%02x ", a[i]);
64 	}
65 	VB2_DEBUG_RAW("\n");
66 }
67 
68 
69 /* Executes a command on the TPM.
70  */
TpmExecute(const uint8_t * in,const uint32_t in_len,uint8_t * out,uint32_t * pout_len)71 static uint32_t TpmExecute(const uint8_t *in, const uint32_t in_len,
72 			   uint8_t *out, uint32_t *pout_len)
73 {
74 	uint8_t response[TPM_MAX_COMMAND_SIZE];
75 	if (in_len <= 0) {
76 		VB2_DEBUG("ERROR: invalid command length %d for command %#x\n",
77 			  in_len, in[9]);
78 		return try_exit(TPM_E_INPUT_TOO_SMALL);
79 	} else if (tpm_fd < 0) {
80 		VB2_DEBUG("ERROR: the TPM device was not opened.  "
81 			  "Forgot to call TlclLibInit?\n");
82 		return try_exit(TPM_E_NO_DEVICE);
83 	} else {
84 		int n;
85 		int retries = 0;
86 		int first_errno = 0;
87 
88 		/* Write command. Retry in case of communication errors.
89 		 */
90 		for ( ; retries < COMM_RETRY_MAX_NUM; ++retries) {
91 			n = write(tpm_fd, in, in_len);
92 			if (n >= 0) {
93 				break;
94 			}
95 			if (retries == 0) {
96 				first_errno = errno;
97 			}
98 			VB2_DEBUG("TPM: write attempt %d failed: %s\n",
99 				  retries + 1, strerror(errno));
100 		}
101 		if (n < 0) {
102 			VB2_DEBUG("ERROR: write failure to TPM device: %s "
103 				  "(first error %d)\n",
104 				  strerror(errno), first_errno);
105 			return try_exit(TPM_E_WRITE_FAILURE);
106 		} else if (n != in_len) {
107 			VB2_DEBUG("ERROR: bad write size to TPM device: "
108 				  "%d vs %u (%d retries, first error %d)\n",
109 				  n, in_len, retries, first_errno);
110 			return try_exit(TPM_E_WRITE_FAILURE);
111 		}
112 
113 		/* Read response. Retry in case of communication errors.
114 		 */
115 		for (retries = 0, first_errno = 0;
116 		     retries < COMM_RETRY_MAX_NUM; ++retries) {
117 			n = read(tpm_fd, response, sizeof(response));
118 			if (n >= 0) {
119 				break;
120 			}
121 			if (retries == 0) {
122 				first_errno = errno;
123 			}
124 			VB2_DEBUG("TPM: read attempt %d failed: %s\n",
125 				  retries + 1, strerror(errno));
126 		}
127 		if (n == 0) {
128 			VB2_DEBUG("ERROR: null read from TPM device\n");
129 			return try_exit(TPM_E_READ_EMPTY);
130 		} else if (n < 0) {
131 			VB2_DEBUG("ERROR: read failure from TPM device: %s "
132 				  "(first error %d)\n",
133 				  strerror(errno), first_errno);
134 			return try_exit(TPM_E_READ_FAILURE);
135 		} else {
136 			if (n > *pout_len) {
137 				VB2_DEBUG("ERROR: TPM response too long for "
138 					  "output buffer\n");
139 				return try_exit(TPM_E_RESPONSE_TOO_LARGE);
140 			} else {
141 				*pout_len = n;
142 				memcpy(out, response, n);
143 			}
144 		}
145 	}
146 	return TPM_SUCCESS;
147 }
148 
149 /* Gets the tag field of a TPM command.
150  */
151 __attribute__((unused))
TpmTag(const uint8_t * buffer)152 static inline int TpmTag(const uint8_t* buffer)
153 {
154 	uint16_t tag;
155 	FromTpmUint16(buffer, &tag);
156 	return (int) tag;
157 }
158 
159 /* Gets the size field of a TPM command.
160  */
161 __attribute__((unused))
TpmResponseSize(const uint8_t * buffer)162 static inline int TpmResponseSize(const uint8_t* buffer)
163 {
164 	uint32_t size;
165 	FromTpmUint32(buffer + sizeof(uint16_t), &size);
166 	return (int) size;
167 }
168 
vb2ex_tpm_init(void)169 vb2_error_t vb2ex_tpm_init(void)
170 {
171 	char *no_exit = getenv("TPM_NO_EXIT");
172 	if (no_exit)
173 		exit_on_failure = !atoi(no_exit);
174 	return vb2ex_tpm_open();
175 }
176 
vb2ex_tpm_close(void)177 vb2_error_t vb2ex_tpm_close(void)
178 {
179 	if (tpm_fd != -1) {
180 		close(tpm_fd);
181 		tpm_fd = -1;
182 	}
183 	return VB2_SUCCESS;
184 }
185 
vb2ex_tpm_open(void)186 vb2_error_t vb2ex_tpm_open(void)
187 {
188 	const char *device_path;
189 	struct timespec delay;
190 	int retries, saved_errno;
191 
192 	if (tpm_fd >= 0)
193 		return VB2_SUCCESS;  /* Already open */
194 
195 	device_path = getenv("TPM_DEVICE_PATH");
196 	if (device_path == NULL) {
197 		device_path = TPM_DEVICE_PATH;
198 	}
199 
200 	/* Retry TPM opens on EBUSY failures. */
201 	for (retries = 0; retries < OPEN_RETRY_MAX_NUM; ++ retries) {
202 		errno = 0;
203 		tpm_fd = open(device_path, O_RDWR | O_CLOEXEC);
204 		saved_errno = errno;
205 		if (tpm_fd >= 0)
206 			return VB2_SUCCESS;
207 		if (saved_errno != EBUSY)
208 			break;
209 
210 		VB2_DEBUG("TPM: retrying %s: %s\n",
211 			  device_path, strerror(errno));
212 
213 		/* Stall until TPM comes back. */
214 		delay.tv_sec = 0;
215 		delay.tv_nsec = OPEN_RETRY_DELAY_NS;
216 		nanosleep(&delay, NULL);
217 	}
218 	VB2_DEBUG("ERROR: TPM: Cannot open TPM device %s: %s\n",
219 		  device_path, strerror(saved_errno));
220 	return try_exit(VB2_ERROR_UNKNOWN);
221 }
222 
vb2ex_tpm_send_recv(const uint8_t * request,uint32_t request_length,uint8_t * response,uint32_t * response_length)223 uint32_t vb2ex_tpm_send_recv(const uint8_t* request, uint32_t request_length,
224 			     uint8_t* response, uint32_t* response_length)
225 {
226 	/*
227 	 * In a real firmware implementation, this function should contain
228 	 * the equivalent API call for the firmware TPM driver which takes a
229 	 * raw sequence of bytes as input command and a pointer to the
230 	 * output buffer for putting in the results.
231 	 *
232 	 * For EFI firmwares, this can make use of the EFI TPM driver as
233 	 * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20
234 	 * availaible from the TCG website):
235 	 *
236 	 * EFI_STATUS status;
237 	 * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM(
238 	 *		TpmCommandSize(request),
239 	 *              request,
240 	 *              max_length,
241 	 *              response);
242 	 * // Error checking depending on the value of the status above
243 	 */
244 	uint32_t result;
245 
246 #ifdef VBOOT_DEBUG
247 	struct timeval before, after;
248 	VB2_DEBUG("request (%d bytes):\n", request_length);
249 	DbgPrintBytes(request, request_length);
250 	gettimeofday(&before, NULL);
251 #endif
252 
253 	result = TpmExecute(request, request_length, response, response_length);
254 	if (result != TPM_SUCCESS)
255 		return result;
256 
257 #ifdef VBOOT_DEBUG
258 	gettimeofday(&after, NULL);
259 	VB2_DEBUG("response (%d bytes):\n", *response_length);
260 	DbgPrintBytes(response, *response_length);
261 	VB2_DEBUG("execution time: %dms\n",
262 		  (int) ((after.tv_sec - before.tv_sec) * VB2_MSEC_PER_SEC +
263 			 (after.tv_usec - before.tv_usec) / VB2_USEC_PER_MSEC));
264 #endif
265 
266 #ifndef NDEBUG
267 #ifndef TPM2_MODE
268 	/* validity checks */
269 	int tag = TpmTag(request);
270 	int response_tag = TpmTag(response);
271 	assert(
272 		(tag == TPM_TAG_RQU_COMMAND &&
273 		 response_tag == TPM_TAG_RSP_COMMAND) ||
274 		(tag == TPM_TAG_RQU_AUTH1_COMMAND &&
275 		 response_tag == TPM_TAG_RSP_AUTH1_COMMAND) ||
276 		(tag == TPM_TAG_RQU_AUTH2_COMMAND &&
277 		 response_tag == TPM_TAG_RSP_AUTH2_COMMAND));
278 	assert(*response_length == TpmResponseSize(response));
279 #endif
280 #endif
281 
282 	return TPM_SUCCESS;
283 }
284 
vb2ex_tpm_get_random(uint8_t * buf,uint32_t length)285 vb2_error_t vb2ex_tpm_get_random(uint8_t *buf, uint32_t length)
286 {
287 	static int urandom_fd = -1;
288 	if (urandom_fd < 0) {
289 		urandom_fd = open("/dev/urandom", O_RDONLY);
290 		if (urandom_fd == -1) {
291 			return VB2_ERROR_UNKNOWN;
292 		}
293 	}
294 
295 	if (length != read(urandom_fd, buf, length)) {
296 		return VB2_ERROR_UNKNOWN;
297 	}
298 
299 	return VB2_SUCCESS;
300 }
301