1 /** @file
2   Api's to communicate with OP-TEE OS (Trusted OS based on ARM TrustZone) via
3   secure monitor calls.
4 
5   Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
6   Copyright (c) 2021, Arm Limited. All rights reserved.<BR>
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <Library/ArmMmuLib.h>
13 #include <Library/ArmSmcLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/BaseLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/OpteeLib.h>
18 
19 #include <IndustryStandard/ArmStdSmc.h>
20 #include <OpteeSmc.h>
21 #include <Uefi.h>
22 
23 STATIC OPTEE_SHARED_MEMORY_INFORMATION OpteeSharedMemoryInformation = { 0 };
24 
25 /**
26   Check for OP-TEE presence.
27 **/
28 BOOLEAN
29 EFIAPI
IsOpteePresent(VOID)30 IsOpteePresent (
31   VOID
32   )
33 {
34   ARM_SMC_ARGS ArmSmcArgs;
35 
36   ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
37   // Send a Trusted OS Calls UID command
38   ArmSmcArgs.Arg0 = ARM_SMC_ID_TOS_UID;
39   ArmCallSmc (&ArmSmcArgs);
40 
41   if ((ArmSmcArgs.Arg0 == OPTEE_OS_UID0) &&
42       (ArmSmcArgs.Arg1 == OPTEE_OS_UID1) &&
43       (ArmSmcArgs.Arg2 == OPTEE_OS_UID2) &&
44       (ArmSmcArgs.Arg3 == OPTEE_OS_UID3)) {
45     return TRUE;
46   } else {
47     return FALSE;
48   }
49 }
50 
51 STATIC
52 EFI_STATUS
OpteeSharedMemoryRemap(VOID)53 OpteeSharedMemoryRemap (
54   VOID
55   )
56 {
57   ARM_SMC_ARGS                 ArmSmcArgs;
58   EFI_PHYSICAL_ADDRESS         PhysicalAddress;
59   EFI_PHYSICAL_ADDRESS         Start;
60   EFI_PHYSICAL_ADDRESS         End;
61   EFI_STATUS                   Status;
62   UINTN                        Size;
63 
64   ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
65   ArmSmcArgs.Arg0 = OPTEE_SMC_GET_SHARED_MEMORY_CONFIG;
66 
67   ArmCallSmc (&ArmSmcArgs);
68   if (ArmSmcArgs.Arg0 != OPTEE_SMC_RETURN_OK) {
69     DEBUG ((DEBUG_WARN, "OP-TEE shared memory not supported\n"));
70     return EFI_UNSUPPORTED;
71   }
72 
73   if (ArmSmcArgs.Arg3 != OPTEE_SMC_SHARED_MEMORY_CACHED) {
74     DEBUG ((DEBUG_WARN, "OP-TEE: Only normal cached shared memory supported\n"));
75     return EFI_UNSUPPORTED;
76   }
77 
78   Start = (ArmSmcArgs.Arg1 + SIZE_4KB - 1) & ~(SIZE_4KB - 1);
79   End = (ArmSmcArgs.Arg1 + ArmSmcArgs.Arg2) & ~(SIZE_4KB - 1);
80   PhysicalAddress = Start;
81   Size = End - Start;
82 
83   if (Size < SIZE_4KB) {
84     DEBUG ((DEBUG_WARN, "OP-TEE shared memory too small\n"));
85     return EFI_BUFFER_TOO_SMALL;
86   }
87 
88   Status = ArmSetMemoryAttributes (PhysicalAddress, Size, EFI_MEMORY_WB);
89   if (EFI_ERROR (Status)) {
90     return Status;
91   }
92 
93   OpteeSharedMemoryInformation.Base = (UINTN)PhysicalAddress;
94   OpteeSharedMemoryInformation.Size = Size;
95 
96   return EFI_SUCCESS;
97 }
98 
99 EFI_STATUS
100 EFIAPI
OpteeInit(VOID)101 OpteeInit (
102   VOID
103   )
104 {
105   EFI_STATUS      Status;
106 
107   if (!IsOpteePresent ()) {
108     DEBUG ((DEBUG_WARN, "OP-TEE not present\n"));
109     return EFI_UNSUPPORTED;
110   }
111 
112   Status = OpteeSharedMemoryRemap ();
113   if (EFI_ERROR (Status)) {
114     DEBUG ((DEBUG_WARN, "OP-TEE shared memory remap failed\n"));
115     return Status;
116   }
117 
118   return EFI_SUCCESS;
119 }
120 
121 STATIC
122 BOOLEAN
IsOpteeSmcReturnRpc(UINT32 Return)123 IsOpteeSmcReturnRpc (
124   UINT32 Return
125   )
126 {
127   return (Return != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION) &&
128          ((Return & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) ==
129           OPTEE_SMC_RETURN_RPC_PREFIX);
130 }
131 
132 /**
133   Does Standard SMC to OP-TEE in secure world.
134 
135   @param[in]  PhysicalArg   Physical address of message to pass to secure world
136 
137   @return                   0 on success, secure world return code otherwise
138 
139 **/
140 STATIC
141 UINT32
OpteeCallWithArg(IN UINT64 PhysicalArg)142 OpteeCallWithArg (
143   IN UINT64 PhysicalArg
144   )
145 {
146   ARM_SMC_ARGS ArmSmcArgs;
147 
148   ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
149   ArmSmcArgs.Arg0 = OPTEE_SMC_CALL_WITH_ARG;
150   ArmSmcArgs.Arg1 = (UINT32)(PhysicalArg >> 32);
151   ArmSmcArgs.Arg2 = (UINT32)PhysicalArg;
152 
153   while (TRUE) {
154     ArmCallSmc (&ArmSmcArgs);
155 
156     if (IsOpteeSmcReturnRpc (ArmSmcArgs.Arg0)) {
157       switch (ArmSmcArgs.Arg0) {
158       case OPTEE_SMC_RETURN_RPC_FOREIGN_INTERRUPT:
159         //
160         // A foreign interrupt was raised while secure world was
161         // executing, since they are handled in UEFI a dummy RPC is
162         // performed to let UEFI take the interrupt through the normal
163         // vector.
164         //
165         break;
166 
167       default:
168          // Do nothing in case RPC is not implemented.
169         break;
170       }
171 
172       ArmSmcArgs.Arg0 = OPTEE_SMC_RETURN_FROM_RPC;
173     } else {
174       break;
175     }
176   }
177 
178   return ArmSmcArgs.Arg0;
179 }
180 
181 STATIC
182 VOID
EfiGuidToRfc4122Uuid(OUT RFC4122_UUID * Rfc4122Uuid,IN EFI_GUID * Guid)183 EfiGuidToRfc4122Uuid (
184   OUT RFC4122_UUID       *Rfc4122Uuid,
185   IN EFI_GUID            *Guid
186   )
187 {
188   Rfc4122Uuid->Data1 = SwapBytes32 (Guid->Data1);
189   Rfc4122Uuid->Data2 = SwapBytes16 (Guid->Data2);
190   Rfc4122Uuid->Data3 = SwapBytes16 (Guid->Data3);
191   CopyMem (Rfc4122Uuid->Data4, Guid->Data4, sizeof (Rfc4122Uuid->Data4));
192 }
193 
194 EFI_STATUS
195 EFIAPI
OpteeOpenSession(IN OUT OPTEE_OPEN_SESSION_ARG * OpenSessionArg)196 OpteeOpenSession (
197   IN OUT OPTEE_OPEN_SESSION_ARG      *OpenSessionArg
198   )
199 {
200   OPTEE_MESSAGE_ARG    *MessageArg;
201 
202   MessageArg = NULL;
203 
204   if (OpteeSharedMemoryInformation.Base == 0) {
205     DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
206     return EFI_NOT_STARTED;
207   }
208 
209   MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
210   ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
211 
212   MessageArg->Command = OPTEE_MESSAGE_COMMAND_OPEN_SESSION;
213 
214   //
215   // Initialize and add the meta parameters needed when opening a
216   // session.
217   //
218   MessageArg->Params[0].Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT |
219                                     OPTEE_MESSAGE_ATTRIBUTE_META;
220   MessageArg->Params[1].Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT |
221                                     OPTEE_MESSAGE_ATTRIBUTE_META;
222   EfiGuidToRfc4122Uuid (
223     (RFC4122_UUID *)&MessageArg->Params[0].Union.Value,
224     &OpenSessionArg->Uuid
225     );
226   ZeroMem (&MessageArg->Params[1].Union.Value, sizeof (EFI_GUID));
227   MessageArg->Params[1].Union.Value.C = OPTEE_LOGIN_PUBLIC;
228 
229   MessageArg->NumParams = 2;
230 
231   if (OpteeCallWithArg ((UINTN)MessageArg) != 0) {
232     MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
233     MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
234   }
235 
236   OpenSessionArg->Session = MessageArg->Session;
237   OpenSessionArg->Return = MessageArg->Return;
238   OpenSessionArg->ReturnOrigin = MessageArg->ReturnOrigin;
239 
240   return EFI_SUCCESS;
241 }
242 
243 EFI_STATUS
244 EFIAPI
OpteeCloseSession(IN UINT32 Session)245 OpteeCloseSession (
246   IN UINT32                  Session
247   )
248 {
249   OPTEE_MESSAGE_ARG    *MessageArg;
250 
251   MessageArg = NULL;
252 
253   if (OpteeSharedMemoryInformation.Base == 0) {
254     DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
255     return EFI_NOT_STARTED;
256   }
257 
258   MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
259   ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
260 
261   MessageArg->Command = OPTEE_MESSAGE_COMMAND_CLOSE_SESSION;
262   MessageArg->Session = Session;
263 
264   OpteeCallWithArg ((UINTN)MessageArg);
265 
266   return EFI_SUCCESS;
267 }
268 
269 STATIC
270 EFI_STATUS
OpteeToMessageParam(OUT OPTEE_MESSAGE_PARAM * MessageParams,IN UINT32 NumParams,IN OPTEE_MESSAGE_PARAM * InParams)271 OpteeToMessageParam (
272   OUT OPTEE_MESSAGE_PARAM    *MessageParams,
273   IN UINT32                  NumParams,
274   IN OPTEE_MESSAGE_PARAM     *InParams
275   )
276 {
277   UINT32                  Idx;
278   UINTN                   ParamSharedMemoryAddress;
279   UINTN                   SharedMemorySize;
280   UINTN                   Size;
281 
282   Size = (sizeof (OPTEE_MESSAGE_ARG) + sizeof (UINT64) - 1) &
283           ~(sizeof (UINT64) - 1);
284   ParamSharedMemoryAddress = OpteeSharedMemoryInformation.Base + Size;
285   SharedMemorySize = OpteeSharedMemoryInformation.Size - Size;
286 
287   for (Idx = 0; Idx < NumParams; Idx++) {
288     CONST OPTEE_MESSAGE_PARAM    *InParam;
289     OPTEE_MESSAGE_PARAM          *MessageParam;
290     UINT32                       Attribute;
291 
292     InParam = InParams + Idx;
293     MessageParam = MessageParams + Idx;
294     Attribute = InParam->Attribute & OPTEE_MESSAGE_ATTRIBUTE_TYPE_MASK;
295 
296     switch (Attribute) {
297     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE:
298       MessageParam->Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE;
299       ZeroMem (&MessageParam->Union, sizeof (MessageParam->Union));
300       break;
301 
302     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT:
303     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_OUTPUT:
304     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INOUT:
305       MessageParam->Attribute = Attribute;
306       MessageParam->Union.Value.A = InParam->Union.Value.A;
307       MessageParam->Union.Value.B = InParam->Union.Value.B;
308       MessageParam->Union.Value.C = InParam->Union.Value.C;
309       break;
310 
311     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INPUT:
312     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_OUTPUT:
313     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INOUT:
314       MessageParam->Attribute = Attribute;
315 
316       if (InParam->Union.Memory.Size > SharedMemorySize) {
317         return EFI_OUT_OF_RESOURCES;
318       }
319 
320       CopyMem (
321         (VOID *)ParamSharedMemoryAddress,
322         (VOID *)(UINTN)InParam->Union.Memory.BufferAddress,
323         InParam->Union.Memory.Size
324         );
325       MessageParam->Union.Memory.BufferAddress = (UINT64)ParamSharedMemoryAddress;
326       MessageParam->Union.Memory.Size = InParam->Union.Memory.Size;
327 
328       Size = (InParam->Union.Memory.Size + sizeof (UINT64) - 1) &
329               ~(sizeof (UINT64) - 1);
330       ParamSharedMemoryAddress += Size;
331       SharedMemorySize -= Size;
332       break;
333 
334     default:
335       return EFI_INVALID_PARAMETER;
336     }
337   }
338 
339   return EFI_SUCCESS;
340 }
341 
342 STATIC
343 EFI_STATUS
OpteeFromMessageParam(OUT OPTEE_MESSAGE_PARAM * OutParams,IN UINT32 NumParams,IN OPTEE_MESSAGE_PARAM * MessageParams)344 OpteeFromMessageParam (
345   OUT OPTEE_MESSAGE_PARAM    *OutParams,
346   IN UINT32                  NumParams,
347   IN OPTEE_MESSAGE_PARAM     *MessageParams
348   )
349 {
350   UINT32                 Idx;
351 
352   for (Idx = 0; Idx < NumParams; Idx++) {
353     OPTEE_MESSAGE_PARAM          *OutParam;
354     CONST OPTEE_MESSAGE_PARAM    *MessageParam;
355     UINT32                   Attribute;
356 
357     OutParam = OutParams + Idx;
358     MessageParam = MessageParams + Idx;
359     Attribute = MessageParam->Attribute & OPTEE_MESSAGE_ATTRIBUTE_TYPE_MASK;
360 
361     switch (Attribute) {
362     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE:
363       OutParam->Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE;
364       ZeroMem (&OutParam->Union, sizeof (OutParam->Union));
365       break;
366 
367     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT:
368     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_OUTPUT:
369     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INOUT:
370       OutParam->Attribute = Attribute;
371       OutParam->Union.Value.A = MessageParam->Union.Value.A;
372       OutParam->Union.Value.B = MessageParam->Union.Value.B;
373       OutParam->Union.Value.C = MessageParam->Union.Value.C;
374       break;
375 
376     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INPUT:
377     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_OUTPUT:
378     case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INOUT:
379       OutParam->Attribute = Attribute;
380 
381       if (MessageParam->Union.Memory.Size > OutParam->Union.Memory.Size) {
382         return EFI_BAD_BUFFER_SIZE;
383       }
384 
385       CopyMem (
386         (VOID *)(UINTN)OutParam->Union.Memory.BufferAddress,
387         (VOID *)(UINTN)MessageParam->Union.Memory.BufferAddress,
388         MessageParam->Union.Memory.Size
389         );
390       OutParam->Union.Memory.Size = MessageParam->Union.Memory.Size;
391       break;
392 
393     default:
394       return EFI_INVALID_PARAMETER;
395     }
396   }
397 
398   return EFI_SUCCESS;
399 }
400 
401 EFI_STATUS
402 EFIAPI
OpteeInvokeFunction(IN OUT OPTEE_INVOKE_FUNCTION_ARG * InvokeFunctionArg)403 OpteeInvokeFunction (
404   IN OUT OPTEE_INVOKE_FUNCTION_ARG       *InvokeFunctionArg
405   )
406 {
407   EFI_STATUS       Status;
408   OPTEE_MESSAGE_ARG    *MessageArg;
409 
410   MessageArg = NULL;
411 
412   if (OpteeSharedMemoryInformation.Base == 0) {
413     DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
414     return EFI_NOT_STARTED;
415   }
416 
417   MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
418   ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
419 
420   MessageArg->Command = OPTEE_MESSAGE_COMMAND_INVOKE_FUNCTION;
421   MessageArg->Function = InvokeFunctionArg->Function;
422   MessageArg->Session = InvokeFunctionArg->Session;
423 
424   Status = OpteeToMessageParam (
425              MessageArg->Params,
426              OPTEE_MAX_CALL_PARAMS,
427              InvokeFunctionArg->Params
428              );
429   if (Status) {
430     return Status;
431   }
432 
433   MessageArg->NumParams = OPTEE_MAX_CALL_PARAMS;
434 
435   if (OpteeCallWithArg ((UINTN)MessageArg) != 0) {
436     MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
437     MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
438   }
439 
440   if (OpteeFromMessageParam (
441         InvokeFunctionArg->Params,
442         OPTEE_MAX_CALL_PARAMS,
443         MessageArg->Params
444         ) != 0) {
445     MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
446     MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
447   }
448 
449   InvokeFunctionArg->Return = MessageArg->Return;
450   InvokeFunctionArg->ReturnOrigin = MessageArg->ReturnOrigin;
451 
452   return EFI_SUCCESS;
453 }
454