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