1 /* Microsoft Reference Implementation for TPM 2.0
2 *
3 * The copyright in this software is being made available under the BSD License,
4 * included below. This software may be subject to other third party and
5 * contributor rights, including patent rights, and no such rights are granted
6 * under this license.
7 *
8 * Copyright (c) Microsoft Corporation
9 *
10 * All rights reserved.
11 *
12 * BSD License
13 *
14 * Redistribution and use in source and binary forms, with or without modification,
15 * are permitted provided that the following conditions are met:
16 *
17 * Redistributions of source code must retain the above copyright notice, this list
18 * of conditions and the following disclaimer.
19 *
20 * Redistributions in binary form must reproduce the above copyright notice, this
21 * list of conditions and the following disclaimer in the documentation and/or
22 * other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
28 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #define STR_TRACE_USER_TA "fTPM"
37
38 #include <tee_internal_api.h>
39 #include <tee_internal_api_extensions.h>
40 #include <string.h>
41
42 #include "fTPM.h"
43
44 #define TA_ALL_PARAM_TYPE(type) TEE_PARAM_TYPES(type, type, type, type)
45
46 //
47 // Ensure we have only one active session
48 //
49 static bool fTPMSessionActive = false;
50
51 //
52 // Initialization
53 //
54 bool fTPMInitialized = false;
55
56 //
57 // Local (SW) command buffer
58 //
59 static uint8_t fTPMCommand[MAX_COMMAND_SIZE];
60
61 //
62 // A subset of TPM return codes (see TpmTypes.h)
63 //
64 typedef uint32_t TPM_RC;
65 #define RC_VER1 (TPM_RC) (0x100)
66 #define TPM_RC_SUCCESS (TPM_RC) (0x000)
67 #define TPM_RC_FAILURE (TPM_RC) (RC_VER1+0x001)
68
69 //
70 // Helper functions for byte ordering of TPM commands/responses
71 //
SwapBytes16(uint16_t Value)72 static uint16_t SwapBytes16(uint16_t Value)
73 {
74 return (uint16_t)((Value << 8) | (Value >> 8));
75 }
76
SwapBytes32(uint32_t Value)77 static uint32_t SwapBytes32(uint32_t Value)
78 {
79 uint32_t LowerBytes;
80 uint32_t HigherBytes;
81
82 LowerBytes = (uint32_t)SwapBytes16((uint16_t)Value);
83 HigherBytes = (uint32_t)SwapBytes16((uint16_t)(Value >> 16));
84
85 return (LowerBytes << 16 | HigherBytes);
86 }
87
88 //
89 // Helper function to read response codes from TPM responses
90 //
fTPMResponseCode(uint32_t ResponseSize,uint8_t * ResponseBuffer)91 static uint32_t fTPMResponseCode(uint32_t ResponseSize,
92 uint8_t *ResponseBuffer)
93 {
94 uint32_t ResponseCode;
95 union {
96 uint32_t Data;
97 uint8_t Index[4];
98 } Value;
99
100 // In case of too-small response size, assume failure.
101 if (ResponseSize < 0xA) {
102 return TPM_RC_FAILURE;
103 }
104
105 Value.Index[0] = ResponseBuffer[6];
106 Value.Index[1] = ResponseBuffer[7];
107 Value.Index[2] = ResponseBuffer[8];
108 Value.Index[3] = ResponseBuffer[9];
109 ResponseCode = SwapBytes32(Value.Data);
110
111 return ResponseCode;
112 }
113
114 //
115 // Called when TA instance is created. This is the first call to the TA.
116 //
TA_CreateEntryPoint(void)117 TEE_Result TA_CreateEntryPoint(void)
118 {
119 #define STARTUP_SIZE 0x0C
120
121 uint8_t startupClear[STARTUP_SIZE] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c,
122 0x00, 0x00, 0x01, 0x44, 0x00, 0x00 };
123 uint8_t startupState[STARTUP_SIZE] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c,
124 0x00, 0x00, 0x01, 0x44, 0x00, 0x01 };
125 uint32_t respLen;
126 uint8_t *respBuf;
127
128 #ifdef fTPMDebug
129 DMSG("Entry Point\n");
130 #endif
131
132 // If we've been here before, don't init again.
133 if (fTPMInitialized) {
134 // We may have had TA_DestroyEntryPoint called but we didn't
135 // actually get torn down. Re-NVEnable, just in case.
136 if (_plat__NVEnable(NULL) == 0) {
137 TEE_Panic(TEE_ERROR_BAD_STATE);
138 }
139 return TEE_SUCCESS;
140 }
141
142 // Initialize NV admin state
143 _admin__NvInitState();
144
145 // If we fail to open fTPM storage we cannot continue.
146 if (_plat__NVEnable(NULL) == 0) {
147 TEE_Panic(TEE_ERROR_BAD_STATE);
148 }
149
150 #ifdef fTPMDebug
151 DMSG("NVEnable Complete\n");
152 #endif
153
154 // This only occurs when there is no previous NV state, i.e., on first
155 // boot, after recovering from data loss, we reset the platform, etc.
156 if (_plat__NvNeedsManufacture()) {
157 #ifdef fTPMDebug
158 DMSG("TPM_Manufacture\n");
159 #endif
160 TPM_Manufacture(1);
161 }
162
163 // "Power-On" the platform
164 _plat__Signal_PowerOn();
165
166 // Internal init for reference implementation
167 _TPM_Init();
168
169 #ifdef fTPMDebug
170 DMSG("Init Complete\n");
171 #endif
172
173 // Startup with state
174 if (g_chipFlags.fields.TpmStatePresent) {
175
176 // Re-use request buffer for response (ignored)
177 respBuf = startupState;
178 respLen = STARTUP_SIZE;
179
180 ExecuteCommand(STARTUP_SIZE, startupState, &respLen, &respBuf);
181 if (fTPMResponseCode(respLen, respBuf) == TPM_RC_SUCCESS) {
182 goto Exit;
183 }
184
185 #ifdef fTPMDebug
186 DMSG("Fall through to startup clear\n");
187 #endif
188
189 goto Clear;
190 }
191
192 #ifdef fTPMDebug
193 DMSG("No TPM state present\n");
194 #endif
195
196 Clear:
197 // Re-use request buffer for response (ignored)
198 respBuf = startupClear;
199 respLen = STARTUP_SIZE;
200
201 // Fall back to a Startup Clear
202 ExecuteCommand(STARTUP_SIZE, startupClear, &respLen, &respBuf);
203
204 Exit:
205 // Init is complete, indicate so in fTPM admin state.
206 g_chipFlags.fields.TpmStatePresent = 1;
207 _admin__SaveChipFlags();
208
209 // Initialization complete
210 fTPMInitialized = true;
211
212 return TEE_SUCCESS;
213 }
214
215
216 //
217 // Called when TA instance destroyed. This is the last call in the TA.
218 //
TA_DestroyEntryPoint(void)219 void TA_DestroyEntryPoint(void)
220 {
221 // We should only see this called after the OS has shutdown and there
222 // will be no further commands sent to the TPM. Right now, just close
223 // our storage object, becasue the TPM driver should have already
224 // shutdown cleanly.
225 _plat__NVDisable();
226 return;
227 }
228
229
230 //
231 // Called when a new session is opened to the TA.
232 //
TA_OpenSessionEntryPoint(uint32_t param_types,TEE_Param params[4],void ** sess_ctx)233 TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types,
234 TEE_Param params[4],
235 void **sess_ctx)
236 {
237 uint32_t exp_param_types = TA_ALL_PARAM_TYPE(TEE_PARAM_TYPE_NONE);
238
239 // Unreferenced parameters
240 UNREFERENCED_PARAMETER(params);
241 UNREFERENCED_PARAMETER(sess_ctx);
242
243 // Validate parameter types
244 if (param_types != exp_param_types) {
245 return TEE_ERROR_BAD_PARAMETERS;
246 }
247
248 // Only one active session to the fTPM is permitted
249 if (fTPMSessionActive) {
250 return TEE_ERROR_ACCESS_CONFLICT;
251 }
252
253 // Active session
254 fTPMSessionActive = true;
255
256 // If return value != TEE_SUCCESS the session will not be created.
257 return TEE_SUCCESS;
258 }
259
260
261 //
262 // Called when a session is closed.
263 //
TA_CloseSessionEntryPoint(void * sess_ctx)264 void TA_CloseSessionEntryPoint(void *sess_ctx)
265 {
266 // Unused parameter(s)
267 UNREFERENCED_PARAMETER(sess_ctx);
268
269 // Clear active session
270 if (fTPMSessionActive) {
271 fTPMSessionActive = false;
272 }
273 }
274
275 //
276 // Called to handle command submission.
277 //
fTPM_Submit_Command(uint32_t param_types,TEE_Param params[4])278 static TEE_Result fTPM_Submit_Command(uint32_t param_types,
279 TEE_Param params[4]
280 )
281 {
282 uint8_t *cmdBuf, *respBuf;
283 uint32_t cmdLen, respLen;
284 uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
285 TEE_PARAM_TYPE_MEMREF_INOUT,
286 TEE_PARAM_TYPE_NONE,
287 TEE_PARAM_TYPE_NONE);
288
289 // Validate parameter types
290 if (param_types != exp_param_types) {
291 #ifdef fTPMDebug
292 IMSG("Bad param type(s)\n");
293 #endif
294 return TEE_ERROR_BAD_PARAMETERS;
295 }
296
297 // Sanity check our buffer sizes
298 if ((params[0].memref.size == 0) ||
299 (params[1].memref.size == 0) ||
300 (params[0].memref.size > MAX_COMMAND_SIZE) ||
301 (params[1].memref.size > MAX_RESPONSE_SIZE)) {
302 #ifdef fTPMDebug
303 IMSG("Bad param size(s)\n");
304 #endif
305 return TEE_ERROR_BAD_PARAMETERS;
306 }
307
308 // Copy command locally
309 memcpy(fTPMCommand, params[0].memref.buffer, params[0].memref.size);
310
311 // Pull the command length from the actual TPM command. The memref size
312 // field descibes the buffer containing the command, not the command.
313 cmdBuf = fTPMCommand;
314 cmdLen = BYTE_ARRAY_TO_UINT32((uint8_t *)&(cmdBuf[2]));
315
316 // Sanity check cmd length included in TPM command
317 if (cmdLen > params[0].memref.size) {
318 return TEE_ERROR_BAD_PARAMETERS;
319 }
320
321 respBuf = (uint8_t *)(params[1].memref.buffer);
322 respLen = params[1].memref.size;
323
324 // Check if this is a PPI Command
325 if (!_admin__PPICommand(cmdLen, cmdBuf, &respLen, &respBuf)) {
326 // If not, pass through to TPM
327 ExecuteCommand(cmdLen, cmdBuf, &respLen, &respBuf);
328 }
329
330 // Unfortunately, this cannot be done until after we have our response in
331 // hand. We will, however, make an effort to return at least a portion of
332 // the response along with TEE_ERROR_SHORT_BUFFER.
333 if (respLen > params[1].memref.size)
334 {
335 #ifdef fTPMDebug
336 IMSG("Insufficient buffer length RS: 0x%x > BL: 0x%x\n", respLen, params[1].memref.size);
337 #endif
338 return TEE_ERROR_SHORT_BUFFER;
339 }
340
341 #ifdef fTPMDebug
342 DMSG("Success, RS: 0x%x\n", respLen);
343 #endif
344
345 return TEE_SUCCESS;
346 }
347
348 //
349 // Called to handle PPI commands
350 //
fTPM_Emulate_PPI(uint32_t param_types,TEE_Param params[4])351 static TEE_Result fTPM_Emulate_PPI(uint32_t param_types,
352 TEE_Param params[4]
353 )
354 {
355 uint8_t *cmdBuf, *respBuf;
356 uint32_t cmdLen, respLen;
357 uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
358 TEE_PARAM_TYPE_MEMREF_INOUT,
359 TEE_PARAM_TYPE_NONE,
360 TEE_PARAM_TYPE_NONE);
361
362 // Validate parameter types
363 if (param_types != exp_param_types) {
364 #ifdef fTPMDebug
365 IMSG("Bad param type(s)\n");
366 #endif
367 return TEE_ERROR_BAD_PARAMETERS;
368 }
369
370 // Sanity check our buffer sizes
371 if ((params[0].memref.size == 0) ||
372 (params[1].memref.size == 0) ||
373 (params[0].memref.size > MAX_COMMAND_SIZE) ||
374 (params[1].memref.size > MAX_RESPONSE_SIZE)) {
375 #ifdef fTPMDebug
376 IMSG("Bad param size(s)\n");
377 #endif
378 return TEE_ERROR_BAD_PARAMETERS;
379 }
380
381 // Copy command locally
382 memcpy(fTPMCommand, params[0].memref.buffer, params[0].memref.size);
383
384 cmdBuf = fTPMCommand;
385 cmdLen = params[0].memref.size;
386
387 respBuf = (uint8_t *)(params[1].memref.buffer);
388 respLen = params[1].memref.size;
389
390 // Pass along to platform PPI processing
391 if (_admin__PPIRequest(cmdLen, cmdBuf, &respLen, &respBuf)) {
392 #ifdef fTPMDebug
393 DMSG("Handled PPI command via TA interface\n");
394 #endif
395 }
396 else {
397 #ifdef fTPMDebug
398 IMSG("Failed to handle PPI command via TA interface\n");
399 #endif
400 }
401
402 if (respLen > params[1].memref.size) {
403 #ifdef fTPMDebug
404 IMSG("Insufficient buffer length RS: 0x%x > BL: 0x%x\n", respLen, params[1].memref.size);
405 #endif
406 return TEE_ERROR_SHORT_BUFFER;
407 }
408
409 params[1].memref.size = respLen;
410 return TEE_SUCCESS;
411 }
412
413 //
414 // Called when a TA is invoked. Note, paramters come from normal world.
415 //
TA_InvokeCommandEntryPoint(void * sess_ctx,uint32_t cmd_id,uint32_t param_types,TEE_Param params[4])416 TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx,
417 uint32_t cmd_id,
418 uint32_t param_types,
419 TEE_Param params[4])
420 {
421 // Unused parameter(s)
422 UNREFERENCED_PARAMETER(sess_ctx);
423
424 // Handle command invocation
425 switch (cmd_id) {
426
427 case TA_FTPM_SUBMIT_COMMAND: {
428 return fTPM_Submit_Command(param_types, params);
429 }
430
431 case TA_FTPM_EMULATE_PPI: {
432 return fTPM_Emulate_PPI(param_types, params);
433 }
434
435 default: {
436 return TEE_ERROR_BAD_PARAMETERS;
437 }
438 }
439 }