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