]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 /**
83 Begin executing an EBC image.
84
85 This is a thunk function.
86
87 @param Arg1 The 1st argument.
88 @param Arg2 The 2nd argument.
89 @param Arg3 The 3rd argument.
90 @param Arg4 The 4th argument.
91 @param Arg5 The 5th argument.
92 @param Arg6 The 6th argument.
93 @param Arg7 The 7th argument.
94 @param Arg8 The 8th argument.
95 @param EntryPoint The entrypoint of EBC code.
96 @param Args9_16[] Array containing arguments #9 to #16.
97
98 @return The value returned by the EBC application we're going to run.
99
100 **/
101 UINT64
102 EFIAPI
103 EbcInterpret (
104 IN UINTN Arg1,
105 IN UINTN Arg2,
106 IN UINTN Arg3,
107 IN UINTN Arg4,
108 IN UINTN Arg5,
109 IN UINTN Arg6,
110 IN UINTN Arg7,
111 IN UINTN Arg8,
112 IN UINTN EntryPoint,
113 IN CONST UINTN Args9_16[]
114 )
115 {
116 //
117 // Create a new VM context on the stack
118 //
119 VM_CONTEXT VmContext;
120 UINTN Addr;
121 EFI_STATUS Status;
122 UINTN StackIndex;
123
124 //
125 // Get the EBC entry point
126 //
127 Addr = EntryPoint;
128
129 //
130 // Now clear out our context
131 //
132 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
133
134 //
135 // Set the VM instruction pointer to the correct location in memory.
136 //
137 VmContext.Ip = (VMIP) Addr;
138
139 //
140 // Initialize the stack pointer for the EBC. Get the current system stack
141 // pointer and adjust it down by the max needed for the interpreter.
142 //
143
144 //
145 // Adjust the VM's stack pointer down.
146 //
147
148 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
149 if (EFI_ERROR(Status)) {
150 return Status;
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 /**
235 Begin executing an EBC image.
236
237 @param ImageHandle image handle for the EBC application we're executing
238 @param SystemTable standard system table passed into an driver's entry
239 point
240 @param EntryPoint The entrypoint of EBC code.
241
242 @return The value returned by the EBC application we're going to run.
243
244 **/
245 UINT64
246 EFIAPI
247 ExecuteEbcImageEntryPoint (
248 IN EFI_HANDLE ImageHandle,
249 IN EFI_SYSTEM_TABLE *SystemTable,
250 IN UINTN EntryPoint
251 )
252 {
253 //
254 // Create a new VM context on the stack
255 //
256 VM_CONTEXT VmContext;
257 UINTN Addr;
258 EFI_STATUS Status;
259 UINTN StackIndex;
260
261 //
262 // Get the EBC entry point
263 //
264 Addr = EntryPoint;
265
266 //
267 // Now clear out our context
268 //
269 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
270
271 //
272 // Save the image handle so we can track the thunks created for this image
273 //
274 VmContext.ImageHandle = ImageHandle;
275 VmContext.SystemTable = SystemTable;
276
277 //
278 // Set the VM instruction pointer to the correct location in memory.
279 //
280 VmContext.Ip = (VMIP) Addr;
281
282 //
283 // Initialize the stack pointer for the EBC. Get the current system stack
284 // pointer and adjust it down by the max needed for the interpreter.
285 //
286
287 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
288 if (EFI_ERROR(Status)) {
289 return Status;
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 //
298 // Put a magic value in the stack gap, then adjust down again
299 //
300 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
301 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
302
303 //
304 // Align the stack on a natural boundary
305 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1);
306 //
307 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
308
309 //
310 // Simply copy the image handle and system table onto the EBC stack.
311 // Greatly simplifies things by not having to spill the args.
312 //
313 PushU64 (&VmContext, (UINT64) SystemTable);
314 PushU64 (&VmContext, (UINT64) ImageHandle);
315
316 //
317 // VM pushes 16-bytes for return address. Simulate that here.
318 //
319 PushU64 (&VmContext, (UINT64) 0);
320 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);
321
322 //
323 // For AArch64, this is where we say our return address is
324 //
325 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
326
327 //
328 // Entry function needn't access high stack context, simply
329 // put the stack pointer here.
330 //
331
332 //
333 // Begin executing the EBC code
334 //
335 EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
336 EbcExecute (&VmContext);
337
338 //
339 // Return the value in R[7] unless there was an error
340 //
341 ReturnEBCStack(StackIndex);
342 return (UINT64) VmContext.Gpr[7];
343 }
344
345
346 /**
347 Create thunks for an EBC image entry point, or an EBC protocol service.
348
349 @param ImageHandle Image handle for the EBC image. If not null, then
350 we're creating a thunk for an image entry point.
351 @param EbcEntryPoint Address of the EBC code that the thunk is to call
352 @param Thunk Returned thunk we create here
353 @param Flags Flags indicating options for creating the thunk
354
355 @retval EFI_SUCCESS The thunk was created successfully.
356 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
357 aligned.
358 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
359 Thunk.
360 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
361
362 **/
363 EFI_STATUS
364 EbcCreateThunks (
365 IN EFI_HANDLE ImageHandle,
366 IN VOID *EbcEntryPoint,
367 OUT VOID **Thunk,
368 IN UINT32 Flags
369 )
370 {
371 EBC_INSTRUCTION_BUFFER *InstructionBuffer;
372
373 //
374 // Check alignment of pointer to EBC code
375 //
376 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
377 return EFI_INVALID_PARAMETER;
378 }
379
380 InstructionBuffer = EbcAllocatePoolForThunk (sizeof (EBC_INSTRUCTION_BUFFER));
381 if (InstructionBuffer == NULL) {
382 return EFI_OUT_OF_RESOURCES;
383 }
384
385 //
386 // Give them the address of our buffer we're going to fix up
387 //
388 *Thunk = InstructionBuffer;
389
390 //
391 // Copy whole thunk instruction buffer template
392 //
393 CopyMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
394 sizeof (EBC_INSTRUCTION_BUFFER));
395
396 //
397 // Patch EbcEntryPoint and EbcLLEbcInterpret
398 //
399 InstructionBuffer->EbcEntryPoint = (UINT64)EbcEntryPoint;
400 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
401 InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLExecuteEbcImageEntryPoint;
402 } else {
403 InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLEbcInterpret;
404 }
405
406 //
407 // Add the thunk to the list for this image. Do this last since the add
408 // function flushes the cache for us.
409 //
410 EbcAddImageThunk (ImageHandle, InstructionBuffer,
411 sizeof (EBC_INSTRUCTION_BUFFER));
412
413 return EFI_SUCCESS;
414 }
415
416
417 /**
418 This function is called to execute an EBC CALLEX instruction.
419 The function check the callee's content to see whether it is common native
420 code or a thunk to another piece of EBC code.
421 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
422 otherwise, set the VM->IP to target EBC code directly to avoid another VM
423 be startup which cost time and stack space.
424
425 @param VmPtr Pointer to a VM context.
426 @param FuncAddr Callee's address
427 @param NewStackPointer New stack pointer after the call
428 @param FramePtr New frame pointer after the call
429 @param Size The size of call instruction
430
431 **/
432 VOID
433 EbcLLCALLEX (
434 IN VM_CONTEXT *VmPtr,
435 IN UINTN FuncAddr,
436 IN UINTN NewStackPointer,
437 IN VOID *FramePtr,
438 IN UINT8 Size
439 )
440 {
441 CONST EBC_INSTRUCTION_BUFFER *InstructionBuffer;
442
443 //
444 // Processor specific code to check whether the callee is a thunk to EBC.
445 //
446 InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr;
447
448 if (CompareMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
449 sizeof(EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT64)) == 0) {
450 //
451 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
452 // put our return address and frame pointer on the VM stack.
453 // Then set the VM's IP to new EBC code.
454 //
455 VmPtr->Gpr[0] -= 8;
456 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
457 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
458 VmPtr->Gpr[0] -= 8;
459 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
460
461 VmPtr->Ip = (VMIP) InstructionBuffer->EbcEntryPoint;
462 } else {
463 //
464 // The callee is not a thunk to EBC, call native code,
465 // and get return value.
466 //
467 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
468
469 //
470 // Advance the IP.
471 //
472 VmPtr->Ip += Size;
473 }
474 }
475