1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Copyright (c) 2024, Linaro Limited
4  */
5 /* Microsoft Reference Implementation for TPM 2.0
6  *
7  * The copyright in this software is being made available under the BSD
8  * License, included below. This software may be subject to other third
9  * party and contributor rights, including patent rights, and no such
10  * rights are granted under this license.
11  *
12  * Copyright (c) 2018 Microsoft Corporation
13  *
14  * All rights reserved.
15  *
16  * BSD License
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions are
20  * met:
21  *
22  * Redistributions of source code must retain the above copyright notice,
23  * this list of conditions and the following disclaimer.
24  *
25  * Redistributions in binary form must reproduce the above copyright
26  * notice, this list of conditions and the following disclaimer in the
27  * documentation and/or other materials provided with the distribution.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
32  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
35  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  */
41 
42 //**Introduction
43 // This file contains the emulated Physical Presence Interface.
44 
45 #include <Tpm.h>
46 #include "assert.h"
47 #include "Admin.h"
48 #include "string.h"
49 
50 #include <tee_internal_api.h>
51 #include <tee_internal_api_extensions.h>
52 
53 #define TPM_CC_EmulatePPI     0x200001FF
54 
55 //
56 // Hand marshaling, unmarshaling, and maximally sized structures for EmulatePPI
57 //
58 #pragma pack (push, 1)
59 typedef struct {
60     TPM_ST tag;
61     UINT32 paramSize;
62     TPM_CC commandCode;
63 } TPM2_COMMAND_HEADER;
64 
65 typedef struct {
66     TPM_ST tag;
67     UINT32 paramSize;
68     TPM_RC responseCode;
69 } TPM2_RESPONSE_HEADER;
70 
71 typedef struct{
72     UINT32 FunctionIndex;
73     UINT32 Op;
74 } EmulatePPI_In;
75 
76 typedef struct{
77     UINT32 Result1;
78     UINT32 Result2;
79     UINT32 Result3;
80 } EmulatePPI_Out;
81 
82 typedef struct{
83     TPM2_COMMAND_HEADER header;
84     EmulatePPI_In inputParameters;
85 } TPM2_EmulatePPI_cmd_t;
86 
87 typedef struct{
88     TPM2_RESPONSE_HEADER header;
89     EmulatePPI_Out outputParameters;
90 } TPM2_EmulatePPI_res_t;
91 #pragma pack (pop)
92 
93 FTPM_PPI_STATE s_PPIState;
94 
95 extern int _plat__NvCommit(void);
96 
97 static void
ExecutePPICommand(_In_ UINT32 FunctionIndex,_In_ UINT32 Op,_Out_ UINT32 * Result1,_Out_ UINT32 * Result2,_Out_ UINT32 * Result3)98 ExecutePPICommand(
99     _In_  UINT32 FunctionIndex,
100     _In_  UINT32 Op,
101     _Out_ UINT32 *Result1,
102     _Out_ UINT32 *Result2,
103     _Out_ UINT32 *Result3
104     )
105 {
106     UINT32 retVal1 = 0;
107     UINT32 retVal2 = 0;
108     UINT32 retVal3 = 0;
109 
110     _admin__RestorePPIState();
111 
112     memset(Result1, 0, sizeof(UINT32));
113     memset(Result2, 0, sizeof(UINT32));
114     memset(Result3, 0, sizeof(UINT32));
115 
116     switch (FunctionIndex) {
117     case FTPM_PPI_CMD_QUERY:
118         retVal1 = 0x1AB;             // Per PPI 1.2 specification
119         break;
120 
121     case FTPM_PPI_CMD_VERSION:
122         retVal1 = FTPM_PPI_VERSION;  // String "1.2"
123         break;
124 
125     case FTPM_PPI_CMD_SUBMIT_OP_REQ:
126     case FTPM_PPI_CMD_GET_PLATFORM_ACTION:
127         retVal1 = 2;                 // Reboot/General Failure
128         break;
129 
130     case FTPM_PPI_CMD_GET_PENDING_OP:
131         retVal1 = 0;                 // Success
132         retVal2 = s_PPIState.PendingPseudoOp;
133         break;
134 
135     case FTPM_PPI_CMD_RETURN_OP_RESP:
136         retVal1 = 0;                 // Success
137         retVal2 = s_PPIState.PseudoOpFromLastBoot;
138         retVal3 = s_PPIState.ReturnResponse;
139         break;
140 
141     case FTPM_PPI_CMD_SUBMIT_USER_LANG:
142         retVal1 = 3;                 // Not Implemented
143         break;
144 
145     case FTPM_PPI_CMD_SUBMIT_OP_REQ2:
146         switch (Op) {
147         case FTPM_PPI_OP_NOP:
148         case FTPM_PPI_OP_ENABLE:
149         case FTPM_PPI_OP_DISABLE:
150         case FTPM_PPI_OP_ACTIVATE:
151         case FTPM_PPI_OP_DEACTIVATE:
152         case FTPM_PPI_OP_CLEAR:                  // Causes Clear
153         case FTPM_PPI_OP_E_A:
154         case FTPM_PPI_OP_D_D:
155         case FTPM_PPI_OP_OWNERINSTALL_TRUE:
156         case FTPM_PPI_OP_OWNERINSTALL_FALSE:
157         case FTPM_PPI_OP_E_A_OI_TRUE:
158         case FTPM_PPI_OP_OI_FALSE_D_D:
159         case FTPM_PPI_OP_FIELD_UPGRADE:
160         case FTPM_PPI_OP_OPERATOR_AUTH:
161         case FTPM_PPI_OP_C_E_A:                  // Causes Clear
162         case FTPM_PPI_OP_SET_NO_PROV_FALSE:
163         case FTPM_PPI_OP_SET_NO_PROV_TRUE:
164         case FTPM_PPI_OP_SET_NO_MAINT_FALSE:
165         case FTPM_PPI_OP_SET_NO_MAINT_TRUE:
166         case FTPM_PPI_OP_E_A_C:                  // Causes Clear
167         case FTPM_PPI_OP_E_A_C_E_A:              // Causes Clear
168             retVal1 = 0;                        // Success
169             s_PPIState.PendingPseudoOp = Op;
170             _admin__SavePPIState();
171             break;
172 
173         case FTPM_PPI_OP_SET_NO_CLEAR_FALSE:
174         case FTPM_PPI_OP_SET_NO_CLEAR_TRUE:
175         default:
176             retVal1 = 1;                       // Not Implemented
177             break;
178         }
179         break;
180 
181     case FTPM_PPI_CMD_GET_USER_CONF:
182         switch (Op) {
183         case FTPM_PPI_OP_NOP:
184         case FTPM_PPI_OP_ENABLE:
185         case FTPM_PPI_OP_DISABLE:
186         case FTPM_PPI_OP_ACTIVATE:
187         case FTPM_PPI_OP_DEACTIVATE:
188         case FTPM_PPI_OP_E_A:
189         case FTPM_PPI_OP_D_D:
190         case FTPM_PPI_OP_OWNERINSTALL_TRUE:
191         case FTPM_PPI_OP_OWNERINSTALL_FALSE:
192         case FTPM_PPI_OP_E_A_OI_TRUE:
193         case FTPM_PPI_OP_OI_FALSE_D_D:
194             retVal1 = 4;    // Allowed and PP user NOT required
195             break;
196 
197         case FTPM_PPI_OP_CLEAR:
198         case FTPM_PPI_OP_C_E_A:
199         case FTPM_PPI_OP_E_A_C:
200         case FTPM_PPI_OP_E_A_C_E_A:
201             retVal1 = 3;    // Allowed and PP user required
202             break;
203 
204         default:
205             retVal1 = 0;    // Not Implemented
206             break;
207         }
208         break;
209 
210     default:
211         break;
212     }
213 
214     memcpy(Result1, &retVal1, sizeof(UINT32));
215     memcpy(Result2, &retVal2, sizeof(UINT32));
216     memcpy(Result3, &retVal3, sizeof(UINT32));
217 }
218 
219 static TPM2_EmulatePPI_res_t PPIResponse;
220 
221 #pragma warning(push)
222 #pragma warning(disable:28196)
223 //
224 // The fTPM TA (OpTEE) may receive, from the TrEE driver, a PPI request
225 // thru it's ACPI inteface rather than via the TPM_Emulate_PPI command
226 // we're used to. This function creates a well formes TPM_Emulate_PPI
227 // command and forwards the request on to _admin__PPICommand to handle.
228 //
229 // Return:
230 //          0 - Omproperly formatted PPI command.
231 //  Otherwise - Return from _admin__PPICommand
232 //
233 int
_admin__PPIRequest(UINT32 CommandSize,__in_ecount (CommandSize)UINT8 * CommandBuffer,UINT32 * ResponseSize,__deref_out_ecount (* ResponseSize)UINT8 ** ResponseBuffer)234 _admin__PPIRequest(
235                                         UINT32  CommandSize,
236     __in_ecount(CommandSize)            UINT8   *CommandBuffer,
237                                         UINT32  *ResponseSize,
238     __deref_out_ecount(*ResponseSize)   UINT8   **ResponseBuffer
239     )
240 {
241     TPM2_EmulatePPI_cmd_t cmd;
242     TPM2_EmulatePPI_res_t rsp;
243     TPM2_EmulatePPI_res_t *rspPtr = &rsp;
244     UINT32 rspLen = sizeof(TPM2_EmulatePPI_res_t);
245     UINT8 *CmdBuffer;
246 
247     // Drop request if CommandSize is invalid
248     if (CommandSize < sizeof(UINT32)) {
249         return 0;
250     }
251 
252     CmdBuffer = CommandBuffer;
253 
254     cmd.header.tag = __builtin_bswap16(TPM_ST_NO_SESSIONS);
255     cmd.header.paramSize = __builtin_bswap32(sizeof(TPM2_EmulatePPI_cmd_t));
256     cmd.header.commandCode = __builtin_bswap32(TPM_CC_EmulatePPI);
257 
258     cmd.inputParameters.FunctionIndex = BYTE_ARRAY_TO_UINT32(CmdBuffer);
259     CmdBuffer += sizeof(UINT32);
260     CommandSize -= sizeof(UINT32);
261 
262     // Parameter checking is done in _admin__PPICommand but we still need
263     // to sanity check the size field so as not to overrun CommandBuffer.
264     if (CommandSize > 0) {
265 
266         if (CommandSize < sizeof(UINT32))
267             return 0;
268 
269         cmd.inputParameters.Op = BYTE_ARRAY_TO_UINT32(CmdBuffer);
270     }
271 
272     if (!_admin__PPICommand(sizeof(TPM2_EmulatePPI_cmd_t),
273                             (UINT8 *)&cmd,
274                             &rspLen,
275                             (UINT8**)&rspPtr)) {
276         return 0;
277     }
278 
279     memcpy(*ResponseBuffer, &(rsp.outputParameters.Result1), (rspLen - sizeof(TPM2_RESPONSE_HEADER)));
280     *ResponseSize = (rspLen - sizeof(TPM2_RESPONSE_HEADER));
281     return 1;
282 }
283 
284 //
285 // Return:
286 //  1 - Command has been consumed
287 //  0 - Not a properly formated PPI command, caller should pass through to TPM
288 //
289 int
_admin__PPICommand(UINT32 CommandSize,__in_ecount (CommandSize)UINT8 * CommandBuffer,UINT32 * ResponseSize,__deref_out_ecount (* ResponseSize)UINT8 ** ResponseBuffer)290 _admin__PPICommand(
291                                         UINT32  CommandSize,
292     __in_ecount(CommandSize)            UINT8   *CommandBuffer,
293                                         UINT32  *ResponseSize,
294     __deref_out_ecount(*ResponseSize)   UINT8   **ResponseBuffer
295 )
296 {
297     TPM2_EmulatePPI_cmd_t cmd;
298     UINT8 *CmdBuffer;
299     UINT32 FunctionIndex;
300     UINT32 Op;
301     UINT32 NumberResults = 0;
302     UINT16 Tag;
303 
304     memset(&PPIResponse, 0, sizeof(PPIResponse));
305     memset(&cmd, 0, sizeof(cmd));
306 
307     CmdBuffer = CommandBuffer;
308 
309     if (CommandSize < sizeof(TPM2_COMMAND_HEADER)) {
310         PPIResponse.header.responseCode = TPM_RC_COMMAND_SIZE;
311         goto Exit;
312     }
313 
314     cmd.header.tag = BYTE_ARRAY_TO_UINT16(CmdBuffer);
315     CmdBuffer += sizeof(UINT16);
316     CommandSize -= sizeof(UINT16);
317 
318     cmd.header.paramSize = BYTE_ARRAY_TO_UINT32(CmdBuffer);
319     CmdBuffer += sizeof(UINT32);
320     CommandSize -= sizeof(UINT32);
321 
322     cmd.header.commandCode = BYTE_ARRAY_TO_UINT32(CmdBuffer);
323     CmdBuffer += sizeof(UINT32);
324     CommandSize -= sizeof(UINT32);
325 
326     //
327     // First check that this must be the command we want to execute
328     //
329     if (cmd.header.commandCode != TPM_CC_EmulatePPI) {
330         return 0;
331     }
332 
333     //
334     // Must not be a session
335     //
336     if (cmd.header.tag != TPM_ST_NO_SESSIONS) {
337         PPIResponse.header.responseCode = TPM_RC_BAD_TAG;
338         goto Exit;
339     }
340 
341     //
342     // Must have enough command space left
343     //
344     if (cmd.header.paramSize < CommandSize) {
345         PPIResponse.header.responseCode = TPM_RC_COMMAND_SIZE;
346         goto Exit;
347     }
348 
349     if (CommandSize < sizeof(UINT32)) {
350         PPIResponse.header.responseCode = TPM_RC_COMMAND_SIZE;
351         goto Exit;
352     }
353 
354     FunctionIndex = BYTE_ARRAY_TO_UINT32(CmdBuffer);
355     CmdBuffer += sizeof(UINT32);
356     CommandSize -= sizeof(UINT32);
357 
358     switch (FunctionIndex) {
359     case FTPM_PPI_CMD_QUERY:
360     case FTPM_PPI_CMD_VERSION:
361     case FTPM_PPI_CMD_SUBMIT_OP_REQ:
362     case FTPM_PPI_CMD_GET_PLATFORM_ACTION:
363     case FTPM_PPI_CMD_SUBMIT_USER_LANG:
364         NumberResults = 1;
365         Op = 0;
366         break;
367 
368     case FTPM_PPI_CMD_GET_PENDING_OP:
369         NumberResults = 2;
370         Op = 0;
371         break;
372 
373     case FTPM_PPI_CMD_RETURN_OP_RESP:
374         NumberResults = 3;
375         Op = 0;
376         break;
377 
378     case FTPM_PPI_CMD_SUBMIT_OP_REQ2:
379     case FTPM_PPI_CMD_GET_USER_CONF:
380         NumberResults = 1;
381 
382         if (CommandSize < sizeof(UINT32)) {
383             PPIResponse.header.responseCode = TPM_RC_COMMAND_SIZE;
384             goto Exit;
385         }
386 
387         Op = BYTE_ARRAY_TO_UINT32(CmdBuffer);
388         CmdBuffer += sizeof(UINT32);
389         CommandSize -= sizeof(UINT32);
390         break;
391 
392     default:
393         NumberResults = 0;
394         PPIResponse.header.responseCode = TPM_RC_FAILURE;
395         goto Exit;
396     }
397 
398 
399     ExecutePPICommand(FunctionIndex,
400                       Op,
401 #pragma warning (push)
402 #pragma warning (disable:4366)  // The result of unary '&' may be unaligned
403                       &PPIResponse.outputParameters.Result1,
404                       &PPIResponse.outputParameters.Result2,
405                       &PPIResponse.outputParameters.Result3);
406 #pragma warning (pop)
407 
408     PPIResponse.header.responseCode = TPM_RC_SUCCESS;
409 
410 Exit:
411     if (PPIResponse.header.responseCode != TPM_RC_SUCCESS) {
412         NumberResults = 0;
413     }
414 
415     *ResponseSize = sizeof(TPM2_RESPONSE_HEADER) + (NumberResults * sizeof(UINT32));
416 
417     //
418     // Fill in tag, and size
419     //
420     Tag = TPM_ST_NO_SESSIONS;
421     PPIResponse.header.tag = BYTE_ARRAY_TO_UINT16((BYTE *)&Tag);
422     PPIResponse.header.paramSize = BYTE_ARRAY_TO_UINT32((BYTE *)ResponseSize);
423     PPIResponse.header.responseCode = BYTE_ARRAY_TO_UINT32((BYTE *)&PPIResponse.header.responseCode);
424 
425     //
426     // Results are in host byte order
427     //
428     memcpy(*ResponseBuffer, &PPIResponse, (sizeof(PPIResponse) < *ResponseSize) ? sizeof(PPIResponse) : *ResponseSize);
429 
430     return 1;
431 }
432 #pragma warning(pop)
433 
434