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