]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c
MdeModulePkg/EbcDxe AARCH64: clean up comment style in ASM file
[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
5Copyright (c) 2015, The Linux Foundation. All rights reserved.\r
6Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>\r
7This program and the accompanying materials\r
8are licensed and made available under the terms and conditions of the BSD License\r
9which accompanies this distribution. The full text of the license may be found at\r
10http://opensource.org/licenses/bsd-license.php\r
11\r
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "EbcInt.h"\r
18#include "EbcExecute.h"\r
19\r
20//\r
21// Amount of space that is not used in the stack\r
22//\r
23#define STACK_REMAIN_SIZE (1024 * 4)\r
24\r
25//\r
26// This is instruction buffer used to create EBC thunk\r
27//\r
28#define EBC_MAGIC_SIGNATURE 0xCA112EBCCA112EBCull\r
29#define EBC_ENTRYPOINT_SIGNATURE 0xAFAFAFAFAFAFAFAFull\r
30#define EBC_LL_EBC_ENTRYPOINT_SIGNATURE 0xFAFAFAFAFAFAFAFAull\r
31UINT8 mInstructionBufferTemplate[] = {\r
32 0x03, 0x00, 0x00, 0x14, //b pc+16\r
33 //\r
34 // Add a magic code here to help the VM recognize the thunk..\r
35 //\r
36 (UINT8)(EBC_MAGIC_SIGNATURE & 0xFF),\r
37 (UINT8)((EBC_MAGIC_SIGNATURE >> 8) & 0xFF),\r
38 (UINT8)((EBC_MAGIC_SIGNATURE >> 16) & 0xFF),\r
39 (UINT8)((EBC_MAGIC_SIGNATURE >> 24) & 0xFF),\r
40 (UINT8)((EBC_MAGIC_SIGNATURE >> 32) & 0xFF),\r
41 (UINT8)((EBC_MAGIC_SIGNATURE >> 40) & 0xFF),\r
42 (UINT8)((EBC_MAGIC_SIGNATURE >> 48) & 0xFF),\r
43 (UINT8)((EBC_MAGIC_SIGNATURE >> 56) & 0xFF),\r
44 0x69, 0x00, 0x00, 0x58, //ldr x9, #32\r
45 0x8A, 0x00, 0x00, 0x58, //ldr x10, #40\r
46 0x05, 0x00, 0x00, 0x14, //b pc+32\r
47 (UINT8)(EBC_ENTRYPOINT_SIGNATURE & 0xFF),\r
48 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),\r
49 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),\r
50 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),\r
51 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),\r
52 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),\r
53 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),\r
54 (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),\r
55 (UINT8)(EBC_LL_EBC_ENTRYPOINT_SIGNATURE & 0xFF),\r
56 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF),\r
57 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF),\r
58 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF),\r
59 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF),\r
60 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF),\r
61 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF),\r
62 (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF),\r
63 0x40, 0x01, 0x1F, 0xD6 //br x10\r
64\r
65};\r
66\r
67/**\r
68 Begin executing an EBC image.\r
69 This is used for Ebc Thunk call.\r
70\r
71 @return The value returned by the EBC application we're going to run.\r
72\r
73**/\r
74UINT64\r
75EFIAPI\r
76EbcLLEbcInterpret (\r
77 VOID\r
78 );\r
79\r
80/**\r
81 Begin executing an EBC image.\r
82 This is used for Ebc image entrypoint.\r
83\r
84 @return The value returned by the EBC application we're going to run.\r
85\r
86**/\r
87UINT64\r
88EFIAPI\r
89EbcLLExecuteEbcImageEntryPoint (\r
90 VOID\r
91 );\r
92\r
93/**\r
94 Pushes a 64 bit unsigned value to the VM stack.\r
95\r
96 @param VmPtr The pointer to current VM context.\r
97 @param Arg The value to be pushed.\r
98\r
99**/\r
100VOID\r
101PushU64 (\r
102 IN VM_CONTEXT *VmPtr,\r
103 IN UINT64 Arg\r
104 )\r
105{\r
106 //\r
107 // Advance the VM stack down, and then copy the argument to the stack.\r
108 // Hope it's aligned.\r
109 //\r
110 VmPtr->Gpr[0] -= sizeof (UINT64);\r
111 *(UINT64 *) VmPtr->Gpr[0] = Arg;\r
112 return;\r
113}\r
114\r
115\r
116/**\r
117 Begin executing an EBC image.\r
118\r
119 This is a thunk function.\r
120\r
121 @param EntryPoint The entrypoint of EBC code.\r
122 @param Arg1 The 1st argument.\r
123 @param Arg2 The 2nd argument.\r
124 @param Arg3 The 3rd argument.\r
125 @param Arg4 The 4th argument.\r
126 @param Arg5 The 5th argument.\r
127 @param Arg6 The 6th argument.\r
128 @param Arg7 The 7th argument.\r
129 @param Arg8 The 8th argument.\r
130 @param Arg9 The 9th argument.\r
131 @param Arg10 The 10th argument.\r
132 @param Arg11 The 11th argument.\r
133 @param Arg12 The 12th argument.\r
134 @param Arg13 The 13th argument.\r
135 @param Arg14 The 14th argument.\r
136 @param Arg15 The 15th argument.\r
137 @param Arg16 The 16th argument.\r
138\r
139 @return The value returned by the EBC application we're going to run.\r
140\r
141**/\r
142UINT64\r
143EFIAPI\r
144EbcInterpret (\r
145 IN UINTN EntryPoint,\r
146 IN UINTN Arg1,\r
147 IN UINTN Arg2,\r
148 IN UINTN Arg3,\r
149 IN UINTN Arg4,\r
150 IN UINTN Arg5,\r
151 IN UINTN Arg6,\r
152 IN UINTN Arg7,\r
153 IN UINTN Arg8,\r
154 IN UINTN Arg9,\r
155 IN UINTN Arg10,\r
156 IN UINTN Arg11,\r
157 IN UINTN Arg12,\r
158 IN UINTN Arg13,\r
159 IN UINTN Arg14,\r
160 IN UINTN Arg15,\r
161 IN UINTN Arg16\r
162 )\r
163{\r
164 //\r
165 // Create a new VM context on the stack\r
166 //\r
167 VM_CONTEXT VmContext;\r
168 UINTN Addr;\r
169 EFI_STATUS Status;\r
170 UINTN StackIndex;\r
171\r
172 //\r
173 // Get the EBC entry point\r
174 //\r
175 Addr = EntryPoint;\r
176\r
177 //\r
178 // Now clear out our context\r
179 //\r
180 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));\r
181\r
182 //\r
183 // Set the VM instruction pointer to the correct location in memory.\r
184 //\r
185 VmContext.Ip = (VMIP) Addr;\r
186\r
187 //\r
188 // Initialize the stack pointer for the EBC. Get the current system stack\r
189 // pointer and adjust it down by the max needed for the interpreter.\r
190 //\r
191\r
192 //\r
193 // Adjust the VM's stack pointer down.\r
194 //\r
195\r
196 Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);\r
197 if (EFI_ERROR(Status)) {\r
198 return Status;\r
199 }\r
200 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
201 VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);\r
202 VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];\r
203 VmContext.Gpr[0] -= sizeof (UINTN);\r
204\r
205 //\r
206 // Align the stack on a natural boundary.\r
207 //\r
208 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);\r
209\r
210 //\r
211 // Put a magic value in the stack gap, then adjust down again.\r
212 //\r
213 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;\r
214 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];\r
215\r
216 //\r
217 // The stack upper to LowStackTop is belong to the VM.\r
218 //\r
219 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];\r
220\r
221 //\r
222 // For the worst case, assume there are 4 arguments passed in registers, store\r
223 // them to VM's stack.\r
224 //\r
225 PushU64 (&VmContext, (UINT64) Arg16);\r
226 PushU64 (&VmContext, (UINT64) Arg15);\r
227 PushU64 (&VmContext, (UINT64) Arg14);\r
228 PushU64 (&VmContext, (UINT64) Arg13);\r
229 PushU64 (&VmContext, (UINT64) Arg12);\r
230 PushU64 (&VmContext, (UINT64) Arg11);\r
231 PushU64 (&VmContext, (UINT64) Arg10);\r
232 PushU64 (&VmContext, (UINT64) Arg9);\r
233 PushU64 (&VmContext, (UINT64) Arg8);\r
234 PushU64 (&VmContext, (UINT64) Arg7);\r
235 PushU64 (&VmContext, (UINT64) Arg6);\r
236 PushU64 (&VmContext, (UINT64) Arg5);\r
237 PushU64 (&VmContext, (UINT64) Arg4);\r
238 PushU64 (&VmContext, (UINT64) Arg3);\r
239 PushU64 (&VmContext, (UINT64) Arg2);\r
240 PushU64 (&VmContext, (UINT64) Arg1);\r
241\r
242 //\r
243 // Interpreter assumes 64-bit return address is pushed on the stack.\r
244 // AArch64 does not do this so pad the stack accordingly.\r
245 //\r
246 PushU64 (&VmContext, (UINT64) 0);\r
247 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);\r
248\r
249 //\r
250 // For AArch64, this is where we say our return address is\r
251 //\r
252 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];\r
253\r
254 //\r
255 // We need to keep track of where the EBC stack starts. This way, if the EBC\r
256 // accesses any stack variables above its initial stack setting, then we know\r
257 // it's accessing variables passed into it, which means the data is on the\r
258 // VM's stack.\r
259 // When we're called, on the stack (high to low) we have the parameters, the\r
260 // return address, then the saved ebp. Save the pointer to the return address.\r
261 // EBC code knows that's there, so should look above it for function parameters.\r
262 // The offset is the size of locals (VMContext + Addr + saved ebp).\r
263 // Note that the interpreter assumes there is a 16 bytes of return address on\r
264 // the stack too, so adjust accordingly.\r
265 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));\r
266 //\r
267\r
268 //\r
269 // Begin executing the EBC code\r
270 //\r
271 EbcExecute (&VmContext);\r
272\r
273 //\r
274 // Return the value in R[7] unless there was an error\r
275 //\r
276 ReturnEBCStack(StackIndex);\r
277 return (UINT64) VmContext.Gpr[7];\r
278}\r
279\r
280\r
281/**\r
282 Begin executing an EBC image.\r
283\r
284 @param EntryPoint The entrypoint of EBC code.\r
285 @param ImageHandle image handle for the EBC application we're executing\r
286 @param SystemTable standard system table passed into an driver's entry\r
287 point\r
288\r
289 @return The value returned by the EBC application we're going to run.\r
290\r
291**/\r
292UINT64\r
293EFIAPI\r
294ExecuteEbcImageEntryPoint (\r
295 IN UINTN EntryPoint,\r
296 IN EFI_HANDLE ImageHandle,\r
297 IN EFI_SYSTEM_TABLE *SystemTable\r
298 )\r
299{\r
300 //\r
301 // Create a new VM context on the stack\r
302 //\r
303 VM_CONTEXT VmContext;\r
304 UINTN Addr;\r
305 EFI_STATUS Status;\r
306 UINTN StackIndex;\r
307\r
308 //\r
309 // Get the EBC entry point\r
310 //\r
311 Addr = EntryPoint;\r
312\r
313 //\r
314 // Now clear out our context\r
315 //\r
316 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));\r
317\r
318 //\r
319 // Save the image handle so we can track the thunks created for this image\r
320 //\r
321 VmContext.ImageHandle = ImageHandle;\r
322 VmContext.SystemTable = SystemTable;\r
323\r
324 //\r
325 // Set the VM instruction pointer to the correct location in memory.\r
326 //\r
327 VmContext.Ip = (VMIP) Addr;\r
328\r
329 //\r
330 // Initialize the stack pointer for the EBC. Get the current system stack\r
331 // pointer and adjust it down by the max needed for the interpreter.\r
332 //\r
333\r
334 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);\r
335 if (EFI_ERROR(Status)) {\r
336 return Status;\r
337 }\r
338 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);\r
339 VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);\r
340 VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];\r
341 VmContext.Gpr[0] -= sizeof (UINTN);\r
342\r
343\r
344 //\r
345 // Put a magic value in the stack gap, then adjust down again\r
346 //\r
347 *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;\r
348 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];\r
349\r
350 //\r
351 // Align the stack on a natural boundary\r
352 VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1);\r
353 //\r
354 VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];\r
355\r
356 //\r
357 // Simply copy the image handle and system table onto the EBC stack.\r
358 // Greatly simplifies things by not having to spill the args.\r
359 //\r
360 PushU64 (&VmContext, (UINT64) SystemTable);\r
361 PushU64 (&VmContext, (UINT64) ImageHandle);\r
362\r
363 //\r
364 // VM pushes 16-bytes for return address. Simulate that here.\r
365 //\r
366 PushU64 (&VmContext, (UINT64) 0);\r
367 PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL);\r
368\r
369 //\r
370 // For AArch64, this is where we say our return address is\r
371 //\r
372 VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];\r
373\r
374 //\r
375 // Entry function needn't access high stack context, simply\r
376 // put the stack pointer here.\r
377 //\r
378\r
379 //\r
380 // Begin executing the EBC code\r
381 //\r
382 EbcExecute (&VmContext);\r
383\r
384 //\r
385 // Return the value in R[7] unless there was an error\r
386 //\r
387 ReturnEBCStack(StackIndex);\r
388 return (UINT64) VmContext.Gpr[7];\r
389}\r
390\r
391\r
392/**\r
393 Create thunks for an EBC image entry point, or an EBC protocol service.\r
394\r
395 @param ImageHandle Image handle for the EBC image. If not null, then\r
396 we're creating a thunk for an image entry point.\r
397 @param EbcEntryPoint Address of the EBC code that the thunk is to call\r
398 @param Thunk Returned thunk we create here\r
399 @param Flags Flags indicating options for creating the thunk\r
400\r
401 @retval EFI_SUCCESS The thunk was created successfully.\r
402 @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit\r
403 aligned.\r
404 @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC\r
405 Thunk.\r
406 @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.\r
407\r
408**/\r
409EFI_STATUS\r
410EbcCreateThunks (\r
411 IN EFI_HANDLE ImageHandle,\r
412 IN VOID *EbcEntryPoint,\r
413 OUT VOID **Thunk,\r
414 IN UINT32 Flags\r
415 )\r
416{\r
417 UINT8 *Ptr;\r
418 UINT8 *ThunkBase;\r
419 UINT32 Index;\r
420 INT32 ThunkSize;\r
421\r
422 //\r
423 // Check alignment of pointer to EBC code\r
424 //\r
425 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {\r
426 return EFI_INVALID_PARAMETER;\r
427 }\r
428\r
429 ThunkSize = sizeof(mInstructionBufferTemplate);\r
430\r
431 Ptr = AllocatePool (sizeof(mInstructionBufferTemplate));\r
432\r
433 if (Ptr == NULL) {\r
434 return EFI_OUT_OF_RESOURCES;\r
435 }\r
436 //\r
437 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);\r
438 //\r
439 // Save the start address so we can add a pointer to it to a list later.\r
440 //\r
441 ThunkBase = Ptr;\r
442\r
443 //\r
444 // Give them the address of our buffer we're going to fix up\r
445 //\r
446 *Thunk = (VOID *) Ptr;\r
447\r
448 //\r
449 // Copy whole thunk instruction buffer template\r
450 //\r
451 CopyMem (Ptr, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate));\r
452\r
453 //\r
454 // Patch EbcEntryPoint and EbcLLEbcInterpret\r
455 //\r
456 for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {\r
457 if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) {\r
458 *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint;\r
459 }\r
460 if (*(UINTN *)&Ptr[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {\r
461 if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {\r
462 *(UINTN *)&Ptr[Index] = (UINTN)EbcLLExecuteEbcImageEntryPoint;\r
463 } else {\r
464 *(UINTN *)&Ptr[Index] = (UINTN)EbcLLEbcInterpret;\r
465 }\r
466 }\r
467 }\r
468\r
469 //\r
470 // Add the thunk to the list for this image. Do this last since the add\r
471 // function flushes the cache for us.\r
472 //\r
473 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);\r
474\r
475 return EFI_SUCCESS;\r
476}\r
477\r
478\r
479/**\r
480 This function is called to execute an EBC CALLEX instruction.\r
481 The function check the callee's content to see whether it is common native\r
482 code or a thunk to another piece of EBC code.\r
483 If the callee is common native code, use EbcLLCAllEXASM to manipulate,\r
484 otherwise, set the VM->IP to target EBC code directly to avoid another VM\r
485 be startup which cost time and stack space.\r
486\r
487 @param VmPtr Pointer to a VM context.\r
488 @param FuncAddr Callee's address\r
489 @param NewStackPointer New stack pointer after the call\r
490 @param FramePtr New frame pointer after the call\r
491 @param Size The size of call instruction\r
492\r
493**/\r
494VOID\r
495EbcLLCALLEX (\r
496 IN VM_CONTEXT *VmPtr,\r
497 IN UINTN FuncAddr,\r
498 IN UINTN NewStackPointer,\r
499 IN VOID *FramePtr,\r
500 IN UINT8 Size\r
501 )\r
502{\r
503 UINTN IsThunk;\r
504 UINTN TargetEbcAddr;\r
505 UINT8 InstructionBuffer[sizeof(mInstructionBufferTemplate)];\r
506 UINTN Index;\r
507 UINTN IndexOfEbcEntrypoint;\r
508\r
509 IsThunk = 1;\r
510 TargetEbcAddr = 0;\r
511 IndexOfEbcEntrypoint = 0;\r
512\r
513 //\r
514 // Processor specific code to check whether the callee is a thunk to EBC.\r
515 //\r
516 CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof(InstructionBuffer));\r
517 //\r
518 // Fill the signature according to mInstructionBufferTemplate\r
519 //\r
520 for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) {\r
521 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) {\r
522 *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE;\r
523 IndexOfEbcEntrypoint = Index;\r
524 }\r
525 if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) {\r
526 *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE;\r
527 }\r
528 }\r
529 //\r
530 // Check if we need thunk to native\r
531 //\r
532 if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)) != 0) {\r
533 IsThunk = 0;\r
534 }\r
535\r
536 if (IsThunk == 1){\r
537 //\r
538 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and\r
539 // put our return address and frame pointer on the VM stack.\r
540 // Then set the VM's IP to new EBC code.\r
541 //\r
542 VmPtr->Gpr[0] -= 8;\r
543 VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);\r
544 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];\r
545 VmPtr->Gpr[0] -= 8;\r
546 VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));\r
547\r
548 CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof(UINTN));\r
549 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;\r
550 } else {\r
551 //\r
552 // The callee is not a thunk to EBC, call native code,\r
553 // and get return value.\r
554 //\r
555 VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);\r
556\r
557 //\r
558 // Advance the IP.\r
559 //\r
560 VmPtr->Ip += Size;\r
561 }\r
562}\r
563\r