]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / AArch64 / EbcSupport.c
CommitLineData
a15e5bc2
JB
1/** @file\r
2 This module contains EBC support routines that are customized based on\r
3 the target AArch64 processor.\r
4\r
4d1f5a21
AB
5Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>\r
6Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>\r
a15e5bc2 7Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
4d1f5a21 8\r
9d510e61 9SPDX-License-Identifier: BSD-2-Clause-Patent\r
a15e5bc2
JB
10\r
11**/\r
12\r
13#include "EbcInt.h"\r
14#include "EbcExecute.h"\r
6f0a3cd2 15#include "EbcDebuggerHook.h"\r
a15e5bc2
JB
16\r
17//\r
18// Amount of space that is not used in the stack\r
19//\r
1436aea4 20#define STACK_REMAIN_SIZE (1024 * 4)\r
a15e5bc2 21\r
4d1f5a21
AB
22#pragma pack(1)\r
23typedef struct {\r
24 UINT32 Instr[3];\r
25 UINT32 Magic;\r
26 UINT64 EbcEntryPoint;\r
27 UINT64 EbcLlEntryPoint;\r
28} EBC_INSTRUCTION_BUFFER;\r
29#pragma pack()\r
30\r
1436aea4 31extern CONST EBC_INSTRUCTION_BUFFER mEbcInstructionBufferTemplate;\r
a15e5bc2
JB
32\r
33/**\r
34 Begin executing an EBC image.\r
35 This is used for Ebc Thunk call.\r
36\r
37 @return The value returned by the EBC application we're going to run.\r
38\r
39**/\r
40UINT64\r
41EFIAPI\r
42EbcLLEbcInterpret (\r
43 VOID\r
44 );\r
45\r
46/**\r
47 Begin executing an EBC image.\r
48 This is used for Ebc image entrypoint.\r
49\r
50 @return The value returned by the EBC application we're going to run.\r
51\r
52**/\r
53UINT64\r
54EFIAPI\r
55EbcLLExecuteEbcImageEntryPoint (\r
56 VOID\r
57 );\r
58\r
59/**\r
60 Pushes a 64 bit unsigned value to the VM stack.\r
61\r
62 @param VmPtr The pointer to current VM context.\r
63 @param Arg The value to be pushed.\r
64\r
65**/\r
66VOID\r
67PushU64 (\r
1436aea4
MK
68 IN VM_CONTEXT *VmPtr,\r
69 IN UINT64 Arg\r
a15e5bc2
JB
70 )\r
71{\r
72 //\r
73 // Advance the VM stack down, and then copy the argument to the stack.\r
74 // Hope it's aligned.\r
75 //\r
1436aea4
MK
76 VmPtr->Gpr[0] -= sizeof (UINT64);\r
77 *(UINT64 *)VmPtr->Gpr[0] = Arg;\r
a15e5bc2
JB
78 return;\r
79}\r
80\r
a15e5bc2
JB
81/**\r
82 Begin executing an EBC image.\r
83\r
84 This is a thunk function.\r
85\r
a15e5bc2
JB
86 @param Arg1 The 1st argument.\r
87 @param Arg2 The 2nd argument.\r
88 @param Arg3 The 3rd argument.\r
89 @param Arg4 The 4th argument.\r
90 @param Arg5 The 5th argument.\r
91 @param Arg6 The 6th argument.\r
92 @param Arg7 The 7th argument.\r
93 @param Arg8 The 8th argument.\r
4a2aaff2
AB
94 @param EntryPoint The entrypoint of EBC code.\r
95 @param Args9_16[] Array containing arguments #9 to #16.\r
a15e5bc2
JB
96\r
97 @return The value returned by the EBC application we're going to run.\r
98\r
99**/\r
100UINT64\r
101EFIAPI\r
102EbcInterpret (\r
4a2aaff2
AB
103 IN UINTN Arg1,\r
104 IN UINTN Arg2,\r
105 IN UINTN Arg3,\r
106 IN UINTN Arg4,\r
107 IN UINTN Arg5,\r
108 IN UINTN Arg6,\r
109 IN UINTN Arg7,\r
110 IN UINTN Arg8,\r
111 IN UINTN EntryPoint,\r
112 IN CONST UINTN Args9_16[]\r
a15e5bc2
JB
113 )\r
114{\r
115 //\r
116 // Create a new VM context on the stack\r
117 //\r
118 VM_CONTEXT VmContext;\r
119 UINTN Addr;\r
120 EFI_STATUS Status;\r
121 UINTN StackIndex;\r
122\r
123 //\r
124 // Get the EBC entry point\r
125 //\r
126 Addr = EntryPoint;\r
127\r
128 //\r
129 // Now clear out our context\r
130 //\r
1436aea4 131 ZeroMem ((VOID *)&VmContext, sizeof (VM_CONTEXT));\r
a15e5bc2
JB
132\r
133 //\r
134 // Set the VM instruction pointer to the correct location in memory.\r
135 //\r
1436aea4 136 VmContext.Ip = (VMIP)Addr;\r
a15e5bc2
JB
137\r
138 //\r
139 // Initialize the stack pointer for the EBC. Get the current system stack\r
140 // pointer and adjust it down by the max needed for the interpreter.\r
141 //\r
142\r
143 //\r
144 // Adjust the VM's stack pointer down.\r
145 //\r
146\r
1436aea4
MK
147 Status = GetEBCStack ((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);\r
148 if (EFI_ERROR (Status)) {\r
a15e5bc2
JB
149 return Status;\r
150 }\r
1436aea4
MK
151\r
152 VmContext.StackTop = (UINT8 *)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
153 VmContext.Gpr[0] = (UINT64)((UINT8 *)VmContext.StackPool + STACK_POOL_SIZE);\r
154 VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];\r
155 VmContext.Gpr[0] -= sizeof (UINTN);\r
a15e5bc2
JB
156\r
157 //\r
158 // Align the stack on a natural boundary.\r
159 //\r
160 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);\r
161\r
162 //\r
163 // Put a magic value in the stack gap, then adjust down again.\r
164 //\r
1436aea4
MK
165 *(UINTN *)(UINTN)(VmContext.Gpr[0]) = (UINTN)VM_STACK_KEY_VALUE;\r
166 VmContext.StackMagicPtr = (UINTN *)(UINTN)VmContext.Gpr[0];\r
a15e5bc2
JB
167\r
168 //\r
169 // The stack upper to LowStackTop is belong to the VM.\r
170 //\r
1436aea4 171 VmContext.LowStackTop = (UINTN)VmContext.Gpr[0];\r
a15e5bc2
JB
172\r
173 //\r
174 // For the worst case, assume there are 4 arguments passed in registers, store\r
175 // them to VM's stack.\r
176 //\r
1436aea4
MK
177 PushU64 (&VmContext, (UINT64)Args9_16[7]);\r
178 PushU64 (&VmContext, (UINT64)Args9_16[6]);\r
179 PushU64 (&VmContext, (UINT64)Args9_16[5]);\r
180 PushU64 (&VmContext, (UINT64)Args9_16[4]);\r
181 PushU64 (&VmContext, (UINT64)Args9_16[3]);\r
182 PushU64 (&VmContext, (UINT64)Args9_16[2]);\r
183 PushU64 (&VmContext, (UINT64)Args9_16[1]);\r
184 PushU64 (&VmContext, (UINT64)Args9_16[0]);\r
185 PushU64 (&VmContext, (UINT64)Arg8);\r
186 PushU64 (&VmContext, (UINT64)Arg7);\r
187 PushU64 (&VmContext, (UINT64)Arg6);\r
188 PushU64 (&VmContext, (UINT64)Arg5);\r
189 PushU64 (&VmContext, (UINT64)Arg4);\r
190 PushU64 (&VmContext, (UINT64)Arg3);\r
191 PushU64 (&VmContext, (UINT64)Arg2);\r
192 PushU64 (&VmContext, (UINT64)Arg1);\r
a15e5bc2
JB
193\r
194 //\r
195 // Interpreter assumes 64-bit return address is pushed on the stack.\r
196 // AArch64 does not do this so pad the stack accordingly.\r
197 //\r
1436aea4
MK
198 PushU64 (&VmContext, (UINT64)0);\r
199 PushU64 (&VmContext, (UINT64)0x1234567887654321ULL);\r
a15e5bc2
JB
200\r
201 //\r
202 // For AArch64, this is where we say our return address is\r
203 //\r
1436aea4 204 VmContext.StackRetAddr = (UINT64)VmContext.Gpr[0];\r
a15e5bc2
JB
205\r
206 //\r
207 // We need to keep track of where the EBC stack starts. This way, if the EBC\r
208 // accesses any stack variables above its initial stack setting, then we know\r
209 // it's accessing variables passed into it, which means the data is on the\r
210 // VM's stack.\r
211 // When we're called, on the stack (high to low) we have the parameters, the\r
212 // return address, then the saved ebp. Save the pointer to the return address.\r
213 // EBC code knows that's there, so should look above it for function parameters.\r
214 // The offset is the size of locals (VMContext + Addr + saved ebp).\r
215 // Note that the interpreter assumes there is a 16 bytes of return address on\r
216 // the stack too, so adjust accordingly.\r
217 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));\r
218 //\r
219\r
220 //\r
221 // Begin executing the EBC code\r
222 //\r
6f0a3cd2 223 EbcDebuggerHookEbcInterpret (&VmContext);\r
a15e5bc2
JB
224 EbcExecute (&VmContext);\r
225\r
226 //\r
227 // Return the value in R[7] unless there was an error\r
228 //\r
1436aea4
MK
229 ReturnEBCStack (StackIndex);\r
230 return (UINT64)VmContext.Gpr[7];\r
a15e5bc2
JB
231}\r
232\r
a15e5bc2
JB
233/**\r
234 Begin executing an EBC image.\r
235\r
a15e5bc2
JB
236 @param ImageHandle image handle for the EBC application we're executing\r
237 @param SystemTable standard system table passed into an driver's entry\r
238 point\r
4a2aaff2 239 @param EntryPoint The entrypoint of EBC code.\r
a15e5bc2
JB
240\r
241 @return The value returned by the EBC application we're going to run.\r
242\r
243**/\r
244UINT64\r
245EFIAPI\r
246ExecuteEbcImageEntryPoint (\r
1436aea4
MK
247 IN EFI_HANDLE ImageHandle,\r
248 IN EFI_SYSTEM_TABLE *SystemTable,\r
249 IN UINTN EntryPoint\r
a15e5bc2
JB
250 )\r
251{\r
252 //\r
253 // Create a new VM context on the stack\r
254 //\r
255 VM_CONTEXT VmContext;\r
256 UINTN Addr;\r
257 EFI_STATUS Status;\r
258 UINTN StackIndex;\r
259\r
260 //\r
261 // Get the EBC entry point\r
262 //\r
263 Addr = EntryPoint;\r
264\r
265 //\r
266 // Now clear out our context\r
267 //\r
1436aea4 268 ZeroMem ((VOID *)&VmContext, sizeof (VM_CONTEXT));\r
a15e5bc2
JB
269\r
270 //\r
271 // Save the image handle so we can track the thunks created for this image\r
272 //\r
273 VmContext.ImageHandle = ImageHandle;\r
274 VmContext.SystemTable = SystemTable;\r
275\r
276 //\r
277 // Set the VM instruction pointer to the correct location in memory.\r
278 //\r
1436aea4 279 VmContext.Ip = (VMIP)Addr;\r
a15e5bc2
JB
280\r
281 //\r
282 // Initialize the stack pointer for the EBC. Get the current system stack\r
283 // pointer and adjust it down by the max needed for the interpreter.\r
284 //\r
285\r
1436aea4
MK
286 Status = GetEBCStack (ImageHandle, &VmContext.StackPool, &StackIndex);\r
287 if (EFI_ERROR (Status)) {\r
a15e5bc2
JB
288 return Status;\r
289 }\r
a15e5bc2 290\r
1436aea4
MK
291 VmContext.StackTop = (UINT8 *)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
292 VmContext.Gpr[0] = (UINT64)((UINT8 *)VmContext.StackPool + STACK_POOL_SIZE);\r
293 VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];\r
294 VmContext.Gpr[0] -= sizeof (UINTN);\r
a15e5bc2
JB
295\r
296 //\r
297 // Put a magic value in the stack gap, then adjust down again\r
298 //\r
1436aea4
MK
299 *(UINTN *)(UINTN)(VmContext.Gpr[0]) = (UINTN)VM_STACK_KEY_VALUE;\r
300 VmContext.StackMagicPtr = (UINTN *)(UINTN)VmContext.Gpr[0];\r
a15e5bc2
JB
301\r
302 //\r
303 // Align the stack on a natural boundary\r
1436aea4 304 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);\r
a15e5bc2 305 //\r
1436aea4 306 VmContext.LowStackTop = (UINTN)VmContext.Gpr[0];\r
a15e5bc2
JB
307\r
308 //\r
309 // Simply copy the image handle and system table onto the EBC stack.\r
310 // Greatly simplifies things by not having to spill the args.\r
311 //\r
1436aea4
MK
312 PushU64 (&VmContext, (UINT64)SystemTable);\r
313 PushU64 (&VmContext, (UINT64)ImageHandle);\r
a15e5bc2
JB
314\r
315 //\r
316 // VM pushes 16-bytes for return address. Simulate that here.\r
317 //\r
1436aea4
MK
318 PushU64 (&VmContext, (UINT64)0);\r
319 PushU64 (&VmContext, (UINT64)0x1234567887654321ULL);\r
a15e5bc2
JB
320\r
321 //\r
322 // For AArch64, this is where we say our return address is\r
323 //\r
1436aea4 324 VmContext.StackRetAddr = (UINT64)VmContext.Gpr[0];\r
a15e5bc2
JB
325\r
326 //\r
327 // Entry function needn't access high stack context, simply\r
328 // put the stack pointer here.\r
329 //\r
330\r
331 //\r
332 // Begin executing the EBC code\r
333 //\r
6f0a3cd2 334 EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);\r
a15e5bc2
JB
335 EbcExecute (&VmContext);\r
336\r
337 //\r
338 // Return the value in R[7] unless there was an error\r
339 //\r
1436aea4
MK
340 ReturnEBCStack (StackIndex);\r
341 return (UINT64)VmContext.Gpr[7];\r
a15e5bc2
JB
342}\r
343\r
a15e5bc2
JB
344/**\r
345 Create thunks for an EBC image entry point, or an EBC protocol service.\r
346\r
347 @param ImageHandle Image handle for the EBC image. If not null, then\r
348 we're creating a thunk for an image entry point.\r
349 @param EbcEntryPoint Address of the EBC code that the thunk is to call\r
350 @param Thunk Returned thunk we create here\r
351 @param Flags Flags indicating options for creating the thunk\r
352\r
353 @retval EFI_SUCCESS The thunk was created successfully.\r
354 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit\r
355 aligned.\r
356 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC\r
357 Thunk.\r
358 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.\r
359\r
360**/\r
361EFI_STATUS\r
362EbcCreateThunks (\r
1436aea4
MK
363 IN EFI_HANDLE ImageHandle,\r
364 IN VOID *EbcEntryPoint,\r
365 OUT VOID **Thunk,\r
366 IN UINT32 Flags\r
a15e5bc2
JB
367 )\r
368{\r
1436aea4 369 EBC_INSTRUCTION_BUFFER *InstructionBuffer;\r
a15e5bc2
JB
370\r
371 //\r
372 // Check alignment of pointer to EBC code\r
373 //\r
1436aea4 374 if ((UINT32)(UINTN)EbcEntryPoint & 0x01) {\r
a15e5bc2
JB
375 return EFI_INVALID_PARAMETER;\r
376 }\r
377\r
16dc5b68 378 InstructionBuffer = EbcAllocatePoolForThunk (sizeof (EBC_INSTRUCTION_BUFFER));\r
4d1f5a21 379 if (InstructionBuffer == NULL) {\r
a15e5bc2
JB
380 return EFI_OUT_OF_RESOURCES;\r
381 }\r
a15e5bc2
JB
382\r
383 //\r
384 // Give them the address of our buffer we're going to fix up\r
385 //\r
4d1f5a21 386 *Thunk = InstructionBuffer;\r
a15e5bc2
JB
387\r
388 //\r
389 // Copy whole thunk instruction buffer template\r
390 //\r
1436aea4
MK
391 CopyMem (\r
392 InstructionBuffer,\r
393 &mEbcInstructionBufferTemplate,\r
394 sizeof (EBC_INSTRUCTION_BUFFER)\r
395 );\r
a15e5bc2
JB
396\r
397 //\r
398 // Patch EbcEntryPoint and EbcLLEbcInterpret\r
399 //\r
4d1f5a21
AB
400 InstructionBuffer->EbcEntryPoint = (UINT64)EbcEntryPoint;\r
401 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {\r
402 InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLExecuteEbcImageEntryPoint;\r
403 } else {\r
404 InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLEbcInterpret;\r
a15e5bc2
JB
405 }\r
406\r
407 //\r
408 // Add the thunk to the list for this image. Do this last since the add\r
409 // function flushes the cache for us.\r
410 //\r
1436aea4
MK
411 EbcAddImageThunk (\r
412 ImageHandle,\r
413 InstructionBuffer,\r
414 sizeof (EBC_INSTRUCTION_BUFFER)\r
415 );\r
a15e5bc2
JB
416\r
417 return EFI_SUCCESS;\r
418}\r
419\r
a15e5bc2
JB
420/**\r
421 This function is called to execute an EBC CALLEX instruction.\r
422 The function check the callee's content to see whether it is common native\r
423 code or a thunk to another piece of EBC code.\r
424 If the callee is common native code, use EbcLLCAllEXASM to manipulate,\r
425 otherwise, set the VM->IP to target EBC code directly to avoid another VM\r
426 be startup which cost time and stack space.\r
427\r
428 @param VmPtr Pointer to a VM context.\r
429 @param FuncAddr Callee's address\r
430 @param NewStackPointer New stack pointer after the call\r
431 @param FramePtr New frame pointer after the call\r
432 @param Size The size of call instruction\r
433\r
434**/\r
435VOID\r
436EbcLLCALLEX (\r
1436aea4
MK
437 IN VM_CONTEXT *VmPtr,\r
438 IN UINTN FuncAddr,\r
439 IN UINTN NewStackPointer,\r
440 IN VOID *FramePtr,\r
441 IN UINT8 Size\r
a15e5bc2
JB
442 )\r
443{\r
1436aea4 444 CONST EBC_INSTRUCTION_BUFFER *InstructionBuffer;\r
a15e5bc2
JB
445\r
446 //\r
447 // Processor specific code to check whether the callee is a thunk to EBC.\r
448 //\r
4d1f5a21 449 InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr;\r
a15e5bc2 450\r
1436aea4
MK
451 if (CompareMem (\r
452 InstructionBuffer,\r
453 &mEbcInstructionBufferTemplate,\r
454 sizeof (EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT64)\r
455 ) == 0)\r
456 {\r
a15e5bc2
JB
457 //\r
458 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and\r
459 // put our return address and frame pointer on the VM stack.\r
460 // Then set the VM's IP to new EBC code.\r
461 //\r
462 VmPtr->Gpr[0] -= 8;\r
1436aea4
MK
463 VmWriteMemN (VmPtr, (UINTN)VmPtr->Gpr[0], (UINTN)FramePtr);\r
464 VmPtr->FramePtr = (VOID *)(UINTN)VmPtr->Gpr[0];\r
465 VmPtr->Gpr[0] -= 8;\r
466 VmWriteMem64 (VmPtr, (UINTN)VmPtr->Gpr[0], (UINT64)(UINTN)(VmPtr->Ip + Size));\r
a15e5bc2 467\r
1436aea4 468 VmPtr->Ip = (VMIP)InstructionBuffer->EbcEntryPoint;\r
a15e5bc2
JB
469 } else {\r
470 //\r
471 // The callee is not a thunk to EBC, call native code,\r
472 // and get return value.\r
473 //\r
474 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);\r
475\r
476 //\r
477 // Advance the IP.\r
478 //\r
479 VmPtr->Ip += Size;\r
480 }\r
481}\r