1 /** @file
2 
3   Copyright (c) 2016 HP Development Company, L.P.
4   Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <Base.h>
11 #include <Pi/PiMmCis.h>
12 
13 
14 #include <Library/ArmSvcLib.h>
15 #include <Library/ArmLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/HobLib.h>
19 
20 #include <Protocol/DebugSupport.h> // for EFI_SYSTEM_CONTEXT
21 
22 #include <Guid/ZeroGuid.h>
23 #include <Guid/MmramMemoryReserve.h>
24 
25 #include <IndustryStandard/ArmFfaSvc.h>
26 #include <IndustryStandard/ArmStdSmc.h>
27 
28 #include "StandaloneMmCpu.h"
29 
30 EFI_STATUS
31 EFIAPI
32 MmFoundationEntryRegister (
33   IN CONST EFI_MM_CONFIGURATION_PROTOCOL  *This,
34   IN EFI_MM_ENTRY_POINT                    MmEntryPoint
35   );
36 
37 //
38 // On ARM platforms every event is expected to have a GUID associated with
39 // it. It will be used by the MM Entry point to find the handler for the
40 // event. It will either be populated in a EFI_MM_COMMUNICATE_HEADER by the
41 // caller of the event (e.g. MM_COMMUNICATE SMC) or by the CPU driver
42 // (e.g. during an asynchronous event). In either case, this context is
43 // maintained in an array which has an entry for each CPU. The pointer to this
44 // array is held in PerCpuGuidedEventContext. Memory is allocated once the
45 // number of CPUs in the system are made known through the
46 // MP_INFORMATION_HOB_DATA.
47 //
48 EFI_MM_COMMUNICATE_HEADER **PerCpuGuidedEventContext = NULL;
49 
50 // Descriptor with whereabouts of memory used for communication with the normal world
51 EFI_MMRAM_DESCRIPTOR  mNsCommBuffer;
52 
53 MP_INFORMATION_HOB_DATA *mMpInformationHobData;
54 
55 EFI_MM_CONFIGURATION_PROTOCOL mMmConfig = {
56   0,
57   MmFoundationEntryRegister
58 };
59 
60 STATIC EFI_MM_ENTRY_POINT     mMmEntryPoint = NULL;
61 
62 /**
63   The PI Standalone MM entry point for the TF-A CPU driver.
64 
65   @param  [in] EventId            The event Id.
66   @param  [in] CpuNumber          The CPU number.
67   @param  [in] NsCommBufferAddr   Address of the NS common buffer.
68 
69   @retval   EFI_SUCCESS             Success.
70   @retval   EFI_INVALID_PARAMETER   A parameter was invalid.
71   @retval   EFI_ACCESS_DENIED       Access not permitted.
72   @retval   EFI_OUT_OF_RESOURCES    Out of resources.
73   @retval   EFI_UNSUPPORTED         Operation not supported.
74 **/
75 EFI_STATUS
PiMmStandaloneArmTfCpuDriverEntry(IN UINTN EventId,IN UINTN CpuNumber,IN UINTN NsCommBufferAddr)76 PiMmStandaloneArmTfCpuDriverEntry (
77   IN UINTN EventId,
78   IN UINTN CpuNumber,
79   IN UINTN NsCommBufferAddr
80   )
81 {
82   EFI_MM_COMMUNICATE_HEADER   *GuidedEventContext;
83   EFI_MM_ENTRY_CONTEXT        MmEntryPointContext;
84   EFI_STATUS                  Status;
85   UINTN                       NsCommBufferSize;
86 
87   DEBUG ((DEBUG_INFO, "Received event - 0x%x on cpu %d\n", EventId, CpuNumber));
88 
89   Status = EFI_SUCCESS;
90   //
91   // ARM TF passes SMC FID of the MM_COMMUNICATE interface as the Event ID upon
92   // receipt of a synchronous MM request. Use the Event ID to distinguish
93   // between synchronous and asynchronous events.
94   //
95   if ((ARM_SMC_ID_MM_COMMUNICATE_AARCH64 != EventId) &&
96       (ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ_AARCH64 != EventId)) {
97     DEBUG ((DEBUG_INFO, "UnRecognized Event - 0x%x\n", EventId));
98     return EFI_INVALID_PARAMETER;
99   }
100 
101   // Perform parameter validation of NsCommBufferAddr
102   if (NsCommBufferAddr == (UINTN)NULL) {
103     return EFI_INVALID_PARAMETER;
104   }
105 
106   if (NsCommBufferAddr < mNsCommBuffer.PhysicalStart) {
107     return EFI_ACCESS_DENIED;
108   }
109 
110   if ((NsCommBufferAddr + sizeof (EFI_MM_COMMUNICATE_HEADER)) >=
111       (mNsCommBuffer.PhysicalStart + mNsCommBuffer.PhysicalSize)) {
112     return EFI_INVALID_PARAMETER;
113   }
114 
115   // Find out the size of the buffer passed
116   NsCommBufferSize = ((EFI_MM_COMMUNICATE_HEADER *) NsCommBufferAddr)->MessageLength +
117     sizeof (EFI_MM_COMMUNICATE_HEADER);
118 
119   // perform bounds check.
120   if (NsCommBufferAddr + NsCommBufferSize >=
121       mNsCommBuffer.PhysicalStart + mNsCommBuffer.PhysicalSize) {
122     return EFI_ACCESS_DENIED;
123   }
124 
125   GuidedEventContext = NULL;
126   // Now that the secure world can see the normal world buffer, allocate
127   // memory to copy the communication buffer to the secure world.
128   Status = mMmst->MmAllocatePool (
129                     EfiRuntimeServicesData,
130                     NsCommBufferSize,
131                     (VOID **) &GuidedEventContext
132                     );
133 
134   if (Status != EFI_SUCCESS) {
135     DEBUG ((DEBUG_INFO, "Mem alloc failed - 0x%x\n", EventId));
136     return EFI_OUT_OF_RESOURCES;
137   }
138 
139   // X1 contains the VA of the normal world memory accessible from
140   // S-EL0
141   CopyMem (GuidedEventContext, (CONST VOID *) NsCommBufferAddr, NsCommBufferSize);
142 
143   // Stash the pointer to the allocated Event Context for this CPU
144   PerCpuGuidedEventContext[CpuNumber] = GuidedEventContext;
145 
146   ZeroMem (&MmEntryPointContext, sizeof (EFI_MM_ENTRY_CONTEXT));
147 
148   MmEntryPointContext.CurrentlyExecutingCpu = CpuNumber;
149   MmEntryPointContext.NumberOfCpus = mMpInformationHobData->NumberOfProcessors;
150 
151   // Populate the MM system table with MP and state information
152   mMmst->CurrentlyExecutingCpu = CpuNumber;
153   mMmst->NumberOfCpus = mMpInformationHobData->NumberOfProcessors;
154   mMmst->CpuSaveStateSize = 0;
155   mMmst->CpuSaveState = NULL;
156 
157   if (mMmEntryPoint == NULL) {
158     DEBUG ((DEBUG_INFO, "Mm Entry point Not Found\n"));
159     return EFI_UNSUPPORTED;
160   }
161 
162   mMmEntryPoint (&MmEntryPointContext);
163 
164   // Free the memory allocation done earlier and reset the per-cpu context
165   ASSERT (GuidedEventContext);
166   CopyMem ((VOID *)NsCommBufferAddr, (CONST VOID *) GuidedEventContext, NsCommBufferSize);
167 
168   Status = mMmst->MmFreePool ((VOID *) GuidedEventContext);
169   if (Status != EFI_SUCCESS) {
170     return EFI_OUT_OF_RESOURCES;
171   }
172   PerCpuGuidedEventContext[CpuNumber] = NULL;
173 
174   return Status;
175 }
176 
177 /**
178   Registers the MM foundation entry point.
179 
180   @param  [in] This               Pointer to the MM Configuration protocol.
181   @param  [in] MmEntryPoint       Function pointer to the MM Entry point.
182 
183   @retval   EFI_SUCCESS             Success.
184 **/
185 EFI_STATUS
186 EFIAPI
MmFoundationEntryRegister(IN CONST EFI_MM_CONFIGURATION_PROTOCOL * This,IN EFI_MM_ENTRY_POINT MmEntryPoint)187 MmFoundationEntryRegister (
188   IN CONST EFI_MM_CONFIGURATION_PROTOCOL  *This,
189   IN EFI_MM_ENTRY_POINT                    MmEntryPoint
190   )
191 {
192   // store the entry point in a global
193   mMmEntryPoint = MmEntryPoint;
194   return EFI_SUCCESS;
195 }
196 
197 /**
198   This function is the main entry point for an MM handler dispatch
199   or communicate-based callback.
200 
201   @param  DispatchHandle  The unique handle assigned to this handler by
202                           MmiHandlerRegister().
203   @param  Context         Points to an optional handler context which was
204                           specified when the handler was registered.
205   @param  CommBuffer      A pointer to a collection of data in memory that will
206                           be conveyed from a non-MM environment into an
207                           MM environment.
208   @param  CommBufferSize  The size of the CommBuffer.
209 
210   @return Status Code
211 
212 **/
213 EFI_STATUS
214 EFIAPI
PiMmCpuTpFwRootMmiHandler(IN EFI_HANDLE DispatchHandle,IN CONST VOID * Context,OPTIONAL IN OUT VOID * CommBuffer,OPTIONAL IN OUT UINTN * CommBufferSize OPTIONAL)215 PiMmCpuTpFwRootMmiHandler (
216   IN     EFI_HANDLE               DispatchHandle,
217   IN     CONST VOID               *Context,        OPTIONAL
218   IN OUT VOID                     *CommBuffer,     OPTIONAL
219   IN OUT UINTN                    *CommBufferSize  OPTIONAL
220   )
221 {
222   EFI_STATUS Status;
223   UINTN      CpuNumber;
224 
225   ASSERT (Context == NULL);
226   ASSERT (CommBuffer == NULL);
227   ASSERT (CommBufferSize == NULL);
228 
229   CpuNumber = mMmst->CurrentlyExecutingCpu;
230   if (PerCpuGuidedEventContext[CpuNumber] == NULL) {
231     return EFI_NOT_FOUND;
232   }
233 
234   DEBUG ((DEBUG_INFO, "CommBuffer - 0x%x, CommBufferSize - 0x%x\n",
235           PerCpuGuidedEventContext[CpuNumber],
236           PerCpuGuidedEventContext[CpuNumber]->MessageLength));
237 
238   Status = mMmst->MmiManage (
239                     &PerCpuGuidedEventContext[CpuNumber]->HeaderGuid,
240                     NULL,
241                     PerCpuGuidedEventContext[CpuNumber]->Data,
242                     &PerCpuGuidedEventContext[CpuNumber]->MessageLength
243                     );
244 
245   if (Status != EFI_SUCCESS) {
246     DEBUG ((DEBUG_WARN, "Unable to manage Guided Event - %d\n", Status));
247   }
248 
249   return Status;
250 }
251