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