xref: /aosp_15_r20/external/ms-tpm-20-ref/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/fTPM.c (revision 5c591343844d1f9da7da26467c4bf7efc8a7a413)
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 }