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