]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Ebc/Dxe/Ia32/EbcSupport.c
Add EBC, FTW, Crc32SectionExtract, NullMemoryTest modules.
[mirror_edk2.git] / MdeModulePkg / Universal / Ebc / Dxe / Ia32 / EbcSupport.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 EbcSupport.c
15
16 Abstract:
17
18 This module contains EBC support routines that are customized based on
19 the target processor.
20
21 --*/
22
23 #include "EbcInt.h"
24 #include "EbcExecute.h"
25
26 //
27 // NOTE: This is the stack size allocated for the interpreter
28 // when it executes an EBC image. The requirements can change
29 // based on whether or not a debugger is present, and other
30 // platform-specific configurations.
31 //
32 #define VM_STACK_SIZE (1024 * 4)
33 #define EBC_THUNK_SIZE 32
34
35 #define STACK_REMAIN_SIZE (1024 * 4)
36 VOID
37 EbcLLCALLEX (
38 IN VM_CONTEXT *VmPtr,
39 IN UINTN FuncAddr,
40 IN UINTN NewStackPointer,
41 IN VOID *FramePtr,
42 IN UINT8 Size
43 )
44 /*++
45
46 Routine Description:
47
48 This function is called to execute an EBC CALLEX instruction.
49 The function check the callee's content to see whether it is common native
50 code or a thunk to another piece of EBC code.
51 If the callee is common native code, use EbcLLCAllEXASM to manipulate,
52 otherwise, set the VM->IP to target EBC code directly to avoid another VM
53 be startup which cost time and stack space.
54
55 Arguments:
56
57 VmPtr - Pointer to a VM context.
58 FuncAddr - Callee's address
59 NewStackPointer - New stack pointer after the call
60 FramePtr - New frame pointer after the call
61 Size - The size of call instruction
62
63 Returns:
64
65 None.
66
67 --*/
68 {
69 UINTN IsThunk;
70 UINTN TargetEbcAddr;
71
72 IsThunk = 1;
73 TargetEbcAddr = 0;
74
75 //
76 // Processor specific code to check whether the callee is a thunk to EBC.
77 //
78 if (*((UINT8 *)FuncAddr) != 0xB8) {
79 IsThunk = 0;
80 goto Action;
81 }
82 if (*((UINT8 *)FuncAddr + 1) != 0xBC) {
83 IsThunk = 0;
84 goto Action;
85 }
86 if (*((UINT8 *)FuncAddr + 2) != 0x2E) {
87 IsThunk = 0;
88 goto Action;
89 }
90 if (*((UINT8 *)FuncAddr + 3) != 0x11) {
91 IsThunk = 0;
92 goto Action;
93 }
94 if (*((UINT8 *)FuncAddr + 4) != 0xCA) {
95 IsThunk = 0;
96 goto Action;
97 }
98 if (*((UINT8 *)FuncAddr + 5) != 0xB8) {
99 IsThunk = 0;
100 goto Action;
101 }
102 if (*((UINT8 *)FuncAddr + 10) != 0xB9) {
103 IsThunk = 0;
104 goto Action;
105 }
106 if (*((UINT8 *)FuncAddr + 15) != 0xFF) {
107 IsThunk = 0;
108 goto Action;
109 }
110 if (*((UINT8 *)FuncAddr + 16) != 0xE1) {
111 IsThunk = 0;
112 goto Action;
113 }
114
115 TargetEbcAddr = ((UINTN)(*((UINT8 *)FuncAddr + 9)) << 24) + ((UINTN)(*((UINT8 *)FuncAddr + 8)) << 16) +
116 ((UINTN)(*((UINT8 *)FuncAddr + 7)) << 8) + ((UINTN)(*((UINT8 *)FuncAddr + 6)));
117
118 Action:
119 if (IsThunk == 1){
120 //
121 // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
122 // put our return address and frame pointer on the VM stack.
123 // Then set the VM's IP to new EBC code.
124 //
125 VmPtr->R[0] -= 8;
126 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
127 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
128 VmPtr->R[0] -= 8;
129 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
130
131 VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
132 } else {
133 //
134 // The callee is not a thunk to EBC, call native code.
135 //
136 EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
137
138 //
139 // Get return value and advance the IP.
140 //
141 VmPtr->R[7] = EbcLLGetReturnValue ();
142 VmPtr->Ip += Size;
143 }
144 }
145
146 STATIC
147 UINT64
148 EbcInterpret (
149 IN OUT UINTN Arg1,
150 IN OUT UINTN Arg2,
151 IN OUT UINTN Arg3,
152 IN OUT UINTN Arg4,
153 IN OUT UINTN Arg5,
154 IN OUT UINTN Arg6,
155 IN OUT UINTN Arg7,
156 IN OUT UINTN Arg8,
157 IN OUT UINTN Arg9,
158 IN OUT UINTN Arg10,
159 IN OUT UINTN Arg11,
160 IN OUT UINTN Arg12,
161 IN OUT UINTN Arg13,
162 IN OUT UINTN Arg14,
163 IN OUT UINTN Arg15,
164 IN OUT UINTN Arg16
165 )
166 /*++
167
168 Routine Description:
169
170 Begin executing an EBC image. The address of the entry point is passed
171 in via a processor register, so we'll need to make a call to get the
172 value.
173
174 Arguments:
175
176 None. Since we're called from a fixed up thunk (which we want to keep
177 small), our only so-called argument is the EBC entry point passed in
178 to us in a processor register.
179
180 Returns:
181
182 The value returned by the EBC application we're going to run.
183
184 --*/
185 {
186 //
187 // Create a new VM context on the stack
188 //
189 VM_CONTEXT VmContext;
190 UINTN Addr;
191 EFI_STATUS Status;
192 UINTN StackIndex;
193
194 //
195 // Get the EBC entry point from the processor register.
196 //
197 Addr = EbcLLGetEbcEntryPoint ();
198
199 //
200 // Now clear out our context
201 //
202 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
203
204 //
205 // Set the VM instruction pointer to the correct location in memory.
206 //
207 VmContext.Ip = (VMIP) Addr;
208 //
209 // Initialize the stack pointer for the EBC. Get the current system stack
210 // pointer and adjust it down by the max needed for the interpreter.
211 //
212
213 //
214 // Align the stack on a natural boundary
215 //
216
217 //
218 // Allocate stack pool
219 //
220 Status = GetEBCStack((EFI_HANDLE)-1, &VmContext.StackPool, &StackIndex);
221 if (EFI_ERROR(Status)) {
222 return Status;
223 }
224 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
225 VmContext.R[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
226 VmContext.HighStackBottom = (UINTN)VmContext.R[0];
227 VmContext.R[0] &= ~(sizeof (UINTN) - 1);
228 VmContext.R[0] -= sizeof (UINTN);
229
230 //
231 // Put a magic value in the stack gap, then adjust down again
232 //
233 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
234 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
235 VmContext.LowStackTop = (UINTN) VmContext.R[0];
236
237 //
238 // For IA32, this is where we say our return address is
239 //
240 VmContext.R[0] -= sizeof (UINTN);
241 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg16;
242 VmContext.R[0] -= sizeof (UINTN);
243 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg15;
244 VmContext.R[0] -= sizeof (UINTN);
245 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg14;
246 VmContext.R[0] -= sizeof (UINTN);
247 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg13;
248 VmContext.R[0] -= sizeof (UINTN);
249 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg12;
250 VmContext.R[0] -= sizeof (UINTN);
251 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg11;
252 VmContext.R[0] -= sizeof (UINTN);
253 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg10;
254 VmContext.R[0] -= sizeof (UINTN);
255 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg9;
256 VmContext.R[0] -= sizeof (UINTN);
257 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg8;
258 VmContext.R[0] -= sizeof (UINTN);
259 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg7;
260 VmContext.R[0] -= sizeof (UINTN);
261 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg6;
262 VmContext.R[0] -= sizeof (UINTN);
263 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg5;
264 VmContext.R[0] -= sizeof (UINTN);
265 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg4;
266 VmContext.R[0] -= sizeof (UINTN);
267 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg3;
268 VmContext.R[0] -= sizeof (UINTN);
269 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg2;
270 VmContext.R[0] -= sizeof (UINTN);
271 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) Arg1;
272 VmContext.R[0] -= 16;
273 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
274
275 //
276 // We need to keep track of where the EBC stack starts. This way, if the EBC
277 // accesses any stack variables above its initial stack setting, then we know
278 // it's accessing variables passed into it, which means the data is on the
279 // VM's stack.
280 // When we're called, on the stack (high to low) we have the parameters, the
281 // return address, then the saved ebp. Save the pointer to the return address.
282 // EBC code knows that's there, so should look above it for function parameters.
283 // The offset is the size of locals (VMContext + Addr + saved ebp).
284 // Note that the interpreter assumes there is a 16 bytes of return address on
285 // the stack too, so adjust accordingly.
286 // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
287 //
288
289 //
290 // Begin executing the EBC code
291 //
292 EbcExecute (&VmContext);
293
294 //
295 // Return the value in R[7] unless there was an error
296 //
297 ReturnEBCStack(StackIndex);
298 return (UINT64) VmContext.R[7];
299 }
300
301 STATIC
302 UINT64
303 ExecuteEbcImageEntryPoint (
304 IN EFI_HANDLE ImageHandle,
305 IN EFI_SYSTEM_TABLE *SystemTable
306 )
307 /*++
308
309 Routine Description:
310
311 Begin executing an EBC image. The address of the entry point is passed
312 in via a processor register, so we'll need to make a call to get the
313 value.
314
315 Arguments:
316
317 ImageHandle - image handle for the EBC application we're executing
318 SystemTable - standard system table passed into an driver's entry point
319
320 Returns:
321
322 The value returned by the EBC application we're going to run.
323
324 --*/
325 {
326 //
327 // Create a new VM context on the stack
328 //
329 VM_CONTEXT VmContext;
330 UINTN Addr;
331 EFI_STATUS Status;
332 UINTN StackIndex;
333
334 //
335 // Get the EBC entry point from the processor register. Make sure you don't
336 // call any functions before this or you could mess up the register the
337 // entry point is passed in.
338 //
339 Addr = EbcLLGetEbcEntryPoint ();
340
341 //
342 // Print(L"*** Thunked into EBC entry point - ImageHandle = 0x%X\n", (UINTN)ImageHandle);
343 // Print(L"EBC entry point is 0x%X\n", (UINT32)(UINTN)Addr);
344 //
345 // Now clear out our context
346 //
347 ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
348
349 //
350 // Save the image handle so we can track the thunks created for this image
351 //
352 VmContext.ImageHandle = ImageHandle;
353 VmContext.SystemTable = SystemTable;
354
355 //
356 // Set the VM instruction pointer to the correct location in memory.
357 //
358 VmContext.Ip = (VMIP) Addr;
359
360 //
361 // Initialize the stack pointer for the EBC. Get the current system stack
362 // pointer and adjust it down by the max needed for the interpreter.
363 //
364
365 //
366 // Allocate stack pool
367 //
368 Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
369 if (EFI_ERROR(Status)) {
370 return Status;
371 }
372 VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
373 VmContext.R[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
374 VmContext.HighStackBottom = (UINTN)VmContext.R[0];
375 VmContext.R[0] -= sizeof (UINTN);
376
377 //
378 // Put a magic value in the stack gap, then adjust down again
379 //
380 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
381 VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
382
383 //
384 // Align the stack on a natural boundary
385 // VmContext.R[0] &= ~(sizeof(UINTN) - 1);
386 //
387 VmContext.LowStackTop = (UINTN) VmContext.R[0];
388 VmContext.R[0] -= sizeof (UINTN);
389 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) SystemTable;
390 VmContext.R[0] -= sizeof (UINTN);
391 *(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) ImageHandle;
392
393 VmContext.R[0] -= 16;
394 VmContext.StackRetAddr = (UINT64) VmContext.R[0];
395 //
396 // VM pushes 16-bytes for return address. Simulate that here.
397 //
398
399 //
400 // Begin executing the EBC code
401 //
402 EbcExecute (&VmContext);
403
404 //
405 // Return the value in R[7] unless there was an error
406 //
407 return (UINT64) VmContext.R[7];
408 }
409
410 EFI_STATUS
411 EbcCreateThunks (
412 IN EFI_HANDLE ImageHandle,
413 IN VOID *EbcEntryPoint,
414 OUT VOID **Thunk,
415 IN UINT32 Flags
416 )
417 /*++
418
419 Routine Description:
420
421 Create an IA32 thunk for the given EBC entry point.
422
423 Arguments:
424
425 ImageHandle - Handle of image for which this thunk is being created
426 EbcEntryPoint - Address of the EBC code that the thunk is to call
427 Thunk - Returned thunk we create here
428
429 Returns:
430
431 Standard EFI status.
432
433 --*/
434 {
435 UINT8 *Ptr;
436 UINT8 *ThunkBase;
437 UINT32 I;
438 UINT32 Addr;
439 INT32 Size;
440 INT32 ThunkSize;
441
442 //
443 // Check alignment of pointer to EBC code
444 //
445 if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
446 return EFI_INVALID_PARAMETER;
447 }
448
449 Size = EBC_THUNK_SIZE;
450 ThunkSize = Size;
451
452 Ptr = AllocatePool (Size);
453
454 if (Ptr == NULL) {
455 return EFI_OUT_OF_RESOURCES;
456 }
457 //
458 // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
459 //
460 // Save the start address so we can add a pointer to it to a list later.
461 //
462 ThunkBase = Ptr;
463
464 //
465 // Give them the address of our buffer we're going to fix up
466 //
467 *Thunk = (VOID *) Ptr;
468
469 //
470 // Add a magic code here to help the VM recognize the thunk..
471 // mov eax, 0xca112ebc => B8 BC 2E 11 CA
472 //
473 *Ptr = 0xB8;
474 Ptr++;
475 Size--;
476 Addr = (UINT32) 0xCA112EBC;
477 for (I = 0; I < sizeof (Addr); I++) {
478 *Ptr = (UINT8) (UINTN) Addr;
479 Addr >>= 8;
480 Ptr++;
481 Size--;
482 }
483
484 //
485 // Add code bytes to load up a processor register with the EBC entry point.
486 // mov eax, 0xaa55aa55 => B8 55 AA 55 AA
487 // The first 8 bytes of the thunk entry is the address of the EBC
488 // entry point.
489 //
490 *Ptr = 0xB8;
491 Ptr++;
492 Size--;
493 Addr = (UINT32) EbcEntryPoint;
494 for (I = 0; I < sizeof (Addr); I++) {
495 *Ptr = (UINT8) (UINTN) Addr;
496 Addr >>= 8;
497 Ptr++;
498 Size--;
499 }
500 //
501 // Stick in a load of ecx with the address of appropriate VM function.
502 // mov ecx 12345678h => 0xB9 0x78 0x56 0x34 0x12
503 //
504 if (Flags & FLAG_THUNK_ENTRY_POINT) {
505 Addr = (UINT32) (UINTN) ExecuteEbcImageEntryPoint;
506 } else {
507 Addr = (UINT32) (UINTN) EbcInterpret;
508 }
509
510 //
511 // MOV ecx
512 //
513 *Ptr = 0xB9;
514 Ptr++;
515 Size--;
516 for (I = 0; I < sizeof (Addr); I++) {
517 *Ptr = (UINT8) Addr;
518 Addr >>= 8;
519 Ptr++;
520 Size--;
521 }
522 //
523 // Stick in jump opcode bytes for jmp ecx => 0xFF 0xE1
524 //
525 *Ptr = 0xFF;
526 Ptr++;
527 Size--;
528 *Ptr = 0xE1;
529 Size--;
530
531 //
532 // Double check that our defined size is ok (application error)
533 //
534 if (Size < 0) {
535 ASSERT (FALSE);
536 return EFI_BUFFER_TOO_SMALL;
537 }
538 //
539 // Add the thunk to the list for this image. Do this last since the add
540 // function flushes the cache for us.
541 //
542 EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
543
544 return EFI_SUCCESS;
545 }