]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EbcDxe/EbcExecute.c
174e774de3087d81f3be5529346f88093b5ca1a7
[mirror_edk2.git] / MdeModulePkg / Universal / EbcDxe / EbcExecute.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 EbcExecute.c
15
16 Abstract:
17
18 Contains code that implements the virtual machine.
19
20 --*/
21
22 #include "EbcInt.h"
23 #include "EbcExecute.h"
24
25
26 //
27 // Define some useful data size constants to allow switch statements based on
28 // size of operands or data.
29 //
30 #define DATA_SIZE_INVALID 0
31 #define DATA_SIZE_8 1
32 #define DATA_SIZE_16 2
33 #define DATA_SIZE_32 4
34 #define DATA_SIZE_64 8
35 #define DATA_SIZE_N 48 // 4 or 8
36 //
37 // Structure we'll use to dispatch opcodes to execute functions.
38 //
39 typedef struct {
40 EFI_STATUS (*ExecuteFunction) (IN VM_CONTEXT * VmPtr);
41 }
42 VM_TABLE_ENTRY;
43
44 typedef
45 UINT64
46 (*DATA_MANIP_EXEC_FUNCTION) (
47 IN VM_CONTEXT * VmPtr,
48 IN UINT64 Op1,
49 IN UINT64 Op2
50 );
51
52 STATIC
53 INT16
54 VmReadIndex16 (
55 IN VM_CONTEXT *VmPtr,
56 IN UINT32 CodeOffset
57 );
58
59 STATIC
60 INT32
61 VmReadIndex32 (
62 IN VM_CONTEXT *VmPtr,
63 IN UINT32 CodeOffset
64 );
65
66 STATIC
67 INT64
68 VmReadIndex64 (
69 IN VM_CONTEXT *VmPtr,
70 IN UINT32 CodeOffset
71 );
72
73 STATIC
74 UINT8
75 VmReadMem8 (
76 IN VM_CONTEXT *VmPtr,
77 IN UINTN Addr
78 );
79
80 STATIC
81 UINT16
82 VmReadMem16 (
83 IN VM_CONTEXT *VmPtr,
84 IN UINTN Addr
85 );
86
87 STATIC
88 UINT32
89 VmReadMem32 (
90 IN VM_CONTEXT *VmPtr,
91 IN UINTN Addr
92 );
93
94 STATIC
95 UINT64
96 VmReadMem64 (
97 IN VM_CONTEXT *VmPtr,
98 IN UINTN Addr
99 );
100
101 STATIC
102 UINTN
103 VmReadMemN (
104 IN VM_CONTEXT *VmPtr,
105 IN UINTN Addr
106 );
107
108 STATIC
109 EFI_STATUS
110 VmWriteMem8 (
111 IN VM_CONTEXT *VmPtr,
112 UINTN Addr,
113 IN UINT8 Data
114 );
115
116 STATIC
117 EFI_STATUS
118 VmWriteMem16 (
119 IN VM_CONTEXT *VmPtr,
120 UINTN Addr,
121 IN UINT16 Data
122 );
123
124 STATIC
125 EFI_STATUS
126 VmWriteMem32 (
127 IN VM_CONTEXT *VmPtr,
128 UINTN Addr,
129 IN UINT32 Data
130 );
131
132 STATIC
133 UINT16
134 VmReadCode16 (
135 IN VM_CONTEXT *VmPtr,
136 IN UINT32 Offset
137 );
138
139 STATIC
140 UINT32
141 VmReadCode32 (
142 IN VM_CONTEXT *VmPtr,
143 IN UINT32 Offset
144 );
145
146 STATIC
147 UINT64
148 VmReadCode64 (
149 IN VM_CONTEXT *VmPtr,
150 IN UINT32 Offset
151 );
152
153 STATIC
154 INT8
155 VmReadImmed8 (
156 IN VM_CONTEXT *VmPtr,
157 IN UINT32 Offset
158 );
159
160 STATIC
161 INT16
162 VmReadImmed16 (
163 IN VM_CONTEXT *VmPtr,
164 IN UINT32 Offset
165 );
166
167 STATIC
168 INT32
169 VmReadImmed32 (
170 IN VM_CONTEXT *VmPtr,
171 IN UINT32 Offset
172 );
173
174 STATIC
175 INT64
176 VmReadImmed64 (
177 IN VM_CONTEXT *VmPtr,
178 IN UINT32 Offset
179 );
180
181 STATIC
182 UINTN
183 ConvertStackAddr (
184 IN VM_CONTEXT *VmPtr,
185 IN UINTN Addr
186 );
187
188 STATIC
189 EFI_STATUS
190 ExecuteDataManip (
191 IN VM_CONTEXT *VmPtr,
192 IN BOOLEAN IsSignedOperation
193 );
194
195 //
196 // Functions that execute VM opcodes
197 //
198 STATIC
199 EFI_STATUS
200 ExecuteBREAK (
201 IN VM_CONTEXT *VmPtr
202 );
203
204 STATIC
205 EFI_STATUS
206 ExecuteJMP (
207 IN VM_CONTEXT *VmPtr
208 );
209
210 STATIC
211 EFI_STATUS
212 ExecuteJMP8 (
213 IN VM_CONTEXT *VmPtr
214 );
215
216 STATIC
217 EFI_STATUS
218 ExecuteCALL (
219 IN VM_CONTEXT *VmPtr
220 );
221
222 STATIC
223 EFI_STATUS
224 ExecuteRET (
225 IN VM_CONTEXT *VmPtr
226 );
227
228 STATIC
229 EFI_STATUS
230 ExecuteCMP (
231 IN VM_CONTEXT *VmPtr
232 );
233
234 STATIC
235 EFI_STATUS
236 ExecuteCMPI (
237 IN VM_CONTEXT *VmPtr
238 );
239
240 STATIC
241 EFI_STATUS
242 ExecuteMOVxx (
243 IN VM_CONTEXT *VmPtr
244 );
245
246 STATIC
247 EFI_STATUS
248 ExecuteMOVI (
249 IN VM_CONTEXT *VmPtr
250 );
251
252 STATIC
253 EFI_STATUS
254 ExecuteMOVIn (
255 IN VM_CONTEXT *VmPtr
256 );
257
258 STATIC
259 EFI_STATUS
260 ExecuteMOVREL (
261 IN VM_CONTEXT *VmPtr
262 );
263
264 STATIC
265 EFI_STATUS
266 ExecutePUSHn (
267 IN VM_CONTEXT *VmPtr
268 );
269
270 STATIC
271 EFI_STATUS
272 ExecutePUSH (
273 IN VM_CONTEXT *VmPtr
274 );
275
276 STATIC
277 EFI_STATUS
278 ExecutePOPn (
279 IN VM_CONTEXT *VmPtr
280 );
281
282 STATIC
283 EFI_STATUS
284 ExecutePOP (
285 IN VM_CONTEXT *VmPtr
286 );
287
288 STATIC
289 EFI_STATUS
290 ExecuteSignedDataManip (
291 IN VM_CONTEXT *VmPtr
292 );
293
294 STATIC
295 EFI_STATUS
296 ExecuteUnsignedDataManip (
297 IN VM_CONTEXT *VmPtr
298 );
299
300 STATIC
301 EFI_STATUS
302 ExecuteLOADSP (
303 IN VM_CONTEXT *VmPtr
304 );
305
306 STATIC
307 EFI_STATUS
308 ExecuteSTORESP (
309 IN VM_CONTEXT *VmPtr
310 );
311
312 STATIC
313 EFI_STATUS
314 ExecuteMOVsnd (
315 IN VM_CONTEXT *VmPtr
316 );
317
318 STATIC
319 EFI_STATUS
320 ExecuteMOVsnw (
321 IN VM_CONTEXT *VmPtr
322 );
323
324 //
325 // Data manipulation subfunctions
326 //
327 STATIC
328 UINT64
329 ExecuteNOT (
330 IN VM_CONTEXT *VmPtr,
331 IN UINT64 Op1,
332 IN UINT64 Op2
333 );
334
335 STATIC
336 UINT64
337 ExecuteNEG (
338 IN VM_CONTEXT *VmPtr,
339 IN UINT64 Op1,
340 IN UINT64 Op2
341 );
342
343 STATIC
344 UINT64
345 ExecuteADD (
346 IN VM_CONTEXT *VmPtr,
347 IN UINT64 Op1,
348 IN UINT64 Op2
349 );
350
351 STATIC
352 UINT64
353 ExecuteSUB (
354 IN VM_CONTEXT *VmPtr,
355 IN UINT64 Op1,
356 IN UINT64 Op2
357 );
358
359 STATIC
360 UINT64
361 ExecuteMUL (
362 IN VM_CONTEXT *VmPtr,
363 IN UINT64 Op1,
364 IN UINT64 Op2
365 );
366
367 STATIC
368 UINT64
369 ExecuteMULU (
370 IN VM_CONTEXT *VmPtr,
371 IN UINT64 Op1,
372 IN UINT64 Op2
373 );
374
375 STATIC
376 UINT64
377 ExecuteDIV (
378 IN VM_CONTEXT *VmPtr,
379 IN UINT64 Op1,
380 IN UINT64 Op2
381 );
382
383 STATIC
384 UINT64
385 ExecuteDIVU (
386 IN VM_CONTEXT *VmPtr,
387 IN UINT64 Op1,
388 IN UINT64 Op2
389 );
390
391 STATIC
392 UINT64
393 ExecuteMOD (
394 IN VM_CONTEXT *VmPtr,
395 IN UINT64 Op1,
396 IN UINT64 Op2
397 );
398
399 STATIC
400 UINT64
401 ExecuteMODU (
402 IN VM_CONTEXT *VmPtr,
403 IN UINT64 Op1,
404 IN UINT64 Op2
405 );
406
407 STATIC
408 UINT64
409 ExecuteAND (
410 IN VM_CONTEXT *VmPtr,
411 IN UINT64 Op1,
412 IN UINT64 Op2
413 );
414
415 STATIC
416 UINT64
417 ExecuteOR (
418 IN VM_CONTEXT *VmPtr,
419 IN UINT64 Op1,
420 IN UINT64 Op2
421 );
422
423 STATIC
424 UINT64
425 ExecuteXOR (
426 IN VM_CONTEXT *VmPtr,
427 IN UINT64 Op1,
428 IN UINT64 Op2
429 );
430
431 STATIC
432 UINT64
433 ExecuteSHL (
434 IN VM_CONTEXT *VmPtr,
435 IN UINT64 Op1,
436 IN UINT64 Op2
437 );
438
439 STATIC
440 UINT64
441 ExecuteSHR (
442 IN VM_CONTEXT *VmPtr,
443 IN UINT64 Op1,
444 IN UINT64 Op2
445 );
446
447 STATIC
448 UINT64
449 ExecuteASHR (
450 IN VM_CONTEXT *VmPtr,
451 IN UINT64 Op1,
452 IN UINT64 Op2
453 );
454
455 STATIC
456 UINT64
457 ExecuteEXTNDB (
458 IN VM_CONTEXT *VmPtr,
459 IN UINT64 Op1,
460 IN UINT64 Op2
461 );
462
463 STATIC
464 UINT64
465 ExecuteEXTNDW (
466 IN VM_CONTEXT *VmPtr,
467 IN UINT64 Op1,
468 IN UINT64 Op2
469 );
470
471 STATIC
472 UINT64
473 ExecuteEXTNDD (
474 IN VM_CONTEXT *VmPtr,
475 IN UINT64 Op1,
476 IN UINT64 Op2
477 );
478
479 //
480 // Once we retrieve the operands for the data manipulation instructions,
481 // call these functions to perform the operation.
482 //
483 static CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable[] = {
484 ExecuteNOT,
485 ExecuteNEG,
486 ExecuteADD,
487 ExecuteSUB,
488 ExecuteMUL,
489 ExecuteMULU,
490 ExecuteDIV,
491 ExecuteDIVU,
492 ExecuteMOD,
493 ExecuteMODU,
494 ExecuteAND,
495 ExecuteOR,
496 ExecuteXOR,
497 ExecuteSHL,
498 ExecuteSHR,
499 ExecuteASHR,
500 ExecuteEXTNDB,
501 ExecuteEXTNDW,
502 ExecuteEXTNDD,
503 };
504
505 static CONST VM_TABLE_ENTRY mVmOpcodeTable[] = {
506 { ExecuteBREAK }, // opcode 0x00
507 { ExecuteJMP }, // opcode 0x01
508 { ExecuteJMP8 }, // opcode 0x02
509 { ExecuteCALL }, // opcode 0x03
510 { ExecuteRET }, // opcode 0x04
511 { ExecuteCMP }, // opcode 0x05 CMPeq
512 { ExecuteCMP }, // opcode 0x06 CMPlte
513 { ExecuteCMP }, // opcode 0x07 CMPgte
514 { ExecuteCMP }, // opcode 0x08 CMPulte
515 { ExecuteCMP }, // opcode 0x09 CMPugte
516 { ExecuteUnsignedDataManip }, // opcode 0x0A NOT
517 { ExecuteSignedDataManip }, // opcode 0x0B NEG
518 { ExecuteSignedDataManip }, // opcode 0x0C ADD
519 { ExecuteSignedDataManip }, // opcode 0x0D SUB
520 { ExecuteSignedDataManip }, // opcode 0x0E MUL
521 { ExecuteUnsignedDataManip }, // opcode 0x0F MULU
522 { ExecuteSignedDataManip }, // opcode 0x10 DIV
523 { ExecuteUnsignedDataManip }, // opcode 0x11 DIVU
524 { ExecuteSignedDataManip }, // opcode 0x12 MOD
525 { ExecuteUnsignedDataManip }, // opcode 0x13 MODU
526 { ExecuteUnsignedDataManip }, // opcode 0x14 AND
527 { ExecuteUnsignedDataManip }, // opcode 0x15 OR
528 { ExecuteUnsignedDataManip }, // opcode 0x16 XOR
529 { ExecuteUnsignedDataManip }, // opcode 0x17 SHL
530 { ExecuteUnsignedDataManip }, // opcode 0x18 SHR
531 { ExecuteSignedDataManip }, // opcode 0x19 ASHR
532 { ExecuteUnsignedDataManip }, // opcode 0x1A EXTNDB
533 { ExecuteUnsignedDataManip }, // opcode 0x1B EXTNDW
534 { ExecuteUnsignedDataManip }, // opcode 0x1C EXTNDD
535 { ExecuteMOVxx }, // opcode 0x1D MOVBW
536 { ExecuteMOVxx }, // opcode 0x1E MOVWW
537 { ExecuteMOVxx }, // opcode 0x1F MOVDW
538 { ExecuteMOVxx }, // opcode 0x20 MOVQW
539 { ExecuteMOVxx }, // opcode 0x21 MOVBD
540 { ExecuteMOVxx }, // opcode 0x22 MOVWD
541 { ExecuteMOVxx }, // opcode 0x23 MOVDD
542 { ExecuteMOVxx }, // opcode 0x24 MOVQD
543 { ExecuteMOVsnw }, // opcode 0x25 MOVsnw
544 { ExecuteMOVsnd }, // opcode 0x26 MOVsnd
545 { NULL }, // opcode 0x27
546 { ExecuteMOVxx }, // opcode 0x28 MOVqq
547 { ExecuteLOADSP }, // opcode 0x29 LOADSP SP1, R2
548 { ExecuteSTORESP }, // opcode 0x2A STORESP R1, SP2
549 { ExecutePUSH }, // opcode 0x2B PUSH {@}R1 [imm16]
550 { ExecutePOP }, // opcode 0x2C POP {@}R1 [imm16]
551 { ExecuteCMPI }, // opcode 0x2D CMPIEQ
552 { ExecuteCMPI }, // opcode 0x2E CMPILTE
553 { ExecuteCMPI }, // opcode 0x2F CMPIGTE
554 { ExecuteCMPI }, // opcode 0x30 CMPIULTE
555 { ExecuteCMPI }, // opcode 0x31 CMPIUGTE
556 { ExecuteMOVxx }, // opcode 0x32 MOVN
557 { ExecuteMOVxx }, // opcode 0x33 MOVND
558 { NULL }, // opcode 0x34
559 { ExecutePUSHn }, // opcode 0x35
560 { ExecutePOPn }, // opcode 0x36
561 { ExecuteMOVI }, // opcode 0x37 - mov immediate data
562 { ExecuteMOVIn }, // opcode 0x38 - mov immediate natural
563 { ExecuteMOVREL } // opcode 0x39 - move data relative to PC
564 };
565
566 //
567 // Length of JMP instructions, depending on upper two bits of opcode.
568 //
569 static CONST UINT8 mJMPLen[] = { 2, 2, 6, 10 };
570
571 //
572 // Simple Debugger Protocol GUID
573 //
574 EFI_GUID mEbcSimpleDebuggerProtocolGuid = EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL_GUID;
575
576 EFI_STATUS
577 EbcExecuteInstructions (
578 IN EFI_EBC_VM_TEST_PROTOCOL *This,
579 IN VM_CONTEXT *VmPtr,
580 IN OUT UINTN *InstructionCount
581 )
582 /*++
583
584 Routine Description:
585
586 Given a pointer to a new VM context, execute one or more instructions. This
587 function is only used for test purposes via the EBC VM test protocol.
588
589 Arguments:
590
591 This - pointer to protocol interface
592 VmPtr - pointer to a VM context
593 InstructionCount - how many instructions to execute. 0 if don't count.
594
595 Returns:
596
597 EFI_UNSUPPORTED
598 EFI_SUCCESS
599
600 --*/
601 {
602 UINTN ExecFunc;
603 EFI_STATUS Status;
604 UINTN InstructionsLeft;
605 UINTN SavedInstructionCount;
606
607 Status = EFI_SUCCESS;
608
609 if (*InstructionCount == 0) {
610 InstructionsLeft = 1;
611 } else {
612 InstructionsLeft = *InstructionCount;
613 }
614
615 SavedInstructionCount = *InstructionCount;
616 *InstructionCount = 0;
617
618 //
619 // Index into the opcode table using the opcode byte for this instruction.
620 // This gives you the execute function, which we first test for null, then
621 // call it if it's not null.
622 //
623 while (InstructionsLeft != 0) {
624 ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & 0x3F)].ExecuteFunction;
625 if (ExecFunc == (UINTN) NULL) {
626 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
627 return EFI_UNSUPPORTED;
628 } else {
629 mVmOpcodeTable[(*VmPtr->Ip & 0x3F)].ExecuteFunction (VmPtr);
630 *InstructionCount = *InstructionCount + 1;
631 }
632
633 //
634 // Decrement counter if applicable
635 //
636 if (SavedInstructionCount != 0) {
637 InstructionsLeft--;
638 }
639 }
640
641 return Status;
642 }
643
644 EFI_STATUS
645 EbcExecute (
646 IN VM_CONTEXT *VmPtr
647 )
648 /*++
649
650 Routine Description:
651
652 Execute an EBC image from an entry point or from a published protocol.
653
654 Arguments:
655
656 VmPtr - pointer to prepared VM context.
657
658 Returns:
659
660 Standard EBC status.
661
662 --*/
663 {
664 UINTN ExecFunc;
665 UINT8 StackCorrupted;
666 EFI_STATUS Status;
667 EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL *EbcSimpleDebugger;
668
669 mVmPtr = VmPtr;
670 EbcSimpleDebugger = NULL;
671 Status = EFI_SUCCESS;
672 StackCorrupted = 0;
673
674 //
675 // Make sure the magic value has been put on the stack before we got here.
676 //
677 if (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE) {
678 StackCorrupted = 1;
679 }
680
681 VmPtr->FramePtr = (VOID *) ((UINT8 *) (UINTN) VmPtr->R[0] + 8);
682
683 //
684 // Try to get the debug support for EBC
685 //
686 DEBUG_CODE_BEGIN ();
687 Status = gBS->LocateProtocol (
688 &mEbcSimpleDebuggerProtocolGuid,
689 NULL,
690 (VOID **) &EbcSimpleDebugger
691 );
692 if (EFI_ERROR (Status)) {
693 EbcSimpleDebugger = NULL;
694 }
695 DEBUG_CODE_END ();
696
697 //
698 // Save the start IP for debug. For example, if we take an exception we
699 // can print out the location of the exception relative to the entry point,
700 // which could then be used in a disassembly listing to find the problem.
701 //
702 VmPtr->EntryPoint = (VOID *) VmPtr->Ip;
703
704 //
705 // We'll wait for this flag to know when we're done. The RET
706 // instruction sets it if it runs out of stack.
707 //
708 VmPtr->StopFlags = 0;
709 while (!(VmPtr->StopFlags & STOPFLAG_APP_DONE)) {
710 //
711 // If we've found a simple debugger protocol, call it
712 //
713 DEBUG_CODE_BEGIN ();
714 if (EbcSimpleDebugger != NULL) {
715 EbcSimpleDebugger->Debugger (EbcSimpleDebugger, VmPtr);
716 }
717 DEBUG_CODE_END ();
718
719 //
720 // Verify the opcode is in range. Otherwise generate an exception.
721 //
722 if ((*VmPtr->Ip & OPCODE_M_OPCODE) >= (sizeof (mVmOpcodeTable) / sizeof (mVmOpcodeTable[0]))) {
723 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
724 Status = EFI_UNSUPPORTED;
725 goto Done;
726 }
727 //
728 // Use the opcode bits to index into the opcode dispatch table. If the
729 // function pointer is null then generate an exception.
730 //
731 ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction;
732 if (ExecFunc == (UINTN) NULL) {
733 EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr);
734 Status = EFI_UNSUPPORTED;
735 goto Done;
736 }
737 //
738 // The EBC VM is a strongly ordered processor, so perform a fence operation before
739 // and after each instruction is executed.
740 //
741 MemoryFence ();
742
743 mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr);
744
745 MemoryFence ();
746
747 //
748 // If the step flag is set, signal an exception and continue. We don't
749 // clear it here. Assuming the debugger is responsible for clearing it.
750 //
751 if (VMFLAG_ISSET (VmPtr, VMFLAGS_STEP)) {
752 EbcDebugSignalException (EXCEPT_EBC_STEP, EXCEPTION_FLAG_NONE, VmPtr);
753 }
754 //
755 // Make sure stack has not been corrupted. Only report it once though.
756 //
757 if (!StackCorrupted && (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE)) {
758 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr);
759 StackCorrupted = 1;
760 }
761 if (!StackCorrupted && ((UINT64)VmPtr->R[0] <= (UINT64)(UINTN) VmPtr->StackTop)) {
762 EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr);
763 StackCorrupted = 1;
764 }
765 }
766
767 Done:
768 mVmPtr = NULL;
769
770 return Status;
771 }
772
773 STATIC
774 EFI_STATUS
775 ExecuteMOVxx (
776 IN VM_CONTEXT *VmPtr
777 )
778 /*++
779
780 Routine Description:
781
782 Execute the MOVxx instructions.
783
784 Arguments:
785
786 VmPtr - pointer to a VM context.
787
788 Returns:
789
790 EFI_UNSUPPORTED
791 EFI_SUCCESS
792
793 Instruction format:
794
795 MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32}
796 MOVqq {@}R1 {Index64}, {@}R2 {Index64}
797
798 Copies contents of [R2] -> [R1], zero extending where required.
799
800 First character indicates the size of the move.
801 Second character indicates the size of the index(s).
802
803 Invalid to have R1 direct with index.
804
805 --*/
806 {
807 UINT8 Opcode;
808 UINT8 OpcMasked;
809 UINT8 Operands;
810 UINT8 Size;
811 UINT8 MoveSize;
812 INT16 Index16;
813 INT32 Index32;
814 INT64 Index64Op1;
815 INT64 Index64Op2;
816 UINT64 Data64;
817 UINT64 DataMask;
818 UINTN Source;
819
820 Opcode = GETOPCODE (VmPtr);
821 OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE);
822
823 //
824 // Get the operands byte so we can get R1 and R2
825 //
826 Operands = GETOPERANDS (VmPtr);
827
828 //
829 // Assume no indexes
830 //
831 Index64Op1 = 0;
832 Index64Op2 = 0;
833 Data64 = 0;
834
835 //
836 // Determine if we have an index/immediate data. Base instruction size
837 // is 2 (opcode + operands). Add to this size each index specified.
838 //
839 Size = 2;
840 if (Opcode & (OPCODE_M_IMMED_OP1 | OPCODE_M_IMMED_OP2)) {
841 //
842 // Determine size of the index from the opcode. Then get it.
843 //
844 if ((OpcMasked <= OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVNW)) {
845 //
846 // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index.
847 // Get one or both index values.
848 //
849 if (Opcode & OPCODE_M_IMMED_OP1) {
850 Index16 = VmReadIndex16 (VmPtr, 2);
851 Index64Op1 = (INT64) Index16;
852 Size += sizeof (UINT16);
853 }
854
855 if (Opcode & OPCODE_M_IMMED_OP2) {
856 Index16 = VmReadIndex16 (VmPtr, Size);
857 Index64Op2 = (INT64) Index16;
858 Size += sizeof (UINT16);
859 }
860 } else if ((OpcMasked <= OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVND)) {
861 //
862 // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
863 //
864 if (Opcode & OPCODE_M_IMMED_OP1) {
865 Index32 = VmReadIndex32 (VmPtr, 2);
866 Index64Op1 = (INT64) Index32;
867 Size += sizeof (UINT32);
868 }
869
870 if (Opcode & OPCODE_M_IMMED_OP2) {
871 Index32 = VmReadIndex32 (VmPtr, Size);
872 Index64Op2 = (INT64) Index32;
873 Size += sizeof (UINT32);
874 }
875 } else if (OpcMasked == OPCODE_MOVQQ) {
876 //
877 // MOVqq -- only form with a 64-bit index
878 //
879 if (Opcode & OPCODE_M_IMMED_OP1) {
880 Index64Op1 = VmReadIndex64 (VmPtr, 2);
881 Size += sizeof (UINT64);
882 }
883
884 if (Opcode & OPCODE_M_IMMED_OP2) {
885 Index64Op2 = VmReadIndex64 (VmPtr, Size);
886 Size += sizeof (UINT64);
887 }
888 } else {
889 //
890 // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index
891 //
892 EbcDebugSignalException (
893 EXCEPT_EBC_INSTRUCTION_ENCODING,
894 EXCEPTION_FLAG_FATAL,
895 VmPtr
896 );
897 return EFI_UNSUPPORTED;
898 }
899 }
900 //
901 // Determine the size of the move, and create a mask for it so we can
902 // clear unused bits.
903 //
904 if ((OpcMasked == OPCODE_MOVBW) || (OpcMasked == OPCODE_MOVBD)) {
905 MoveSize = DATA_SIZE_8;
906 DataMask = 0xFF;
907 } else if ((OpcMasked == OPCODE_MOVWW) || (OpcMasked == OPCODE_MOVWD)) {
908 MoveSize = DATA_SIZE_16;
909 DataMask = 0xFFFF;
910 } else if ((OpcMasked == OPCODE_MOVDW) || (OpcMasked == OPCODE_MOVDD)) {
911 MoveSize = DATA_SIZE_32;
912 DataMask = 0xFFFFFFFF;
913 } else if ((OpcMasked == OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVQQ)) {
914 MoveSize = DATA_SIZE_64;
915 DataMask = (UINT64)~0;
916 } else if ((OpcMasked == OPCODE_MOVNW) || (OpcMasked == OPCODE_MOVND)) {
917 MoveSize = DATA_SIZE_N;
918 DataMask = (UINT64)~0 >> (64 - 8 * sizeof (UINTN));
919 } else {
920 //
921 // We were dispatched to this function and we don't recognize the opcode
922 //
923 EbcDebugSignalException (EXCEPT_EBC_UNDEFINED, EXCEPTION_FLAG_FATAL, VmPtr);
924 return EFI_UNSUPPORTED;
925 }
926 //
927 // Now get the source address
928 //
929 if (OPERAND2_INDIRECT (Operands)) {
930 //
931 // Indirect form @R2. Compute address of operand2
932 //
933 Source = (UINTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Index64Op2);
934 //
935 // Now get the data from the source. Always 0-extend and let the compiler
936 // sign-extend where required.
937 //
938 switch (MoveSize) {
939 case DATA_SIZE_8:
940 Data64 = (UINT64) (UINT8) VmReadMem8 (VmPtr, Source);
941 break;
942
943 case DATA_SIZE_16:
944 Data64 = (UINT64) (UINT16) VmReadMem16 (VmPtr, Source);
945 break;
946
947 case DATA_SIZE_32:
948 Data64 = (UINT64) (UINT32) VmReadMem32 (VmPtr, Source);
949 break;
950
951 case DATA_SIZE_64:
952 Data64 = (UINT64) VmReadMem64 (VmPtr, Source);
953 break;
954
955 case DATA_SIZE_N:
956 Data64 = (UINT64) (UINTN) VmReadMemN (VmPtr, Source);
957 break;
958
959 default:
960 //
961 // not reached
962 //
963 break;
964 }
965 } else {
966 //
967 // Not indirect source: MOVxx {@}Rx, Ry [Index]
968 //
969 Data64 = VmPtr->R[OPERAND2_REGNUM (Operands)] + Index64Op2;
970 //
971 // Did Operand2 have an index? If so, treat as two signed values since
972 // indexes are signed values.
973 //
974 if (Opcode & OPCODE_M_IMMED_OP2) {
975 //
976 // NOTE: need to find a way to fix this, most likely by changing the VM
977 // implementation to remove the stack gap. To do that, we'd need to
978 // allocate stack space for the VM and actually set the system
979 // stack pointer to the allocated buffer when the VM starts.
980 //
981 // Special case -- if someone took the address of a function parameter
982 // then we need to make sure it's not in the stack gap. We can identify
983 // this situation if (Operand2 register == 0) && (Operand2 is direct)
984 // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0)
985 // Situations that to be aware of:
986 // * stack adjustments at beginning and end of functions R0 = R0 += stacksize
987 //
988 if ((OPERAND2_REGNUM (Operands) == 0) &&
989 (!OPERAND2_INDIRECT (Operands)) &&
990 (Index64Op2 > 0) &&
991 (OPERAND1_REGNUM (Operands) == 0) &&
992 (OPERAND1_INDIRECT (Operands))
993 ) {
994 Data64 = (UINT64) ConvertStackAddr (VmPtr, (UINTN) (INT64) Data64);
995 }
996 }
997 }
998 //
999 // Now write it back
1000 //
1001 if (OPERAND1_INDIRECT (Operands)) {
1002 //
1003 // Reuse the Source variable to now be dest.
1004 //
1005 Source = (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index64Op1);
1006 //
1007 // Do the write based on the size
1008 //
1009 switch (MoveSize) {
1010 case DATA_SIZE_8:
1011 VmWriteMem8 (VmPtr, Source, (UINT8) Data64);
1012 break;
1013
1014 case DATA_SIZE_16:
1015 VmWriteMem16 (VmPtr, Source, (UINT16) Data64);
1016 break;
1017
1018 case DATA_SIZE_32:
1019 VmWriteMem32 (VmPtr, Source, (UINT32) Data64);
1020 break;
1021
1022 case DATA_SIZE_64:
1023 VmWriteMem64 (VmPtr, Source, Data64);
1024 break;
1025
1026 case DATA_SIZE_N:
1027 VmWriteMemN (VmPtr, Source, (UINTN) Data64);
1028 break;
1029
1030 default:
1031 //
1032 // not reached
1033 //
1034 break;
1035 }
1036 } else {
1037 //
1038 // Operand1 direct.
1039 // Make sure we didn't have an index on operand1.
1040 //
1041 if (Opcode & OPCODE_M_IMMED_OP1) {
1042 EbcDebugSignalException (
1043 EXCEPT_EBC_INSTRUCTION_ENCODING,
1044 EXCEPTION_FLAG_FATAL,
1045 VmPtr
1046 );
1047 return EFI_UNSUPPORTED;
1048 }
1049 //
1050 // Direct storage in register. Clear unused bits and store back to
1051 // register.
1052 //
1053 VmPtr->R[OPERAND1_REGNUM (Operands)] = Data64 & DataMask;
1054 }
1055 //
1056 // Advance the instruction pointer
1057 //
1058 VmPtr->Ip += Size;
1059 return EFI_SUCCESS;
1060 }
1061
1062 STATIC
1063 EFI_STATUS
1064 ExecuteBREAK (
1065 IN VM_CONTEXT *VmPtr
1066 )
1067 /*++
1068
1069 Routine Description:
1070
1071 Execute the EBC BREAK instruction
1072
1073 Arguments:
1074
1075 VmPtr - pointer to current VM context
1076
1077 Returns:
1078
1079 EFI_UNSUPPORTED
1080 EFI_SUCCESS
1081
1082 --*/
1083 {
1084 UINT8 Operands;
1085 VOID *EbcEntryPoint;
1086 VOID *Thunk;
1087 UINT64 U64EbcEntryPoint;
1088 INT32 Offset;
1089
1090 Operands = GETOPERANDS (VmPtr);
1091 switch (Operands) {
1092 //
1093 // Runaway program break. Generate an exception and terminate
1094 //
1095 case 0:
1096 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr);
1097 break;
1098
1099 //
1100 // Get VM version -- return VM revision number in R7
1101 //
1102 case 1:
1103 //
1104 // Bits:
1105 // 63-17 = 0
1106 // 16-8 = Major version
1107 // 7-0 = Minor version
1108 //
1109 VmPtr->R[7] = GetVmVersion ();
1110 break;
1111
1112 //
1113 // Debugger breakpoint
1114 //
1115 case 3:
1116 VmPtr->StopFlags |= STOPFLAG_BREAKPOINT;
1117 //
1118 // See if someone has registered a handler
1119 //
1120 EbcDebugSignalException (
1121 EXCEPT_EBC_BREAKPOINT,
1122 EXCEPTION_FLAG_NONE,
1123 VmPtr
1124 );
1125 break;
1126
1127 //
1128 // System call, which there are none, so NOP it.
1129 //
1130 case 4:
1131 break;
1132
1133 //
1134 // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
1135 // "offset from self" pointer to the EBC entry point.
1136 // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
1137 //
1138 case 5:
1139 Offset = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->R[7]);
1140 U64EbcEntryPoint = (UINT64) (VmPtr->R[7] + Offset + 4);
1141 EbcEntryPoint = (VOID *) (UINTN) U64EbcEntryPoint;
1142
1143 //
1144 // Now create a new thunk
1145 //
1146 EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0);
1147
1148 //
1149 // Finally replace the EBC entry point memory with the thunk address
1150 //
1151 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[7], (UINT64) (UINTN) Thunk);
1152 break;
1153
1154 //
1155 // Compiler setting version per value in R7
1156 //
1157 case 6:
1158 VmPtr->CompilerVersion = (UINT32) VmPtr->R[7];
1159 //
1160 // Check compiler version against VM version?
1161 //
1162 break;
1163
1164 //
1165 // Unhandled break code. Signal exception.
1166 //
1167 default:
1168 EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr);
1169 break;
1170 }
1171 //
1172 // Advance IP
1173 //
1174 VmPtr->Ip += 2;
1175 return EFI_SUCCESS;
1176 }
1177
1178 STATIC
1179 EFI_STATUS
1180 ExecuteJMP (
1181 IN VM_CONTEXT *VmPtr
1182 )
1183 /*++
1184
1185 Routine Description:
1186 Execute the JMP instruction
1187
1188 Arguments:
1189 VmPtr - pointer to VM context
1190
1191 Returns:
1192 Standard EFI_STATUS
1193
1194 Instruction syntax:
1195 JMP64{cs|cc} Immed64
1196 JMP32{cs|cc} {@}R1 {Immed32|Index32}
1197
1198 Encoding:
1199 b0.7 - immediate data present
1200 b0.6 - 1 = 64 bit immediate data
1201 0 = 32 bit immediate data
1202 b1.7 - 1 = conditional
1203 b1.6 1 = CS (condition set)
1204 0 = CC (condition clear)
1205 b1.4 1 = relative address
1206 0 = absolute address
1207 b1.3 1 = operand1 indirect
1208 b1.2-0 operand 1
1209
1210 --*/
1211 {
1212 UINT8 Opcode;
1213 UINT8 CompareSet;
1214 UINT8 ConditionFlag;
1215 UINT8 Size;
1216 UINT8 Operand;
1217 UINT64 Data64;
1218 INT32 Index32;
1219 UINTN Addr;
1220
1221 Operand = GETOPERANDS (VmPtr);
1222 Opcode = GETOPCODE (VmPtr);
1223
1224 //
1225 // Get instruction length from the opcode. The upper two bits are used here
1226 // to index into the length array.
1227 //
1228 Size = mJMPLen[(Opcode >> 6) & 0x03];
1229
1230 //
1231 // Decode instruction conditions
1232 // If we haven't met the condition, then simply advance the IP and return.
1233 //
1234 CompareSet = (UINT8) ((Operand & JMP_M_CS) ? 1 : 0);
1235 ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC);
1236 if (Operand & CONDITION_M_CONDITIONAL) {
1237 if (CompareSet != ConditionFlag) {
1238 VmPtr->Ip += Size;
1239 return EFI_SUCCESS;
1240 }
1241 }
1242 //
1243 // Check for 64-bit form and do it right away since it's the most
1244 // straight-forward form.
1245 //
1246 if (Opcode & OPCODE_M_IMMDATA64) {
1247 //
1248 // Double check for immediate-data, which is required. If not there,
1249 // then signal an exception
1250 //
1251 if (!(Opcode & OPCODE_M_IMMDATA)) {
1252 EbcDebugSignalException (
1253 EXCEPT_EBC_INSTRUCTION_ENCODING,
1254 EXCEPTION_FLAG_ERROR,
1255 VmPtr
1256 );
1257 return EFI_UNSUPPORTED;
1258 }
1259 //
1260 // 64-bit immediate data is full address. Read the immediate data,
1261 // check for alignment, and jump absolute.
1262 //
1263 Data64 = VmReadImmed64 (VmPtr, 2);
1264 if (!IS_ALIGNED ((UINTN) Data64, sizeof (UINT16))) {
1265 EbcDebugSignalException (
1266 EXCEPT_EBC_ALIGNMENT_CHECK,
1267 EXCEPTION_FLAG_FATAL,
1268 VmPtr
1269 );
1270
1271 return EFI_UNSUPPORTED;
1272 }
1273
1274 //
1275 // Take jump -- relative or absolute
1276 //
1277 if (Operand & JMP_M_RELATIVE) {
1278 VmPtr->Ip += (UINTN) Data64 + Size;
1279 } else {
1280 VmPtr->Ip = (VMIP) (UINTN) Data64;
1281 }
1282
1283 return EFI_SUCCESS;
1284 }
1285 //
1286 // 32-bit forms:
1287 // Get the index if there is one. May be either an index, or an immediate
1288 // offset depending on indirect operand.
1289 // JMP32 @R1 Index32 -- immediate data is an index
1290 // JMP32 R1 Immed32 -- immedate data is an offset
1291 //
1292 if (Opcode & OPCODE_M_IMMDATA) {
1293 if (OPERAND1_INDIRECT (Operand)) {
1294 Index32 = VmReadIndex32 (VmPtr, 2);
1295 } else {
1296 Index32 = VmReadImmed32 (VmPtr, 2);
1297 }
1298 } else {
1299 Index32 = 0;
1300 }
1301 //
1302 // Get the register data. If R == 0, then special case where it's ignored.
1303 //
1304 if (OPERAND1_REGNUM (Operand) == 0) {
1305 Data64 = 0;
1306 } else {
1307 Data64 = OPERAND1_REGDATA (VmPtr, Operand);
1308 }
1309 //
1310 // Decode the forms
1311 //
1312 if (OPERAND1_INDIRECT (Operand)) {
1313 //
1314 // Form: JMP32 @Rx {Index32}
1315 //
1316 Addr = VmReadMemN (VmPtr, (UINTN) Data64 + Index32);
1317 if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) {
1318 EbcDebugSignalException (
1319 EXCEPT_EBC_ALIGNMENT_CHECK,
1320 EXCEPTION_FLAG_FATAL,
1321 VmPtr
1322 );
1323
1324 return EFI_UNSUPPORTED;
1325 }
1326
1327 if (Operand & JMP_M_RELATIVE) {
1328 VmPtr->Ip += (UINTN) Addr + Size;
1329 } else {
1330 VmPtr->Ip = (VMIP) Addr;
1331 }
1332 } else {
1333 //
1334 // Form: JMP32 Rx {Immed32}
1335 //
1336 Addr = (UINTN) (Data64 + Index32);
1337 if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) {
1338 EbcDebugSignalException (
1339 EXCEPT_EBC_ALIGNMENT_CHECK,
1340 EXCEPTION_FLAG_FATAL,
1341 VmPtr
1342 );
1343
1344 return EFI_UNSUPPORTED;
1345 }
1346
1347 if (Operand & JMP_M_RELATIVE) {
1348 VmPtr->Ip += (UINTN) Addr + Size;
1349 } else {
1350 VmPtr->Ip = (VMIP) Addr;
1351 }
1352 }
1353
1354 return EFI_SUCCESS;
1355 }
1356
1357 STATIC
1358 EFI_STATUS
1359 ExecuteJMP8 (
1360 IN VM_CONTEXT *VmPtr
1361 )
1362 /*++
1363
1364 Routine Description:
1365 Execute the EBC JMP8 instruction
1366
1367 Arguments:
1368 VmPtr - pointer to a VM context
1369
1370 Returns:
1371 Standard EFI_STATUS
1372
1373 Instruction syntax:
1374 JMP8{cs|cc} Offset/2
1375
1376 --*/
1377 {
1378 UINT8 Opcode;
1379 UINT8 ConditionFlag;
1380 UINT8 CompareSet;
1381 INT8 Offset;
1382
1383 //
1384 // Decode instruction.
1385 //
1386 Opcode = GETOPCODE (VmPtr);
1387 CompareSet = (UINT8) ((Opcode & JMP_M_CS) ? 1 : 0);
1388 ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC);
1389
1390 //
1391 // If we haven't met the condition, then simply advance the IP and return
1392 //
1393 if (Opcode & CONDITION_M_CONDITIONAL) {
1394 if (CompareSet != ConditionFlag) {
1395 VmPtr->Ip += 2;
1396 return EFI_SUCCESS;
1397 }
1398 }
1399 //
1400 // Get the offset from the instruction stream. It's relative to the
1401 // following instruction, and divided by 2.
1402 //
1403 Offset = VmReadImmed8 (VmPtr, 1);
1404 //
1405 // Want to check for offset == -2 and then raise an exception?
1406 //
1407 VmPtr->Ip += (Offset * 2) + 2;
1408 return EFI_SUCCESS;
1409 }
1410
1411 STATIC
1412 EFI_STATUS
1413 ExecuteMOVI (
1414 IN VM_CONTEXT *VmPtr
1415 )
1416 /*++
1417
1418 Routine Description:
1419
1420 Execute the EBC MOVI
1421
1422 Arguments:
1423
1424 VmPtr - pointer to a VM context
1425
1426 Returns:
1427
1428 Standard EFI_STATUS
1429
1430 Instruction syntax:
1431
1432 MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64
1433
1434 First variable character specifies the move size
1435 Second variable character specifies size of the immediate data
1436
1437 Sign-extend the immediate data to the size of the operation, and zero-extend
1438 if storing to a register.
1439
1440 Operand1 direct with index/immed is invalid.
1441
1442 --*/
1443 {
1444 UINT8 Opcode;
1445 UINT8 Operands;
1446 UINT8 Size;
1447 INT16 Index16;
1448 INT64 ImmData64;
1449 UINT64 Op1;
1450 UINT64 Mask64;
1451
1452 //
1453 // Get the opcode and operands byte so we can get R1 and R2
1454 //
1455 Opcode = GETOPCODE (VmPtr);
1456 Operands = GETOPERANDS (VmPtr);
1457
1458 //
1459 // Get the index (16-bit) if present
1460 //
1461 if (Operands & MOVI_M_IMMDATA) {
1462 Index16 = VmReadIndex16 (VmPtr, 2);
1463 Size = 4;
1464 } else {
1465 Index16 = 0;
1466 Size = 2;
1467 }
1468 //
1469 // Extract the immediate data. Sign-extend always.
1470 //
1471 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) {
1472 ImmData64 = (INT64) (INT16) VmReadImmed16 (VmPtr, Size);
1473 Size += 2;
1474 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) {
1475 ImmData64 = (INT64) (INT32) VmReadImmed32 (VmPtr, Size);
1476 Size += 4;
1477 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) {
1478 ImmData64 = (INT64) VmReadImmed64 (VmPtr, Size);
1479 Size += 8;
1480 } else {
1481 //
1482 // Invalid encoding
1483 //
1484 EbcDebugSignalException (
1485 EXCEPT_EBC_INSTRUCTION_ENCODING,
1486 EXCEPTION_FLAG_FATAL,
1487 VmPtr
1488 );
1489 return EFI_UNSUPPORTED;
1490 }
1491 //
1492 // Now write back the result
1493 //
1494 if (!OPERAND1_INDIRECT (Operands)) {
1495 //
1496 // Operand1 direct. Make sure it didn't have an index.
1497 //
1498 if (Operands & MOVI_M_IMMDATA) {
1499 EbcDebugSignalException (
1500 EXCEPT_EBC_INSTRUCTION_ENCODING,
1501 EXCEPTION_FLAG_FATAL,
1502 VmPtr
1503 );
1504 return EFI_UNSUPPORTED;
1505 }
1506 //
1507 // Writing directly to a register. Clear unused bits.
1508 //
1509 if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) {
1510 Mask64 = 0x000000FF;
1511 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) {
1512 Mask64 = 0x0000FFFF;
1513 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) {
1514 Mask64 = 0x00000000FFFFFFFF;
1515 } else {
1516 Mask64 = (UINT64)~0;
1517 }
1518
1519 VmPtr->R[OPERAND1_REGNUM (Operands)] = ImmData64 & Mask64;
1520 } else {
1521 //
1522 // Get the address then write back based on size of the move
1523 //
1524 Op1 = (UINT64) VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16;
1525 if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) {
1526 VmWriteMem8 (VmPtr, (UINTN) Op1, (UINT8) ImmData64);
1527 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) {
1528 VmWriteMem16 (VmPtr, (UINTN) Op1, (UINT16) ImmData64);
1529 } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) {
1530 VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) ImmData64);
1531 } else {
1532 VmWriteMem64 (VmPtr, (UINTN) Op1, ImmData64);
1533 }
1534 }
1535 //
1536 // Advance the instruction pointer
1537 //
1538 VmPtr->Ip += Size;
1539 return EFI_SUCCESS;
1540 }
1541
1542 STATIC
1543 EFI_STATUS
1544 ExecuteMOVIn (
1545 IN VM_CONTEXT *VmPtr
1546 )
1547 /*++
1548
1549 Routine Description:
1550
1551 Execute the EBC MOV immediate natural. This instruction moves an immediate
1552 index value into a register or memory location.
1553
1554 Arguments:
1555
1556 VmPtr - pointer to a VM context
1557
1558 Returns:
1559
1560 Standard EFI_STATUS
1561
1562 Instruction syntax:
1563
1564 MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64
1565
1566 --*/
1567 {
1568 UINT8 Opcode;
1569 UINT8 Operands;
1570 UINT8 Size;
1571 INT16 Index16;
1572 INT16 ImmedIndex16;
1573 INT32 ImmedIndex32;
1574 INT64 ImmedIndex64;
1575 UINT64 Op1;
1576
1577 //
1578 // Get the opcode and operands byte so we can get R1 and R2
1579 //
1580 Opcode = GETOPCODE (VmPtr);
1581 Operands = GETOPERANDS (VmPtr);
1582
1583 //
1584 // Get the operand1 index (16-bit) if present
1585 //
1586 if (Operands & MOVI_M_IMMDATA) {
1587 Index16 = VmReadIndex16 (VmPtr, 2);
1588 Size = 4;
1589 } else {
1590 Index16 = 0;
1591 Size = 2;
1592 }
1593 //
1594 // Extract the immediate data and convert to a 64-bit index.
1595 //
1596 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) {
1597 ImmedIndex16 = VmReadIndex16 (VmPtr, Size);
1598 ImmedIndex64 = (INT64) ImmedIndex16;
1599 Size += 2;
1600 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) {
1601 ImmedIndex32 = VmReadIndex32 (VmPtr, Size);
1602 ImmedIndex64 = (INT64) ImmedIndex32;
1603 Size += 4;
1604 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) {
1605 ImmedIndex64 = VmReadIndex64 (VmPtr, Size);
1606 Size += 8;
1607 } else {
1608 //
1609 // Invalid encoding
1610 //
1611 EbcDebugSignalException (
1612 EXCEPT_EBC_INSTRUCTION_ENCODING,
1613 EXCEPTION_FLAG_FATAL,
1614 VmPtr
1615 );
1616 return EFI_UNSUPPORTED;
1617 }
1618 //
1619 // Now write back the result
1620 //
1621 if (!OPERAND1_INDIRECT (Operands)) {
1622 //
1623 // Check for MOVIn R1 Index16, Immed (not indirect, with index), which
1624 // is illegal
1625 //
1626 if (Operands & MOVI_M_IMMDATA) {
1627 EbcDebugSignalException (
1628 EXCEPT_EBC_INSTRUCTION_ENCODING,
1629 EXCEPTION_FLAG_FATAL,
1630 VmPtr
1631 );
1632 return EFI_UNSUPPORTED;
1633 }
1634
1635 VmPtr->R[OPERAND1_REGNUM (Operands)] = ImmedIndex64;
1636 } else {
1637 //
1638 // Get the address
1639 //
1640 Op1 = (UINT64) VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16;
1641 VmWriteMemN (VmPtr, (UINTN) Op1, (INTN) ImmedIndex64);
1642 }
1643 //
1644 // Advance the instruction pointer
1645 //
1646 VmPtr->Ip += Size;
1647 return EFI_SUCCESS;
1648 }
1649
1650 STATIC
1651 EFI_STATUS
1652 ExecuteMOVREL (
1653 IN VM_CONTEXT *VmPtr
1654 )
1655 /*++
1656
1657 Routine Description:
1658
1659 Execute the EBC MOVREL instruction.
1660 Dest <- Ip + ImmData
1661
1662 Arguments:
1663
1664 VmPtr - pointer to a VM context
1665
1666 Returns:
1667
1668 Standard EFI_STATUS
1669
1670 Instruction syntax:
1671
1672 MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64
1673
1674 --*/
1675 {
1676 UINT8 Opcode;
1677 UINT8 Operands;
1678 UINT8 Size;
1679 INT16 Index16;
1680 INT64 ImmData64;
1681 UINT64 Op1;
1682 UINT64 Op2;
1683
1684 //
1685 // Get the opcode and operands byte so we can get R1 and R2
1686 //
1687 Opcode = GETOPCODE (VmPtr);
1688 Operands = GETOPERANDS (VmPtr);
1689
1690 //
1691 // Get the Operand 1 index (16-bit) if present
1692 //
1693 if (Operands & MOVI_M_IMMDATA) {
1694 Index16 = VmReadIndex16 (VmPtr, 2);
1695 Size = 4;
1696 } else {
1697 Index16 = 0;
1698 Size = 2;
1699 }
1700 //
1701 // Get the immediate data.
1702 //
1703 if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) {
1704 ImmData64 = (INT64) VmReadImmed16 (VmPtr, Size);
1705 Size += 2;
1706 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) {
1707 ImmData64 = (INT64) VmReadImmed32 (VmPtr, Size);
1708 Size += 4;
1709 } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) {
1710 ImmData64 = VmReadImmed64 (VmPtr, Size);
1711 Size += 8;
1712 } else {
1713 //
1714 // Invalid encoding
1715 //
1716 EbcDebugSignalException (
1717 EXCEPT_EBC_INSTRUCTION_ENCODING,
1718 EXCEPTION_FLAG_FATAL,
1719 VmPtr
1720 );
1721 return EFI_UNSUPPORTED;
1722 }
1723 //
1724 // Compute the value and write back the result
1725 //
1726 Op2 = (UINT64) ((INT64) ((UINT64) (UINTN) VmPtr->Ip) + (INT64) ImmData64 + Size);
1727 if (!OPERAND1_INDIRECT (Operands)) {
1728 //
1729 // Check for illegal combination of operand1 direct with immediate data
1730 //
1731 if (Operands & MOVI_M_IMMDATA) {
1732 EbcDebugSignalException (
1733 EXCEPT_EBC_INSTRUCTION_ENCODING,
1734 EXCEPTION_FLAG_FATAL,
1735 VmPtr
1736 );
1737 return EFI_UNSUPPORTED;
1738 }
1739
1740 VmPtr->R[OPERAND1_REGNUM (Operands)] = (VM_REGISTER) Op2;
1741 } else {
1742 //
1743 // Get the address = [Rx] + Index16
1744 // Write back the result. Always a natural size write, since
1745 // we're talking addresses here.
1746 //
1747 Op1 = (UINT64) VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16;
1748 VmWriteMemN (VmPtr, (UINTN) Op1, (UINTN) Op2);
1749 }
1750 //
1751 // Advance the instruction pointer
1752 //
1753 VmPtr->Ip += Size;
1754 return EFI_SUCCESS;
1755 }
1756
1757 STATIC
1758 EFI_STATUS
1759 ExecuteMOVsnw (
1760 IN VM_CONTEXT *VmPtr
1761 )
1762 /*++
1763
1764 Routine Description:
1765
1766 Execute the EBC MOVsnw instruction. This instruction loads a signed
1767 natural value from memory or register to another memory or register. On
1768 32-bit machines, the value gets sign-extended to 64 bits if the destination
1769 is a register.
1770
1771 Arguments:
1772
1773 VmPtr - pointer to a VM context
1774
1775 Returns:
1776
1777 Standard EFI_STATUS
1778
1779 Instruction syntax:
1780
1781 MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16}
1782
1783 0:7 1=>operand1 index present
1784 0:6 1=>operand2 index present
1785
1786 --*/
1787 {
1788 UINT8 Opcode;
1789 UINT8 Operands;
1790 UINT8 Size;
1791 INT16 Op1Index;
1792 INT16 Op2Index;
1793 UINT64 Op2;
1794
1795 //
1796 // Get the opcode and operand bytes
1797 //
1798 Opcode = GETOPCODE (VmPtr);
1799 Operands = GETOPERANDS (VmPtr);
1800
1801 Op1Index = Op2Index = 0;
1802
1803 //
1804 // Get the indexes if present.
1805 //
1806 Size = 2;
1807 if (Opcode & OPCODE_M_IMMED_OP1) {
1808 if (OPERAND1_INDIRECT (Operands)) {
1809 Op1Index = VmReadIndex16 (VmPtr, 2);
1810 } else {
1811 //
1812 // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2
1813 //
1814 EbcDebugSignalException (
1815 EXCEPT_EBC_INSTRUCTION_ENCODING,
1816 EXCEPTION_FLAG_FATAL,
1817 VmPtr
1818 );
1819 return EFI_UNSUPPORTED;
1820 }
1821
1822 Size += sizeof (UINT16);
1823 }
1824
1825 if (Opcode & OPCODE_M_IMMED_OP2) {
1826 if (OPERAND2_INDIRECT (Operands)) {
1827 Op2Index = VmReadIndex16 (VmPtr, Size);
1828 } else {
1829 Op2Index = VmReadImmed16 (VmPtr, Size);
1830 }
1831
1832 Size += sizeof (UINT16);
1833 }
1834 //
1835 // Get the data from the source.
1836 //
1837 Op2 = (INT64) ((INTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Op2Index));
1838 if (OPERAND2_INDIRECT (Operands)) {
1839 Op2 = (INT64) (INTN) VmReadMemN (VmPtr, (UINTN) Op2);
1840 }
1841 //
1842 // Now write back the result.
1843 //
1844 if (!OPERAND1_INDIRECT (Operands)) {
1845 VmPtr->R[OPERAND1_REGNUM (Operands)] = Op2;
1846 } else {
1847 VmWriteMemN (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
1848 }
1849 //
1850 // Advance the instruction pointer
1851 //
1852 VmPtr->Ip += Size;
1853 return EFI_SUCCESS;
1854 }
1855
1856 STATIC
1857 EFI_STATUS
1858 ExecuteMOVsnd (
1859 IN VM_CONTEXT *VmPtr
1860 )
1861 /*++
1862
1863 Routine Description:
1864
1865 Execute the EBC MOVsnw instruction. This instruction loads a signed
1866 natural value from memory or register to another memory or register. On
1867 32-bit machines, the value gets sign-extended to 64 bits if the destination
1868 is a register.
1869
1870 Arguments:
1871
1872 VmPtr - pointer to a VM context
1873
1874 Returns:
1875
1876 Standard EFI_STATUS
1877
1878 Instruction syntax:
1879
1880 MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32}
1881
1882 0:7 1=>operand1 index present
1883 0:6 1=>operand2 index present
1884
1885 --*/
1886 {
1887 UINT8 Opcode;
1888 UINT8 Operands;
1889 UINT8 Size;
1890 INT32 Op1Index;
1891 INT32 Op2Index;
1892 UINT64 Op2;
1893
1894 //
1895 // Get the opcode and operand bytes
1896 //
1897 Opcode = GETOPCODE (VmPtr);
1898 Operands = GETOPERANDS (VmPtr);
1899
1900 Op1Index = Op2Index = 0;
1901
1902 //
1903 // Get the indexes if present.
1904 //
1905 Size = 2;
1906 if (Opcode & OPCODE_M_IMMED_OP1) {
1907 if (OPERAND1_INDIRECT (Operands)) {
1908 Op1Index = VmReadIndex32 (VmPtr, 2);
1909 } else {
1910 //
1911 // Illegal form operand1 direct with index: MOVsnd R1 Index16,..
1912 //
1913 EbcDebugSignalException (
1914 EXCEPT_EBC_INSTRUCTION_ENCODING,
1915 EXCEPTION_FLAG_FATAL,
1916 VmPtr
1917 );
1918 return EFI_UNSUPPORTED;
1919 }
1920
1921 Size += sizeof (UINT32);
1922 }
1923
1924 if (Opcode & OPCODE_M_IMMED_OP2) {
1925 if (OPERAND2_INDIRECT (Operands)) {
1926 Op2Index = VmReadIndex32 (VmPtr, Size);
1927 } else {
1928 Op2Index = VmReadImmed32 (VmPtr, Size);
1929 }
1930
1931 Size += sizeof (UINT32);
1932 }
1933 //
1934 // Get the data from the source.
1935 //
1936 Op2 = (INT64) ((INTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Op2Index));
1937 if (OPERAND2_INDIRECT (Operands)) {
1938 Op2 = (INT64) (INTN) VmReadMemN (VmPtr, (UINTN) Op2);
1939 }
1940 //
1941 // Now write back the result.
1942 //
1943 if (!OPERAND1_INDIRECT (Operands)) {
1944 VmPtr->R[OPERAND1_REGNUM (Operands)] = Op2;
1945 } else {
1946 VmWriteMemN (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
1947 }
1948 //
1949 // Advance the instruction pointer
1950 //
1951 VmPtr->Ip += Size;
1952 return EFI_SUCCESS;
1953 }
1954
1955 STATIC
1956 EFI_STATUS
1957 ExecutePUSHn (
1958 IN VM_CONTEXT *VmPtr
1959 )
1960 /*++
1961
1962 Routine Description:
1963 Execute the EBC PUSHn instruction
1964
1965 Arguments:
1966 VmPtr - pointer to a VM context
1967
1968 Returns:
1969 Standard EFI_STATUS
1970
1971 Instruction syntax:
1972 PUSHn {@}R1 {Index16|Immed16}
1973
1974 --*/
1975 {
1976 UINT8 Opcode;
1977 UINT8 Operands;
1978 INT16 Index16;
1979 UINTN DataN;
1980
1981 //
1982 // Get opcode and operands
1983 //
1984 Opcode = GETOPCODE (VmPtr);
1985 Operands = GETOPERANDS (VmPtr);
1986
1987 //
1988 // Get index if present
1989 //
1990 if (Opcode & PUSHPOP_M_IMMDATA) {
1991 if (OPERAND1_INDIRECT (Operands)) {
1992 Index16 = VmReadIndex16 (VmPtr, 2);
1993 } else {
1994 Index16 = VmReadImmed16 (VmPtr, 2);
1995 }
1996
1997 VmPtr->Ip += 4;
1998 } else {
1999 Index16 = 0;
2000 VmPtr->Ip += 2;
2001 }
2002 //
2003 // Get the data to push
2004 //
2005 if (OPERAND1_INDIRECT (Operands)) {
2006 DataN = VmReadMemN (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16));
2007 } else {
2008 DataN = (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16);
2009 }
2010 //
2011 // Adjust the stack down.
2012 //
2013 VmPtr->R[0] -= sizeof (UINTN);
2014 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], DataN);
2015 return EFI_SUCCESS;
2016 }
2017
2018 STATIC
2019 EFI_STATUS
2020 ExecutePUSH (
2021 IN VM_CONTEXT *VmPtr
2022 )
2023 /*++
2024
2025 Routine Description:
2026 Execute the EBC PUSH instruction
2027
2028 Arguments:
2029 VmPtr - pointer to a VM context
2030
2031 Returns:
2032 Standard EFI_STATUS
2033
2034 Instruction syntax:
2035 PUSH[32|64] {@}R1 {Index16|Immed16}
2036
2037 --*/
2038 {
2039 UINT8 Opcode;
2040 UINT8 Operands;
2041 UINT32 Data32;
2042 UINT64 Data64;
2043 INT16 Index16;
2044
2045 //
2046 // Get opcode and operands
2047 //
2048 Opcode = GETOPCODE (VmPtr);
2049 Operands = GETOPERANDS (VmPtr);
2050 //
2051 // Get immediate index if present, then advance the IP.
2052 //
2053 if (Opcode & PUSHPOP_M_IMMDATA) {
2054 if (OPERAND1_INDIRECT (Operands)) {
2055 Index16 = VmReadIndex16 (VmPtr, 2);
2056 } else {
2057 Index16 = VmReadImmed16 (VmPtr, 2);
2058 }
2059
2060 VmPtr->Ip += 4;
2061 } else {
2062 Index16 = 0;
2063 VmPtr->Ip += 2;
2064 }
2065 //
2066 // Get the data to push
2067 //
2068 if (Opcode & PUSHPOP_M_64) {
2069 if (OPERAND1_INDIRECT (Operands)) {
2070 Data64 = VmReadMem64 (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16));
2071 } else {
2072 Data64 = (UINT64) VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16;
2073 }
2074 //
2075 // Adjust the stack down, then write back the data
2076 //
2077 VmPtr->R[0] -= sizeof (UINT64);
2078 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], Data64);
2079 } else {
2080 //
2081 // 32-bit data
2082 //
2083 if (OPERAND1_INDIRECT (Operands)) {
2084 Data32 = VmReadMem32 (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16));
2085 } else {
2086 Data32 = (UINT32) VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16;
2087 }
2088 //
2089 // Adjust the stack down and write the data
2090 //
2091 VmPtr->R[0] -= sizeof (UINT32);
2092 VmWriteMem32 (VmPtr, (UINTN) VmPtr->R[0], Data32);
2093 }
2094
2095 return EFI_SUCCESS;
2096 }
2097
2098 STATIC
2099 EFI_STATUS
2100 ExecutePOPn (
2101 IN VM_CONTEXT *VmPtr
2102 )
2103 /*++
2104
2105 Routine Description:
2106 Execute the EBC POPn instruction
2107
2108 Arguments:
2109 VmPtr - pointer to a VM context
2110
2111 Returns:
2112 Standard EFI_STATUS
2113
2114 Instruction syntax:
2115 POPn {@}R1 {Index16|Immed16}
2116
2117 --*/
2118 {
2119 UINT8 Opcode;
2120 UINT8 Operands;
2121 INT16 Index16;
2122 UINTN DataN;
2123
2124 //
2125 // Get opcode and operands
2126 //
2127 Opcode = GETOPCODE (VmPtr);
2128 Operands = GETOPERANDS (VmPtr);
2129 //
2130 // Get immediate data if present, and advance the IP
2131 //
2132 if (Opcode & PUSHPOP_M_IMMDATA) {
2133 if (OPERAND1_INDIRECT (Operands)) {
2134 Index16 = VmReadIndex16 (VmPtr, 2);
2135 } else {
2136 Index16 = VmReadImmed16 (VmPtr, 2);
2137 }
2138
2139 VmPtr->Ip += 4;
2140 } else {
2141 Index16 = 0;
2142 VmPtr->Ip += 2;
2143 }
2144 //
2145 // Read the data off the stack, then adjust the stack pointer
2146 //
2147 DataN = VmReadMemN (VmPtr, (UINTN) VmPtr->R[0]);
2148 VmPtr->R[0] += sizeof (UINTN);
2149 //
2150 // Do the write-back
2151 //
2152 if (OPERAND1_INDIRECT (Operands)) {
2153 VmWriteMemN (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16), DataN);
2154 } else {
2155 VmPtr->R[OPERAND1_REGNUM (Operands)] = (INT64) (UINT64) ((UINTN) DataN + Index16);
2156 }
2157
2158 return EFI_SUCCESS;
2159 }
2160
2161 STATIC
2162 EFI_STATUS
2163 ExecutePOP (
2164 IN VM_CONTEXT *VmPtr
2165 )
2166 /*++
2167
2168 Routine Description:
2169 Execute the EBC POP instruction
2170
2171 Arguments:
2172 VmPtr - pointer to a VM context
2173
2174 Returns:
2175 Standard EFI_STATUS
2176
2177 Instruction syntax:
2178 POP {@}R1 {Index16|Immed16}
2179
2180 --*/
2181 {
2182 UINT8 Opcode;
2183 UINT8 Operands;
2184 INT16 Index16;
2185 INT32 Data32;
2186 UINT64 Data64;
2187
2188 //
2189 // Get opcode and operands
2190 //
2191 Opcode = GETOPCODE (VmPtr);
2192 Operands = GETOPERANDS (VmPtr);
2193 //
2194 // Get immediate data if present, and advance the IP.
2195 //
2196 if (Opcode & PUSHPOP_M_IMMDATA) {
2197 if (OPERAND1_INDIRECT (Operands)) {
2198 Index16 = VmReadIndex16 (VmPtr, 2);
2199 } else {
2200 Index16 = VmReadImmed16 (VmPtr, 2);
2201 }
2202
2203 VmPtr->Ip += 4;
2204 } else {
2205 Index16 = 0;
2206 VmPtr->Ip += 2;
2207 }
2208 //
2209 // Get the data off the stack, then write it to the appropriate location
2210 //
2211 if (Opcode & PUSHPOP_M_64) {
2212 //
2213 // Read the data off the stack, then adjust the stack pointer
2214 //
2215 Data64 = VmReadMem64 (VmPtr, (UINTN) VmPtr->R[0]);
2216 VmPtr->R[0] += sizeof (UINT64);
2217 //
2218 // Do the write-back
2219 //
2220 if (OPERAND1_INDIRECT (Operands)) {
2221 VmWriteMem64 (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16), Data64);
2222 } else {
2223 VmPtr->R[OPERAND1_REGNUM (Operands)] = Data64 + Index16;
2224 }
2225 } else {
2226 //
2227 // 32-bit pop. Read it off the stack and adjust the stack pointer
2228 //
2229 Data32 = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->R[0]);
2230 VmPtr->R[0] += sizeof (UINT32);
2231 //
2232 // Do the write-back
2233 //
2234 if (OPERAND1_INDIRECT (Operands)) {
2235 VmWriteMem32 (VmPtr, (UINTN) (VmPtr->R[OPERAND1_REGNUM (Operands)] + Index16), Data32);
2236 } else {
2237 VmPtr->R[OPERAND1_REGNUM (Operands)] = (INT64) Data32 + Index16;
2238 }
2239 }
2240
2241 return EFI_SUCCESS;
2242 }
2243
2244 STATIC
2245 EFI_STATUS
2246 ExecuteCALL (
2247 IN VM_CONTEXT *VmPtr
2248 )
2249 /*++
2250
2251 Routine Description:
2252 Implements the EBC CALL instruction.
2253
2254 Instruction format:
2255
2256 CALL64 Immed64
2257 CALL32 {@}R1 {Immed32|Index32}
2258 CALLEX64 Immed64
2259 CALLEX16 {@}R1 {Immed32}
2260
2261 If Rx == R0, then it's a PC relative call to PC = PC + imm32.
2262
2263 Arguments:
2264 VmPtr - pointer to a VM context.
2265
2266 Returns:
2267 Standard EFI_STATUS
2268
2269 --*/
2270 {
2271 UINT8 Opcode;
2272 UINT8 Operands;
2273 INT32 Immed32;
2274 UINT8 Size;
2275 INT64 Immed64;
2276 VOID *FramePtr;
2277
2278 //
2279 // Get opcode and operands
2280 //
2281 Opcode = GETOPCODE (VmPtr);
2282 Operands = GETOPERANDS (VmPtr);
2283 //
2284 // Assign these as well to avoid compiler warnings
2285 //
2286 Immed64 = 0;
2287 Immed32 = 0;
2288
2289 FramePtr = VmPtr->FramePtr;
2290 //
2291 // Determine the instruction size, and get immediate data if present
2292 //
2293 if (Opcode & OPCODE_M_IMMDATA) {
2294 if (Opcode & OPCODE_M_IMMDATA64) {
2295 Immed64 = VmReadImmed64 (VmPtr, 2);
2296 Size = 10;
2297 } else {
2298 //
2299 // If register operand is indirect, then the immediate data is an index
2300 //
2301 if (OPERAND1_INDIRECT (Operands)) {
2302 Immed32 = VmReadIndex32 (VmPtr, 2);
2303 } else {
2304 Immed32 = VmReadImmed32 (VmPtr, 2);
2305 }
2306
2307 Size = 6;
2308 }
2309 } else {
2310 Size = 2;
2311 }
2312 //
2313 // If it's a call to EBC, adjust the stack pointer down 16 bytes and
2314 // put our return address and frame pointer on the VM stack.
2315 //
2316 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) {
2317 VmPtr->R[0] -= 8;
2318 VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
2319 VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
2320 VmPtr->R[0] -= 8;
2321 VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
2322 }
2323 //
2324 // If 64-bit data, then absolute jump only
2325 //
2326 if (Opcode & OPCODE_M_IMMDATA64) {
2327 //
2328 // Native or EBC call?
2329 //
2330 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) {
2331 VmPtr->Ip = (VMIP) (UINTN) Immed64;
2332 } else {
2333 //
2334 // Call external function, get the return value, and advance the IP
2335 //
2336 EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->R[0], FramePtr, Size);
2337 }
2338 } else {
2339 //
2340 // Get the register data. If operand1 == 0, then ignore register and
2341 // take immediate data as relative or absolute address.
2342 // Compiler should take care of upper bits if 32-bit machine.
2343 //
2344 if (OPERAND1_REGNUM (Operands) != 0) {
2345 Immed64 = (UINT64) (UINTN) VmPtr->R[OPERAND1_REGNUM (Operands)];
2346 }
2347 //
2348 // Get final address
2349 //
2350 if (OPERAND1_INDIRECT (Operands)) {
2351 Immed64 = (INT64) (UINT64) (UINTN) VmReadMemN (VmPtr, (UINTN) (Immed64 + Immed32));
2352 } else {
2353 Immed64 += Immed32;
2354 }
2355 //
2356 // Now determine if external call, and then if relative or absolute
2357 //
2358 if ((Operands & OPERAND_M_NATIVE_CALL) == 0) {
2359 //
2360 // EBC call. Relative or absolute? If relative, then it's relative to the
2361 // start of the next instruction.
2362 //
2363 if (Operands & OPERAND_M_RELATIVE_ADDR) {
2364 VmPtr->Ip += Immed64 + Size;
2365 } else {
2366 VmPtr->Ip = (VMIP) (UINTN) Immed64;
2367 }
2368 } else {
2369 //
2370 // Native call. Relative or absolute?
2371 //
2372 if (Operands & OPERAND_M_RELATIVE_ADDR) {
2373 EbcLLCALLEX (VmPtr, (UINTN) (Immed64 + VmPtr->Ip + Size), (UINTN) VmPtr->R[0], FramePtr, Size);
2374 } else {
2375 if (VmPtr->StopFlags & STOPFLAG_BREAK_ON_CALLEX) {
2376 CpuBreakpoint ();
2377 }
2378
2379 EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->R[0], FramePtr, Size);
2380 }
2381 }
2382 }
2383
2384 return EFI_SUCCESS;
2385 }
2386
2387 STATIC
2388 EFI_STATUS
2389 ExecuteRET (
2390 IN VM_CONTEXT *VmPtr
2391 )
2392 /*++
2393
2394 Routine Description:
2395 Execute the EBC RET instruction
2396
2397 Arguments:
2398 VmPtr - pointer to a VM context
2399
2400 Returns:
2401 Standard EFI_STATUS
2402
2403 Instruction syntax:
2404 RET
2405
2406 --*/
2407 {
2408 //
2409 // If we're at the top of the stack, then simply set the done
2410 // flag and return
2411 //
2412 if (VmPtr->StackRetAddr == (UINT64) VmPtr->R[0]) {
2413 VmPtr->StopFlags |= STOPFLAG_APP_DONE;
2414 } else {
2415 //
2416 // Pull the return address off the VM app's stack and set the IP
2417 // to it
2418 //
2419 if (!IS_ALIGNED ((UINTN) VmPtr->R[0], sizeof (UINT16))) {
2420 EbcDebugSignalException (
2421 EXCEPT_EBC_ALIGNMENT_CHECK,
2422 EXCEPTION_FLAG_FATAL,
2423 VmPtr
2424 );
2425 }
2426 //
2427 // Restore the IP and frame pointer from the stack
2428 //
2429 VmPtr->Ip = (VMIP) (UINTN) VmReadMem64 (VmPtr, (UINTN) VmPtr->R[0]);
2430 VmPtr->R[0] += 8;
2431 VmPtr->FramePtr = (VOID *) VmReadMemN (VmPtr, (UINTN) VmPtr->R[0]);
2432 VmPtr->R[0] += 8;
2433 }
2434
2435 return EFI_SUCCESS;
2436 }
2437
2438 STATIC
2439 EFI_STATUS
2440 ExecuteCMP (
2441 IN VM_CONTEXT *VmPtr
2442 )
2443 /*++
2444
2445 Routine Description:
2446 Execute the EBC CMP instruction
2447
2448 Arguments:
2449 VmPtr - pointer to a VM context
2450
2451 Returns:
2452 Standard EFI_STATUS
2453
2454 Instruction syntax:
2455 CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16}
2456
2457 --*/
2458 {
2459 UINT8 Opcode;
2460 UINT8 Operands;
2461 UINT8 Size;
2462 INT16 Index16;
2463 UINT32 Flag;
2464 INT64 Op2;
2465 INT64 Op1;
2466
2467 //
2468 // Get opcode and operands
2469 //
2470 Opcode = GETOPCODE (VmPtr);
2471 Operands = GETOPERANDS (VmPtr);
2472 //
2473 // Get the register data we're going to compare to
2474 //
2475 Op1 = VmPtr->R[OPERAND1_REGNUM (Operands)];
2476 //
2477 // Get immediate data
2478 //
2479 if (Opcode & OPCODE_M_IMMDATA) {
2480 if (OPERAND2_INDIRECT (Operands)) {
2481 Index16 = VmReadIndex16 (VmPtr, 2);
2482 } else {
2483 Index16 = VmReadImmed16 (VmPtr, 2);
2484 }
2485
2486 Size = 4;
2487 } else {
2488 Index16 = 0;
2489 Size = 2;
2490 }
2491 //
2492 // Now get Op2
2493 //
2494 if (OPERAND2_INDIRECT (Operands)) {
2495 if (Opcode & OPCODE_M_64BIT) {
2496 Op2 = (INT64) VmReadMem64 (VmPtr, (UINTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Index16));
2497 } else {
2498 //
2499 // 32-bit operations. 0-extend the values for all cases.
2500 //
2501 Op2 = (INT64) (UINT64) ((UINT32) VmReadMem32 (VmPtr, (UINTN) (VmPtr->R[OPERAND2_REGNUM (Operands)] + Index16)));
2502 }
2503 } else {
2504 Op2 = VmPtr->R[OPERAND2_REGNUM (Operands)] + Index16;
2505 }
2506 //
2507 // Now do the compare
2508 //
2509 Flag = 0;
2510 if (Opcode & OPCODE_M_64BIT) {
2511 //
2512 // 64-bit compares
2513 //
2514 switch (Opcode & OPCODE_M_OPCODE) {
2515 case OPCODE_CMPEQ:
2516 if (Op1 == Op2) {
2517 Flag = 1;
2518 }
2519 break;
2520
2521 case OPCODE_CMPLTE:
2522 if (Op1 <= Op2) {
2523 Flag = 1;
2524 }
2525 break;
2526
2527 case OPCODE_CMPGTE:
2528 if (Op1 >= Op2) {
2529 Flag = 1;
2530 }
2531 break;
2532
2533 case OPCODE_CMPULTE:
2534 if ((UINT64) Op1 <= (UINT64) Op2) {
2535 Flag = 1;
2536 }
2537 break;
2538
2539 case OPCODE_CMPUGTE:
2540 if ((UINT64) Op1 >= (UINT64) Op2) {
2541 Flag = 1;
2542 }
2543 break;
2544
2545 default:
2546 ASSERT (0);
2547 }
2548 } else {
2549 //
2550 // 32-bit compares
2551 //
2552 switch (Opcode & OPCODE_M_OPCODE) {
2553 case OPCODE_CMPEQ:
2554 if ((INT32) Op1 == (INT32) Op2) {
2555 Flag = 1;
2556 }
2557 break;
2558
2559 case OPCODE_CMPLTE:
2560 if ((INT32) Op1 <= (INT32) Op2) {
2561 Flag = 1;
2562 }
2563 break;
2564
2565 case OPCODE_CMPGTE:
2566 if ((INT32) Op1 >= (INT32) Op2) {
2567 Flag = 1;
2568 }
2569 break;
2570
2571 case OPCODE_CMPULTE:
2572 if ((UINT32) Op1 <= (UINT32) Op2) {
2573 Flag = 1;
2574 }
2575 break;
2576
2577 case OPCODE_CMPUGTE:
2578 if ((UINT32) Op1 >= (UINT32) Op2) {
2579 Flag = 1;
2580 }
2581 break;
2582
2583 default:
2584 ASSERT (0);
2585 }
2586 }
2587 //
2588 // Now set the flag accordingly for the comparison
2589 //
2590 if (Flag) {
2591 VMFLAG_SET (VmPtr, VMFLAGS_CC);
2592 } else {
2593 VMFLAG_CLEAR (VmPtr, VMFLAGS_CC);
2594 }
2595 //
2596 // Advance the IP
2597 //
2598 VmPtr->Ip += Size;
2599 return EFI_SUCCESS;
2600 }
2601
2602 STATIC
2603 EFI_STATUS
2604 ExecuteCMPI (
2605 IN VM_CONTEXT *VmPtr
2606 )
2607 /*++
2608
2609 Routine Description:
2610 Execute the EBC CMPI instruction
2611
2612 Arguments:
2613 VmPtr - pointer to a VM context
2614
2615 Returns:
2616 Standard EFI_STATUS
2617
2618 Instruction syntax:
2619 CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32
2620
2621 --*/
2622 {
2623 UINT8 Opcode;
2624 UINT8 Operands;
2625 UINT8 Size;
2626 INT64 Op1;
2627 INT64 Op2;
2628 INT16 Index16;
2629 UINT32 Flag;
2630
2631 //
2632 // Get opcode and operands
2633 //
2634 Opcode = GETOPCODE (VmPtr);
2635 Operands = GETOPERANDS (VmPtr);
2636
2637 //
2638 // Get operand1 index if present
2639 //
2640 Size = 2;
2641 if (Operands & OPERAND_M_CMPI_INDEX) {
2642 Index16 = VmReadIndex16 (VmPtr, 2);
2643 Size += 2;
2644 } else {
2645 Index16 = 0;
2646 }
2647 //
2648 // Get operand1 data we're going to compare to
2649 //
2650 Op1 = (INT64) VmPtr->R[OPERAND1_REGNUM (Operands)];
2651 if (OPERAND1_INDIRECT (Operands)) {
2652 //
2653 // Indirect operand1. Fetch 32 or 64-bit value based on compare size.
2654 //
2655 if (Opcode & OPCODE_M_CMPI64) {
2656 Op1 = (INT64) VmReadMem64 (VmPtr, (UINTN) Op1 + Index16);
2657 } else {
2658 Op1 = (INT64) VmReadMem32 (VmPtr, (UINTN) Op1 + Index16);
2659 }
2660 } else {
2661 //
2662 // Better not have been an index with direct. That is, CMPI R1 Index,...
2663 // is illegal.
2664 //
2665 if (Operands & OPERAND_M_CMPI_INDEX) {
2666 EbcDebugSignalException (
2667 EXCEPT_EBC_INSTRUCTION_ENCODING,
2668 EXCEPTION_FLAG_ERROR,
2669 VmPtr
2670 );
2671 VmPtr->Ip += Size;
2672 return EFI_UNSUPPORTED;
2673 }
2674 }
2675 //
2676 // Get immediate data -- 16- or 32-bit sign extended
2677 //
2678 if (Opcode & OPCODE_M_CMPI32_DATA) {
2679 Op2 = (INT64) VmReadImmed32 (VmPtr, Size);
2680 Size += 4;
2681 } else {
2682 //
2683 // 16-bit immediate data. Sign extend always.
2684 //
2685 Op2 = (INT64) ((INT16) VmReadImmed16 (VmPtr, Size));
2686 Size += 2;
2687 }
2688 //
2689 // Now do the compare
2690 //
2691 Flag = 0;
2692 if (Opcode & OPCODE_M_CMPI64) {
2693 //
2694 // 64 bit comparison
2695 //
2696 switch (Opcode & OPCODE_M_OPCODE) {
2697 case OPCODE_CMPIEQ:
2698 if (Op1 == (INT64) Op2) {
2699 Flag = 1;
2700 }
2701 break;
2702
2703 case OPCODE_CMPILTE:
2704 if (Op1 <= (INT64) Op2) {
2705 Flag = 1;
2706 }
2707 break;
2708
2709 case OPCODE_CMPIGTE:
2710 if (Op1 >= (INT64) Op2) {
2711 Flag = 1;
2712 }
2713 break;
2714
2715 case OPCODE_CMPIULTE:
2716 if ((UINT64) Op1 <= (UINT64) ((UINT32) Op2)) {
2717 Flag = 1;
2718 }
2719 break;
2720
2721 case OPCODE_CMPIUGTE:
2722 if ((UINT64) Op1 >= (UINT64) ((UINT32) Op2)) {
2723 Flag = 1;
2724 }
2725 break;
2726
2727 default:
2728 ASSERT (0);
2729 }
2730 } else {
2731 //
2732 // 32-bit comparisons
2733 //
2734 switch (Opcode & OPCODE_M_OPCODE) {
2735 case OPCODE_CMPIEQ:
2736 if ((INT32) Op1 == Op2) {
2737 Flag = 1;
2738 }
2739 break;
2740
2741 case OPCODE_CMPILTE:
2742 if ((INT32) Op1 <= Op2) {
2743 Flag = 1;
2744 }
2745 break;
2746
2747 case OPCODE_CMPIGTE:
2748 if ((INT32) Op1 >= Op2) {
2749 Flag = 1;
2750 }
2751 break;
2752
2753 case OPCODE_CMPIULTE:
2754 if ((UINT32) Op1 <= (UINT32) Op2) {
2755 Flag = 1;
2756 }
2757 break;
2758
2759 case OPCODE_CMPIUGTE:
2760 if ((UINT32) Op1 >= (UINT32) Op2) {
2761 Flag = 1;
2762 }
2763 break;
2764
2765 default:
2766 ASSERT (0);
2767 }
2768 }
2769 //
2770 // Now set the flag accordingly for the comparison
2771 //
2772 if (Flag) {
2773 VMFLAG_SET (VmPtr, VMFLAGS_CC);
2774 } else {
2775 VMFLAG_CLEAR (VmPtr, VMFLAGS_CC);
2776 }
2777 //
2778 // Advance the IP
2779 //
2780 VmPtr->Ip += Size;
2781 return EFI_SUCCESS;
2782 }
2783
2784 STATIC
2785 UINT64
2786 ExecuteNOT (
2787 IN VM_CONTEXT *VmPtr,
2788 IN UINT64 Op1,
2789 IN UINT64 Op2
2790 )
2791 /*++
2792
2793 Routine Description:
2794 Execute the EBC NOT instruction
2795
2796 Arguments:
2797 VmPtr - pointer to a VM context
2798 Op1 - Operand 1 from the instruction
2799 Op2 - Operand 2 from the instruction
2800
2801 Returns:
2802 ~Op2
2803
2804 Instruction syntax:
2805 NOT[32|64] {@}R1, {@}R2 {Index16|Immed16}
2806
2807 --*/
2808 {
2809 return ~Op2;
2810 }
2811
2812 STATIC
2813 UINT64
2814 ExecuteNEG (
2815 IN VM_CONTEXT *VmPtr,
2816 IN UINT64 Op1,
2817 IN UINT64 Op2
2818 )
2819 /*++
2820
2821 Routine Description:
2822 Execute the EBC NEG instruction
2823
2824 Arguments:
2825 VmPtr - pointer to a VM context
2826 Op1 - Operand 1 from the instruction
2827 Op2 - Operand 2 from the instruction
2828
2829 Returns:
2830 Op2 * -1
2831
2832 Instruction syntax:
2833 NEG[32|64] {@}R1, {@}R2 {Index16|Immed16}
2834
2835 --*/
2836 {
2837 return ~Op2 + 1;
2838 }
2839
2840 STATIC
2841 UINT64
2842 ExecuteADD (
2843 IN VM_CONTEXT *VmPtr,
2844 IN UINT64 Op1,
2845 IN UINT64 Op2
2846 )
2847 /*++
2848
2849 Routine Description:
2850
2851 Execute the EBC ADD instruction
2852
2853 Arguments:
2854 VmPtr - pointer to a VM context
2855 Op1 - Operand 1 from the instruction
2856 Op2 - Operand 2 from the instruction
2857
2858 Returns:
2859 Op1 + Op2
2860
2861 Instruction syntax:
2862 ADD[32|64] {@}R1, {@}R2 {Index16}
2863
2864 --*/
2865 {
2866 return Op1 + Op2;
2867 }
2868
2869 STATIC
2870 UINT64
2871 ExecuteSUB (
2872 IN VM_CONTEXT *VmPtr,
2873 IN UINT64 Op1,
2874 IN UINT64 Op2
2875 )
2876 /*++
2877
2878 Routine Description:
2879 Execute the EBC SUB instruction
2880
2881 Arguments:
2882 VmPtr - pointer to a VM context
2883 Op1 - Operand 1 from the instruction
2884 Op2 - Operand 2 from the instruction
2885
2886 Returns:
2887 Op1 - Op2
2888 Standard EFI_STATUS
2889
2890 Instruction syntax:
2891 SUB[32|64] {@}R1, {@}R2 {Index16|Immed16}
2892
2893 --*/
2894 {
2895 if (*VmPtr->Ip & DATAMANIP_M_64) {
2896 return (UINT64) ((INT64) ((INT64) Op1 - (INT64) Op2));
2897 } else {
2898 return (UINT64) ((INT64) ((INT32) Op1 - (INT32) Op2));
2899 }
2900 }
2901
2902 STATIC
2903 UINT64
2904 ExecuteMUL (
2905 IN VM_CONTEXT *VmPtr,
2906 IN UINT64 Op1,
2907 IN UINT64 Op2
2908 )
2909 /*++
2910
2911 Routine Description:
2912
2913 Execute the EBC MUL instruction
2914
2915 Arguments:
2916 VmPtr - pointer to a VM context
2917 Op1 - Operand 1 from the instruction
2918 Op2 - Operand 2 from the instruction
2919
2920 Returns:
2921 Op1 * Op2
2922
2923 Instruction syntax:
2924 MUL[32|64] {@}R1, {@}R2 {Index16|Immed16}
2925
2926 --*/
2927 {
2928 if (*VmPtr->Ip & DATAMANIP_M_64) {
2929 return MultS64x64 ((INT64)Op1, (INT64)Op2);
2930 } else {
2931 return (UINT64) ((INT64) ((INT32) Op1 * (INT32) Op2));
2932 }
2933 }
2934
2935 STATIC
2936 UINT64
2937 ExecuteMULU (
2938 IN VM_CONTEXT *VmPtr,
2939 IN UINT64 Op1,
2940 IN UINT64 Op2
2941 )
2942 /*++
2943
2944 Routine Description:
2945 Execute the EBC MULU instruction
2946
2947 Arguments:
2948 VmPtr - pointer to a VM context
2949 Op1 - Operand 1 from the instruction
2950 Op2 - Operand 2 from the instruction
2951
2952 Returns:
2953 (unsigned)Op1 * (unsigned)Op2
2954
2955 Instruction syntax:
2956 MULU[32|64] {@}R1, {@}R2 {Index16|Immed16}
2957
2958 --*/
2959 {
2960 if (*VmPtr->Ip & DATAMANIP_M_64) {
2961 return MultU64x64 (Op1, Op2);
2962 } else {
2963 return (UINT64) ((UINT32) Op1 * (UINT32) Op2);
2964 }
2965 }
2966
2967 STATIC
2968 UINT64
2969 ExecuteDIV (
2970 IN VM_CONTEXT *VmPtr,
2971 IN UINT64 Op1,
2972 IN UINT64 Op2
2973 )
2974 /*++
2975
2976 Routine Description:
2977
2978 Execute the EBC DIV instruction
2979
2980 Arguments:
2981 VmPtr - pointer to a VM context
2982 Op1 - Operand 1 from the instruction
2983 Op2 - Operand 2 from the instruction
2984
2985 Returns:
2986 Op1/Op2
2987
2988 Instruction syntax:
2989 DIV[32|64] {@}R1, {@}R2 {Index16|Immed16}
2990
2991 --*/
2992 {
2993 INT64 Remainder;
2994
2995 //
2996 // Check for divide-by-0
2997 //
2998 if (Op2 == 0) {
2999 EbcDebugSignalException (
3000 EXCEPT_EBC_DIVIDE_ERROR,
3001 EXCEPTION_FLAG_FATAL,
3002 VmPtr
3003 );
3004
3005 return 0;
3006 } else {
3007 if (*VmPtr->Ip & DATAMANIP_M_64) {
3008 return (UINT64) (DivS64x64Remainder (Op1, Op2, &Remainder));
3009 } else {
3010 return (UINT64) ((INT64) ((INT32) Op1 / (INT32) Op2));
3011 }
3012 }
3013 }
3014
3015 STATIC
3016 UINT64
3017 ExecuteDIVU (
3018 IN VM_CONTEXT *VmPtr,
3019 IN UINT64 Op1,
3020 IN UINT64 Op2
3021 )
3022 /*++
3023
3024 Routine Description:
3025 Execute the EBC DIVU instruction
3026
3027 Arguments:
3028 VmPtr - pointer to a VM context
3029 Op1 - Operand 1 from the instruction
3030 Op2 - Operand 2 from the instruction
3031
3032 Returns:
3033 (unsigned)Op1 / (unsigned)Op2
3034
3035 Instruction syntax:
3036 DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3037
3038 --*/
3039 {
3040 UINT64 Remainder;
3041
3042 //
3043 // Check for divide-by-0
3044 //
3045 if (Op2 == 0) {
3046 EbcDebugSignalException (
3047 EXCEPT_EBC_DIVIDE_ERROR,
3048 EXCEPTION_FLAG_FATAL,
3049 VmPtr
3050 );
3051 return 0;
3052 } else {
3053 //
3054 // Get the destination register
3055 //
3056 if (*VmPtr->Ip & DATAMANIP_M_64) {
3057 return (UINT64) (DivU64x64Remainder ((INT64)Op1, (INT64)Op2, &Remainder));
3058 } else {
3059 return (UINT64) ((UINT32) Op1 / (UINT32) Op2);
3060 }
3061 }
3062 }
3063
3064 STATIC
3065 UINT64
3066 ExecuteMOD (
3067 IN VM_CONTEXT *VmPtr,
3068 IN UINT64 Op1,
3069 IN UINT64 Op2
3070 )
3071 /*++
3072
3073 Routine Description:
3074 Execute the EBC MOD instruction
3075
3076 Arguments:
3077 VmPtr - pointer to a VM context
3078 Op1 - Operand 1 from the instruction
3079 Op2 - Operand 2 from the instruction
3080
3081 Returns:
3082 Op1 MODULUS Op2
3083
3084 Instruction syntax:
3085 MOD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3086
3087 --*/
3088 {
3089 INT64 Remainder;
3090
3091 //
3092 // Check for divide-by-0
3093 //
3094 if (Op2 == 0) {
3095 EbcDebugSignalException (
3096 EXCEPT_EBC_DIVIDE_ERROR,
3097 EXCEPTION_FLAG_FATAL,
3098 VmPtr
3099 );
3100 return 0;
3101 } else {
3102 DivS64x64Remainder ((INT64)Op1, (INT64)Op2, &Remainder);
3103 return Remainder;
3104 }
3105 }
3106
3107 STATIC
3108 UINT64
3109 ExecuteMODU (
3110 IN VM_CONTEXT *VmPtr,
3111 IN UINT64 Op1,
3112 IN UINT64 Op2
3113 )
3114 /*++
3115
3116 Routine Description:
3117 Execute the EBC MODU instruction
3118
3119 Arguments:
3120 VmPtr - pointer to a VM context
3121 Op1 - Operand 1 from the instruction
3122 Op2 - Operand 2 from the instruction
3123
3124 Returns:
3125 Op1 UNSIGNED_MODULUS Op2
3126
3127 Instruction syntax:
3128 MODU[32|64] {@}R1, {@}R2 {Index16|Immed16}
3129
3130 --*/
3131 {
3132 UINT64 Remainder;
3133
3134 //
3135 // Check for divide-by-0
3136 //
3137 if (Op2 == 0) {
3138 EbcDebugSignalException (
3139 EXCEPT_EBC_DIVIDE_ERROR,
3140 EXCEPTION_FLAG_FATAL,
3141 VmPtr
3142 );
3143 return 0;
3144 } else {
3145 DivU64x64Remainder (Op1, Op2, &Remainder);
3146 return Remainder;
3147 }
3148 }
3149
3150 STATIC
3151 UINT64
3152 ExecuteAND (
3153 IN VM_CONTEXT *VmPtr,
3154 IN UINT64 Op1,
3155 IN UINT64 Op2
3156 )
3157 /*++
3158
3159 Routine Description:
3160 Execute the EBC AND instruction
3161
3162 Arguments:
3163 VmPtr - pointer to a VM context
3164 Op1 - Operand 1 from the instruction
3165 Op2 - Operand 2 from the instruction
3166
3167 Returns:
3168 Op1 AND Op2
3169
3170 Instruction syntax:
3171 AND[32|64] {@}R1, {@}R2 {Index16|Immed16}
3172
3173 --*/
3174 {
3175 return Op1 & Op2;
3176 }
3177
3178 STATIC
3179 UINT64
3180 ExecuteOR (
3181 IN VM_CONTEXT *VmPtr,
3182 IN UINT64 Op1,
3183 IN UINT64 Op2
3184 )
3185 /*++
3186
3187 Routine Description:
3188 Execute the EBC OR instruction
3189
3190 Arguments:
3191 VmPtr - pointer to a VM context
3192 Op1 - Operand 1 from the instruction
3193 Op2 - Operand 2 from the instruction
3194
3195 Returns:
3196 Op1 OR Op2
3197
3198 Instruction syntax:
3199 OR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3200
3201 --*/
3202 {
3203 return Op1 | Op2;
3204 }
3205
3206 STATIC
3207 UINT64
3208 ExecuteXOR (
3209 IN VM_CONTEXT *VmPtr,
3210 IN UINT64 Op1,
3211 IN UINT64 Op2
3212 )
3213 /*++
3214
3215 Routine Description:
3216 Execute the EBC XOR instruction
3217
3218 Arguments:
3219 VmPtr - pointer to a VM context
3220 Op1 - Operand 1 from the instruction
3221 Op2 - Operand 2 from the instruction
3222
3223 Returns:
3224 Op1 XOR Op2
3225
3226 Instruction syntax:
3227 XOR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3228
3229 --*/
3230 {
3231 return Op1 ^ Op2;
3232 }
3233
3234 STATIC
3235 UINT64
3236 ExecuteSHL (
3237 IN VM_CONTEXT *VmPtr,
3238 IN UINT64 Op1,
3239 IN UINT64 Op2
3240 )
3241 /*++
3242
3243 Routine Description:
3244
3245 Execute the EBC SHL shift left instruction
3246
3247 Arguments:
3248 VmPtr - pointer to a VM context
3249 Op1 - Operand 1 from the instruction
3250 Op2 - Operand 2 from the instruction
3251
3252 Returns:
3253 Op1 << Op2
3254
3255 Instruction syntax:
3256 SHL[32|64] {@}R1, {@}R2 {Index16|Immed16}
3257
3258 --*/
3259 {
3260 if (*VmPtr->Ip & DATAMANIP_M_64) {
3261 return LShiftU64 (Op1, (UINTN)Op2);
3262 } else {
3263 return (UINT64) ((UINT32) ((UINT32) Op1 << (UINT32) Op2));
3264 }
3265 }
3266
3267 STATIC
3268 UINT64
3269 ExecuteSHR (
3270 IN VM_CONTEXT *VmPtr,
3271 IN UINT64 Op1,
3272 IN UINT64 Op2
3273 )
3274 /*++
3275
3276 Routine Description:
3277 Execute the EBC SHR instruction
3278
3279 Arguments:
3280 VmPtr - pointer to a VM context
3281 Op1 - Operand 1 from the instruction
3282 Op2 - Operand 2 from the instruction
3283
3284 Returns:
3285 Op1 >> Op2 (unsigned operands)
3286
3287 Instruction syntax:
3288 SHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3289
3290 --*/
3291 {
3292 if (*VmPtr->Ip & DATAMANIP_M_64) {
3293 return RShiftU64 (Op1, (UINTN)Op2);
3294 } else {
3295 return (UINT64) ((UINT32) Op1 >> (UINT32) Op2);
3296 }
3297 }
3298
3299 STATIC
3300 UINT64
3301 ExecuteASHR (
3302 IN VM_CONTEXT *VmPtr,
3303 IN UINT64 Op1,
3304 IN UINT64 Op2
3305 )
3306 /*++
3307
3308 Routine Description:
3309 Execute the EBC ASHR instruction
3310
3311 Arguments:
3312 VmPtr - pointer to a VM context
3313 Op1 - Operand 1 from the instruction
3314 Op2 - Operand 2 from the instruction
3315
3316 Returns:
3317 Op1 >> Op2 (signed)
3318
3319 Instruction syntax:
3320 ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16}
3321
3322 --*/
3323 {
3324 if (*VmPtr->Ip & DATAMANIP_M_64) {
3325 return ARShiftU64 (Op1, (UINTN)Op2);
3326 } else {
3327 return (UINT64) ((INT64) ((INT32) Op1 >> (UINT32) Op2));
3328 }
3329 }
3330
3331 STATIC
3332 UINT64
3333 ExecuteEXTNDB (
3334 IN VM_CONTEXT *VmPtr,
3335 IN UINT64 Op1,
3336 IN UINT64 Op2
3337 )
3338 /*++
3339
3340 Routine Description:
3341 Execute the EBC EXTNDB instruction to sign-extend a byte value.
3342
3343 Arguments:
3344 VmPtr - pointer to a VM context
3345 Op1 - Operand 1 from the instruction
3346 Op2 - Operand 2 from the instruction
3347
3348 Returns:
3349 (INT64)(INT8)Op2
3350
3351 Instruction syntax:
3352 EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16}
3353
3354
3355 --*/
3356 {
3357 INT8 Data8;
3358 INT64 Data64;
3359 //
3360 // Convert to byte, then return as 64-bit signed value to let compiler
3361 // sign-extend the value
3362 //
3363 Data8 = (INT8) Op2;
3364 Data64 = (INT64) Data8;
3365
3366 return (UINT64) Data64;
3367 }
3368
3369 STATIC
3370 UINT64
3371 ExecuteEXTNDW (
3372 IN VM_CONTEXT *VmPtr,
3373 IN UINT64 Op1,
3374 IN UINT64 Op2
3375 )
3376 /*++
3377
3378 Routine Description:
3379 Execute the EBC EXTNDW instruction to sign-extend a 16-bit value.
3380
3381 Arguments:
3382 VmPtr - pointer to a VM context
3383 Op1 - Operand 1 from the instruction
3384 Op2 - Operand 2 from the instruction
3385
3386 Returns:
3387 (INT64)(INT16)Op2
3388
3389 Instruction syntax:
3390 EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16}
3391
3392
3393 --*/
3394 {
3395 INT16 Data16;
3396 INT64 Data64;
3397 //
3398 // Convert to word, then return as 64-bit signed value to let compiler
3399 // sign-extend the value
3400 //
3401 Data16 = (INT16) Op2;
3402 Data64 = (INT64) Data16;
3403
3404 return (UINT64) Data64;
3405 }
3406 //
3407 // Execute the EBC EXTNDD instruction.
3408 //
3409 // Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16]
3410 // EXTNDD Dest, Source
3411 //
3412 // Operation: Dest <- SignExtended((DWORD)Source))
3413 //
3414 STATIC
3415 UINT64
3416 ExecuteEXTNDD (
3417 IN VM_CONTEXT *VmPtr,
3418 IN UINT64 Op1,
3419 IN UINT64 Op2
3420 )
3421 /*++
3422
3423 Routine Description:
3424 Execute the EBC EXTNDD instruction to sign-extend a 32-bit value.
3425
3426 Arguments:
3427 VmPtr - pointer to a VM context
3428 Op1 - Operand 1 from the instruction
3429 Op2 - Operand 2 from the instruction
3430
3431 Returns:
3432 (INT64)(INT32)Op2
3433
3434 Instruction syntax:
3435 EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16}
3436
3437
3438 --*/
3439 {
3440 INT32 Data32;
3441 INT64 Data64;
3442 //
3443 // Convert to 32-bit value, then return as 64-bit signed value to let compiler
3444 // sign-extend the value
3445 //
3446 Data32 = (INT32) Op2;
3447 Data64 = (INT64) Data32;
3448
3449 return (UINT64) Data64;
3450 }
3451
3452 STATIC
3453 EFI_STATUS
3454 ExecuteSignedDataManip (
3455 IN VM_CONTEXT *VmPtr
3456 )
3457 {
3458 //
3459 // Just call the data manipulation function with a flag indicating this
3460 // is a signed operation.
3461 //
3462 return ExecuteDataManip (VmPtr, TRUE);
3463 }
3464
3465 STATIC
3466 EFI_STATUS
3467 ExecuteUnsignedDataManip (
3468 IN VM_CONTEXT *VmPtr
3469 )
3470 {
3471 //
3472 // Just call the data manipulation function with a flag indicating this
3473 // is not a signed operation.
3474 //
3475 return ExecuteDataManip (VmPtr, FALSE);
3476 }
3477
3478 STATIC
3479 EFI_STATUS
3480 ExecuteDataManip (
3481 IN VM_CONTEXT *VmPtr,
3482 IN BOOLEAN IsSignedOp
3483 )
3484 /*++
3485
3486 Routine Description:
3487 Execute all the EBC data manipulation instructions.
3488 Since the EBC data manipulation instructions all have the same basic form,
3489 they can share the code that does the fetch of operands and the write-back
3490 of the result. This function performs the fetch of the operands (even if
3491 both are not needed to be fetched, like NOT instruction), dispatches to the
3492 appropriate subfunction, then writes back the returned result.
3493
3494 Arguments:
3495 VmPtr - pointer to VM context
3496
3497 Returns:
3498 Standard EBC status
3499
3500 Format:
3501 INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16}
3502
3503 --*/
3504 {
3505 UINT8 Opcode;
3506 INT16 Index16;
3507 UINT8 Operands;
3508 UINT8 Size;
3509 UINT64 Op1;
3510 UINT64 Op2;
3511
3512 //
3513 // Get opcode and operands
3514 //
3515 Opcode = GETOPCODE (VmPtr);
3516 Operands = GETOPERANDS (VmPtr);
3517
3518 //
3519 // Determine if we have immediate data by the opcode
3520 //
3521 if (Opcode & DATAMANIP_M_IMMDATA) {
3522 //
3523 // Index16 if Ry is indirect, or Immed16 if Ry direct.
3524 //
3525 if (OPERAND2_INDIRECT (Operands)) {
3526 Index16 = VmReadIndex16 (VmPtr, 2);
3527 } else {
3528 Index16 = VmReadImmed16 (VmPtr, 2);
3529 }
3530
3531 Size = 4;
3532 } else {
3533 Index16 = 0;
3534 Size = 2;
3535 }
3536 //
3537 // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16}
3538 //
3539 Op2 = (UINT64) VmPtr->R[OPERAND2_REGNUM (Operands)] + Index16;
3540 if (OPERAND2_INDIRECT (Operands)) {
3541 //
3542 // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data
3543 //
3544 if (Opcode & DATAMANIP_M_64) {
3545 Op2 = VmReadMem64 (VmPtr, (UINTN) Op2);
3546 } else {
3547 //
3548 // Read as signed value where appropriate.
3549 //
3550 if (IsSignedOp) {
3551 Op2 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op2));
3552 } else {
3553 Op2 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op2);
3554 }
3555 }
3556 } else {
3557 if ((Opcode & DATAMANIP_M_64) == 0) {
3558 if (IsSignedOp) {
3559 Op2 = (UINT64) (INT64) ((INT32) Op2);
3560 } else {
3561 Op2 = (UINT64) ((UINT32) Op2);
3562 }
3563 }
3564 }
3565 //
3566 // Get operand1 (destination and sometimes also an actual operand)
3567 // of form {@}R1
3568 //
3569 Op1 = VmPtr->R[OPERAND1_REGNUM (Operands)];
3570 if (OPERAND1_INDIRECT (Operands)) {
3571 if (Opcode & DATAMANIP_M_64) {
3572 Op1 = VmReadMem64 (VmPtr, (UINTN) Op1);
3573 } else {
3574 if (IsSignedOp) {
3575 Op1 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op1));
3576 } else {
3577 Op1 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op1);
3578 }
3579 }
3580 } else {
3581 if ((Opcode & DATAMANIP_M_64) == 0) {
3582 if (IsSignedOp) {
3583 Op1 = (UINT64) (INT64) ((INT32) Op1);
3584 } else {
3585 Op1 = (UINT64) ((UINT32) Op1);
3586 }
3587 }
3588 }
3589 //
3590 // Dispatch to the computation function
3591 //
3592 if (((Opcode & OPCODE_M_OPCODE) - OPCODE_NOT) >=
3593 (sizeof (mDataManipDispatchTable) / sizeof (mDataManipDispatchTable[0]))
3594 ) {
3595 EbcDebugSignalException (
3596 EXCEPT_EBC_INVALID_OPCODE,
3597 EXCEPTION_FLAG_ERROR,
3598 VmPtr
3599 );
3600 //
3601 // Advance and return
3602 //
3603 VmPtr->Ip += Size;
3604 return EFI_UNSUPPORTED;
3605 } else {
3606 Op2 = mDataManipDispatchTable[(Opcode & OPCODE_M_OPCODE) - OPCODE_NOT](VmPtr, Op1, Op2);
3607 }
3608 //
3609 // Write back the result.
3610 //
3611 if (OPERAND1_INDIRECT (Operands)) {
3612 Op1 = VmPtr->R[OPERAND1_REGNUM (Operands)];
3613 if (Opcode & DATAMANIP_M_64) {
3614 VmWriteMem64 (VmPtr, (UINTN) Op1, Op2);
3615 } else {
3616 VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) Op2);
3617 }
3618 } else {
3619 //
3620 // Storage back to a register. Write back, clearing upper bits (as per
3621 // the specification) if 32-bit operation.
3622 //
3623 VmPtr->R[OPERAND1_REGNUM (Operands)] = Op2;
3624 if ((Opcode & DATAMANIP_M_64) == 0) {
3625 VmPtr->R[OPERAND1_REGNUM (Operands)] &= 0xFFFFFFFF;
3626 }
3627 }
3628 //
3629 // Advance the instruction pointer
3630 //
3631 VmPtr->Ip += Size;
3632 return EFI_SUCCESS;
3633 }
3634
3635 STATIC
3636 EFI_STATUS
3637 ExecuteLOADSP (
3638 IN VM_CONTEXT *VmPtr
3639 )
3640 /*++
3641
3642 Routine Description:
3643 Execute the EBC LOADSP instruction
3644
3645 Arguments:
3646 VmPtr - pointer to a VM context
3647
3648 Returns:
3649 Standard EFI_STATUS
3650
3651 Instruction syntax:
3652 LOADSP SP1, R2
3653
3654 --*/
3655 {
3656 UINT8 Operands;
3657
3658 //
3659 // Get the operands
3660 //
3661 Operands = GETOPERANDS (VmPtr);
3662
3663 //
3664 // Do the operation
3665 //
3666 switch (OPERAND1_REGNUM (Operands)) {
3667 //
3668 // Set flags
3669 //
3670 case 0:
3671 //
3672 // Spec states that this instruction will not modify reserved bits in
3673 // the flags register.
3674 //
3675 VmPtr->Flags = (VmPtr->Flags &~VMFLAGS_ALL_VALID) | (VmPtr->R[OPERAND2_REGNUM (Operands)] & VMFLAGS_ALL_VALID);
3676 break;
3677
3678 default:
3679 EbcDebugSignalException (
3680 EXCEPT_EBC_INSTRUCTION_ENCODING,
3681 EXCEPTION_FLAG_WARNING,
3682 VmPtr
3683 );
3684 VmPtr->Ip += 2;
3685 return EFI_UNSUPPORTED;
3686 }
3687
3688 VmPtr->Ip += 2;
3689 return EFI_SUCCESS;
3690 }
3691
3692 STATIC
3693 EFI_STATUS
3694 ExecuteSTORESP (
3695 IN VM_CONTEXT *VmPtr
3696 )
3697 /*++
3698
3699 Routine Description:
3700 Execute the EBC STORESP instruction
3701
3702 Arguments:
3703 VmPtr - pointer to a VM context
3704
3705 Returns:
3706 Standard EFI_STATUS
3707
3708 Instruction syntax:
3709 STORESP Rx, FLAGS|IP
3710
3711 --*/
3712 {
3713 UINT8 Operands;
3714
3715 //
3716 // Get the operands
3717 //
3718 Operands = GETOPERANDS (VmPtr);
3719
3720 //
3721 // Do the operation
3722 //
3723 switch (OPERAND2_REGNUM (Operands)) {
3724 //
3725 // Get flags
3726 //
3727 case 0:
3728 //
3729 // Retrieve the value in the flags register, then clear reserved bits
3730 //
3731 VmPtr->R[OPERAND1_REGNUM (Operands)] = (UINT64) (VmPtr->Flags & VMFLAGS_ALL_VALID);
3732 break;
3733
3734 //
3735 // Get IP -- address of following instruction
3736 //
3737 case 1:
3738 VmPtr->R[OPERAND1_REGNUM (Operands)] = (UINT64) (UINTN) VmPtr->Ip + 2;
3739 break;
3740
3741 default:
3742 EbcDebugSignalException (
3743 EXCEPT_EBC_INSTRUCTION_ENCODING,
3744 EXCEPTION_FLAG_WARNING,
3745 VmPtr
3746 );
3747 VmPtr->Ip += 2;
3748 return EFI_UNSUPPORTED;
3749 break;
3750 }
3751
3752 VmPtr->Ip += 2;
3753 return EFI_SUCCESS;
3754 }
3755
3756 STATIC
3757 INT16
3758 VmReadIndex16 (
3759 IN VM_CONTEXT *VmPtr,
3760 IN UINT32 CodeOffset
3761 )
3762 /*++
3763
3764 Routine Description:
3765 Decode a 16-bit index to determine the offset. Given an index value:
3766
3767 b15 - sign bit
3768 b14:12 - number of bits in this index assigned to natural units (=a)
3769 ba:11 - constant units = C
3770 b0:a - natural units = N
3771
3772 Given this info, the offset can be computed by:
3773 offset = sign_bit * (C + N * sizeof(UINTN))
3774
3775 Max offset is achieved with index = 0x7FFF giving an offset of
3776 0x27B (32-bit machine) or 0x477 (64-bit machine).
3777 Min offset is achieved with index =
3778
3779 Arguments:
3780 VmPtr - pointer to VM context
3781 CodeOffset - offset from IP of the location of the 16-bit index to decode
3782
3783 Returns:
3784 The decoded offset.
3785
3786 --*/
3787 {
3788 UINT16 Index;
3789 INT16 Offset;
3790 INT16 C;
3791 INT16 N;
3792 INT16 NBits;
3793 INT16 Mask;
3794
3795 //
3796 // First read the index from the code stream
3797 //
3798 Index = VmReadCode16 (VmPtr, CodeOffset);
3799
3800 //
3801 // Get the mask for N. First get the number of bits from the index.
3802 //
3803 NBits = (INT16) ((Index & 0x7000) >> 12);
3804
3805 //
3806 // Scale it for 16-bit indexes
3807 //
3808 NBits *= 2;
3809
3810 //
3811 // Now using the number of bits, create a mask.
3812 //
3813 Mask = (INT16) ((INT16)~0 << NBits);
3814
3815 //
3816 // Now using the mask, extract N from the lower bits of the index.
3817 //
3818 N = (INT16) (Index &~Mask);
3819
3820 //
3821 // Now compute C
3822 //
3823 C = (INT16) (((Index &~0xF000) & Mask) >> NBits);
3824
3825 Offset = (INT16) (N * sizeof (UINTN) + C);
3826
3827 //
3828 // Now set the sign
3829 //
3830 if (Index & 0x8000) {
3831 //
3832 // Do it the hard way to work around a bogus compiler warning
3833 //
3834 // Offset = -1 * Offset;
3835 //
3836 Offset = (INT16) ((INT32) Offset * -1);
3837 }
3838
3839 return Offset;
3840 }
3841
3842 STATIC
3843 INT32
3844 VmReadIndex32 (
3845 IN VM_CONTEXT *VmPtr,
3846 IN UINT32 CodeOffset
3847 )
3848 /*++
3849
3850 Routine Description:
3851 Decode a 32-bit index to determine the offset.
3852
3853 Arguments:
3854 VmPtr - pointer to VM context
3855 CodeOffset - offset from IP of the location of the 32-bit index to decode
3856
3857 Returns:
3858 Converted index per EBC VM specification
3859
3860 --*/
3861 {
3862 UINT32 Index;
3863 INT32 Offset;
3864 INT32 C;
3865 INT32 N;
3866 INT32 NBits;
3867 INT32 Mask;
3868
3869 Index = VmReadImmed32 (VmPtr, CodeOffset);
3870
3871 //
3872 // Get the mask for N. First get the number of bits from the index.
3873 //
3874 NBits = (Index & 0x70000000) >> 28;
3875
3876 //
3877 // Scale it for 32-bit indexes
3878 //
3879 NBits *= 4;
3880
3881 //
3882 // Now using the number of bits, create a mask.
3883 //
3884 Mask = (INT32)~0 << NBits;
3885
3886 //
3887 // Now using the mask, extract N from the lower bits of the index.
3888 //
3889 N = Index &~Mask;
3890
3891 //
3892 // Now compute C
3893 //
3894 C = ((Index &~0xF0000000) & Mask) >> NBits;
3895
3896 Offset = N * sizeof (UINTN) + C;
3897
3898 //
3899 // Now set the sign
3900 //
3901 if (Index & 0x80000000) {
3902 Offset = Offset * -1;
3903 }
3904
3905 return Offset;
3906 }
3907
3908 STATIC
3909 INT64
3910 VmReadIndex64 (
3911 IN VM_CONTEXT *VmPtr,
3912 IN UINT32 CodeOffset
3913 )
3914 /*++
3915
3916 Routine Description:
3917 Decode a 64-bit index to determine the offset.
3918
3919 Arguments:
3920 VmPtr - pointer to VM context
3921 CodeOffset - offset from IP of the location of the 64-bit index to decode
3922
3923 Returns:
3924 Converted index per EBC VM specification
3925
3926 --*/
3927 {
3928 UINT64 Index;
3929 INT64 Offset;
3930 INT64 C;
3931 INT64 N;
3932 INT64 NBits;
3933 INT64 Mask;
3934
3935 Index = VmReadCode64 (VmPtr, CodeOffset);
3936
3937 //
3938 // Get the mask for N. First get the number of bits from the index.
3939 //
3940 NBits = RShiftU64 ((Index & 0x7000000000000000ULL), 60);
3941
3942 //
3943 // Scale it for 64-bit indexes (multiply by 8 by shifting left 3)
3944 //
3945 NBits = LShiftU64 ((UINT64)NBits, 3);
3946
3947 //
3948 // Now using the number of bits, create a mask.
3949 //
3950 Mask = (LShiftU64 ((UINT64)~0, (UINTN)NBits));
3951
3952 //
3953 // Now using the mask, extract N from the lower bits of the index.
3954 //
3955 N = Index &~Mask;
3956
3957 //
3958 // Now compute C
3959 //
3960 C = ARShiftU64 (((Index &~0xF000000000000000ULL) & Mask), (UINTN)NBits);
3961
3962 Offset = MultU64x64 (N, sizeof (UINTN)) + C;
3963
3964 //
3965 // Now set the sign
3966 //
3967 if (Index & 0x8000000000000000ULL) {
3968 Offset = MultS64x64 (Offset, -1);
3969 }
3970
3971 return Offset;
3972 }
3973
3974 STATIC
3975 EFI_STATUS
3976 VmWriteMem8 (
3977 IN VM_CONTEXT *VmPtr,
3978 IN UINTN Addr,
3979 IN UINT8 Data
3980 )
3981 /*++
3982
3983 Routine Description:
3984 The following VmWriteMem? routines are called by the EBC data
3985 movement instructions that write to memory. Since these writes
3986 may be to the stack, which looks like (high address on top) this,
3987
3988 [EBC entry point arguments]
3989 [VM stack]
3990 [EBC stack]
3991
3992 we need to detect all attempts to write to the EBC entry point argument
3993 stack area and adjust the address (which will initially point into the
3994 VM stack) to point into the EBC entry point arguments.
3995
3996 Arguments:
3997 VmPtr - pointer to a VM context
3998 Addr - adddress to write to
3999 Data - value to write to Addr
4000
4001 Returns:
4002 Standard EFI_STATUS
4003
4004 --*/
4005 {
4006 //
4007 // Convert the address if it's in the stack gap
4008 //
4009 Addr = ConvertStackAddr (VmPtr, Addr);
4010 *(UINT8 *) Addr = Data;
4011 return EFI_SUCCESS;
4012 }
4013
4014 STATIC
4015 EFI_STATUS
4016 VmWriteMem16 (
4017 IN VM_CONTEXT *VmPtr,
4018 IN UINTN Addr,
4019 IN UINT16 Data
4020 )
4021 {
4022 EFI_STATUS Status;
4023
4024 //
4025 // Convert the address if it's in the stack gap
4026 //
4027 Addr = ConvertStackAddr (VmPtr, Addr);
4028
4029 //
4030 // Do a simple write if aligned
4031 //
4032 if (IS_ALIGNED (Addr, sizeof (UINT16))) {
4033 *(UINT16 *) Addr = Data;
4034 } else {
4035 //
4036 // Write as two bytes
4037 //
4038 MemoryFence ();
4039 if ((Status = VmWriteMem8 (VmPtr, Addr, (UINT8) Data)) != EFI_SUCCESS) {
4040 return Status;
4041 }
4042
4043 MemoryFence ();
4044 if ((Status = VmWriteMem8 (VmPtr, Addr + 1, (UINT8) (Data >> 8))) != EFI_SUCCESS) {
4045 return Status;
4046 }
4047
4048 MemoryFence ();
4049 }
4050
4051 return EFI_SUCCESS;
4052 }
4053
4054 STATIC
4055 EFI_STATUS
4056 VmWriteMem32 (
4057 IN VM_CONTEXT *VmPtr,
4058 IN UINTN Addr,
4059 IN UINT32 Data
4060 )
4061 {
4062 EFI_STATUS Status;
4063
4064 //
4065 // Convert the address if it's in the stack gap
4066 //
4067 Addr = ConvertStackAddr (VmPtr, Addr);
4068
4069 //
4070 // Do a simple write if aligned
4071 //
4072 if (IS_ALIGNED (Addr, sizeof (UINT32))) {
4073 *(UINT32 *) Addr = Data;
4074 } else {
4075 //
4076 // Write as two words
4077 //
4078 MemoryFence ();
4079 if ((Status = VmWriteMem16 (VmPtr, Addr, (UINT16) Data)) != EFI_SUCCESS) {
4080 return Status;
4081 }
4082
4083 MemoryFence ();
4084 if ((Status = VmWriteMem16 (VmPtr, Addr + sizeof (UINT16), (UINT16) (Data >> 16))) != EFI_SUCCESS) {
4085 return Status;
4086 }
4087
4088 MemoryFence ();
4089 }
4090
4091 return EFI_SUCCESS;
4092 }
4093
4094 EFI_STATUS
4095 VmWriteMem64 (
4096 IN VM_CONTEXT *VmPtr,
4097 IN UINTN Addr,
4098 IN UINT64 Data
4099 )
4100 {
4101 EFI_STATUS Status;
4102 UINT32 Data32;
4103
4104 //
4105 // Convert the address if it's in the stack gap
4106 //
4107 Addr = ConvertStackAddr (VmPtr, Addr);
4108
4109 //
4110 // Do a simple write if aligned
4111 //
4112 if (IS_ALIGNED (Addr, sizeof (UINT64))) {
4113 *(UINT64 *) Addr = Data;
4114 } else {
4115 //
4116 // Write as two 32-bit words
4117 //
4118 MemoryFence ();
4119 if ((Status = VmWriteMem32 (VmPtr, Addr, (UINT32) Data)) != EFI_SUCCESS) {
4120 return Status;
4121 }
4122
4123 MemoryFence ();
4124 Data32 = (UINT32) (((UINT32 *) &Data)[1]);
4125 if ((Status = VmWriteMem32 (VmPtr, Addr + sizeof (UINT32), Data32)) != EFI_SUCCESS) {
4126 return Status;
4127 }
4128
4129 MemoryFence ();
4130 }
4131
4132 return EFI_SUCCESS;
4133 }
4134
4135 EFI_STATUS
4136 VmWriteMemN (
4137 IN VM_CONTEXT *VmPtr,
4138 IN UINTN Addr,
4139 IN UINTN Data
4140 )
4141 {
4142 EFI_STATUS Status;
4143 UINTN Index;
4144
4145 Status = EFI_SUCCESS;
4146
4147 //
4148 // Convert the address if it's in the stack gap
4149 //
4150 Addr = ConvertStackAddr (VmPtr, Addr);
4151
4152 //
4153 // Do a simple write if aligned
4154 //
4155 if (IS_ALIGNED (Addr, sizeof (UINTN))) {
4156 *(UINTN *) Addr = Data;
4157 } else {
4158 for (Index = 0; Index < sizeof (UINTN) / sizeof (UINT32); Index++) {
4159 MemoryFence ();
4160 Status = VmWriteMem32 (VmPtr, Addr + Index * sizeof (UINT32), (UINT32) Data);
4161 MemoryFence ();
4162 Data = (UINTN)RShiftU64 ((UINT64)Data, 32);
4163 }
4164 }
4165
4166 return Status;
4167 }
4168
4169 STATIC
4170 INT8
4171 VmReadImmed8 (
4172 IN VM_CONTEXT *VmPtr,
4173 IN UINT32 Offset
4174 )
4175 /*++
4176
4177 Routine Description:
4178
4179 The following VmReadImmed routines are called by the EBC execute
4180 functions to read EBC immediate values from the code stream.
4181 Since we can't assume alignment, each tries to read in the biggest
4182 chunks size available, but will revert to smaller reads if necessary.
4183
4184 Arguments:
4185 VmPtr - pointer to a VM context
4186 Offset - offset from IP of the code bytes to read.
4187
4188 Returns:
4189 Signed data of the requested size from the specified address.
4190
4191 --*/
4192 {
4193 //
4194 // Simply return the data in flat memory space
4195 //
4196 return * (INT8 *) (VmPtr->Ip + Offset);
4197 }
4198
4199 STATIC
4200 INT16
4201 VmReadImmed16 (
4202 IN VM_CONTEXT *VmPtr,
4203 IN UINT32 Offset
4204 )
4205 {
4206 //
4207 // Read direct if aligned
4208 //
4209 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (INT16))) {
4210 return * (INT16 *) (VmPtr->Ip + Offset);
4211 } else {
4212 //
4213 // All code word reads should be aligned
4214 //
4215 EbcDebugSignalException (
4216 EXCEPT_EBC_ALIGNMENT_CHECK,
4217 EXCEPTION_FLAG_WARNING,
4218 VmPtr
4219 );
4220 }
4221 //
4222 // Return unaligned data
4223 //
4224 return (INT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8));
4225 }
4226
4227 STATIC
4228 INT32
4229 VmReadImmed32 (
4230 IN VM_CONTEXT *VmPtr,
4231 IN UINT32 Offset
4232 )
4233 {
4234 UINT32 Data;
4235
4236 //
4237 // Read direct if aligned
4238 //
4239 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) {
4240 return * (INT32 *) (VmPtr->Ip + Offset);
4241 }
4242 //
4243 // Return unaligned data
4244 //
4245 Data = (UINT32) VmReadCode16 (VmPtr, Offset);
4246 Data |= (UINT32) (VmReadCode16 (VmPtr, Offset + 2) << 16);
4247 return Data;
4248 }
4249
4250 STATIC
4251 INT64
4252 VmReadImmed64 (
4253 IN VM_CONTEXT *VmPtr,
4254 IN UINT32 Offset
4255 )
4256 {
4257 UINT64 Data64;
4258 UINT32 Data32;
4259 UINT8 *Ptr;
4260
4261 //
4262 // Read direct if aligned
4263 //
4264 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) {
4265 return * (UINT64 *) (VmPtr->Ip + Offset);
4266 }
4267 //
4268 // Return unaligned data.
4269 //
4270 Ptr = (UINT8 *) &Data64;
4271 Data32 = VmReadCode32 (VmPtr, Offset);
4272 *(UINT32 *) Ptr = Data32;
4273 Ptr += sizeof (Data32);
4274 Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32));
4275 *(UINT32 *) Ptr = Data32;
4276 return Data64;
4277 }
4278
4279 STATIC
4280 UINT16
4281 VmReadCode16 (
4282 IN VM_CONTEXT *VmPtr,
4283 IN UINT32 Offset
4284 )
4285 /*++
4286
4287 Routine Description:
4288 The following VmReadCode() routines provide the ability to read raw
4289 unsigned data from the code stream.
4290
4291 Arguments:
4292 VmPtr - pointer to VM context
4293 Offset - offset from current IP to the raw data to read.
4294
4295 Returns:
4296 The raw unsigned 16-bit value from the code stream.
4297
4298 --*/
4299 {
4300 //
4301 // Read direct if aligned
4302 //
4303 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT16))) {
4304 return * (UINT16 *) (VmPtr->Ip + Offset);
4305 } else {
4306 //
4307 // All code word reads should be aligned
4308 //
4309 EbcDebugSignalException (
4310 EXCEPT_EBC_ALIGNMENT_CHECK,
4311 EXCEPTION_FLAG_WARNING,
4312 VmPtr
4313 );
4314 }
4315 //
4316 // Return unaligned data
4317 //
4318 return (UINT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8));
4319 }
4320
4321 STATIC
4322 UINT32
4323 VmReadCode32 (
4324 IN VM_CONTEXT *VmPtr,
4325 IN UINT32 Offset
4326 )
4327 {
4328 UINT32 Data;
4329 //
4330 // Read direct if aligned
4331 //
4332 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) {
4333 return * (UINT32 *) (VmPtr->Ip + Offset);
4334 }
4335 //
4336 // Return unaligned data
4337 //
4338 Data = (UINT32) VmReadCode16 (VmPtr, Offset);
4339 Data |= (VmReadCode16 (VmPtr, Offset + 2) << 16);
4340 return Data;
4341 }
4342
4343 STATIC
4344 UINT64
4345 VmReadCode64 (
4346 IN VM_CONTEXT *VmPtr,
4347 IN UINT32 Offset
4348 )
4349 {
4350 UINT64 Data64;
4351 UINT32 Data32;
4352 UINT8 *Ptr;
4353
4354 //
4355 // Read direct if aligned
4356 //
4357 if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) {
4358 return * (UINT64 *) (VmPtr->Ip + Offset);
4359 }
4360 //
4361 // Return unaligned data.
4362 //
4363 Ptr = (UINT8 *) &Data64;
4364 Data32 = VmReadCode32 (VmPtr, Offset);
4365 *(UINT32 *) Ptr = Data32;
4366 Ptr += sizeof (Data32);
4367 Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32));
4368 *(UINT32 *) Ptr = Data32;
4369 return Data64;
4370 }
4371
4372 STATIC
4373 UINT8
4374 VmReadMem8 (
4375 IN VM_CONTEXT *VmPtr,
4376 IN UINTN Addr
4377 )
4378 {
4379 //
4380 // Convert the address if it's in the stack gap
4381 //
4382 Addr = ConvertStackAddr (VmPtr, Addr);
4383 //
4384 // Simply return the data in flat memory space
4385 //
4386 return * (UINT8 *) Addr;
4387 }
4388
4389 STATIC
4390 UINT16
4391 VmReadMem16 (
4392 IN VM_CONTEXT *VmPtr,
4393 IN UINTN Addr
4394 )
4395 {
4396 //
4397 // Convert the address if it's in the stack gap
4398 //
4399 Addr = ConvertStackAddr (VmPtr, Addr);
4400 //
4401 // Read direct if aligned
4402 //
4403 if (IS_ALIGNED (Addr, sizeof (UINT16))) {
4404 return * (UINT16 *) Addr;
4405 }
4406 //
4407 // Return unaligned data
4408 //
4409 return (UINT16) (*(UINT8 *) Addr + (*(UINT8 *) (Addr + 1) << 8));
4410 }
4411
4412 STATIC
4413 UINT32
4414 VmReadMem32 (
4415 IN VM_CONTEXT *VmPtr,
4416 IN UINTN Addr
4417 )
4418 {
4419 UINT32 Data;
4420
4421 //
4422 // Convert the address if it's in the stack gap
4423 //
4424 Addr = ConvertStackAddr (VmPtr, Addr);
4425 //
4426 // Read direct if aligned
4427 //
4428 if (IS_ALIGNED (Addr, sizeof (UINT32))) {
4429 return * (UINT32 *) Addr;
4430 }
4431 //
4432 // Return unaligned data
4433 //
4434 Data = (UINT32) VmReadMem16 (VmPtr, Addr);
4435 Data |= (VmReadMem16 (VmPtr, Addr + 2) << 16);
4436 return Data;
4437 }
4438
4439 STATIC
4440 UINT64
4441 VmReadMem64 (
4442 IN VM_CONTEXT *VmPtr,
4443 IN UINTN Addr
4444 )
4445 {
4446 UINT64 Data;
4447 UINT32 Data32;
4448
4449 //
4450 // Convert the address if it's in the stack gap
4451 //
4452 Addr = ConvertStackAddr (VmPtr, Addr);
4453
4454 //
4455 // Read direct if aligned
4456 //
4457 if (IS_ALIGNED (Addr, sizeof (UINT64))) {
4458 return * (UINT64 *) Addr;
4459 }
4460 //
4461 // Return unaligned data. Assume little endian.
4462 //
4463 Data = (UINT64) VmReadMem32 (VmPtr, Addr);
4464 Data32 = VmReadMem32 (VmPtr, Addr + sizeof (UINT32));
4465 *(UINT32 *) ((UINT32 *) &Data + 1) = Data32;
4466 return Data;
4467 }
4468
4469 STATIC
4470 UINTN
4471 ConvertStackAddr (
4472 IN VM_CONTEXT *VmPtr,
4473 IN UINTN Addr
4474 )
4475 /*++
4476
4477 Routine Description:
4478
4479 Given an address that EBC is going to read from or write to, return
4480 an appropriate address that accounts for a gap in the stack.
4481
4482 The stack for this application looks like this (high addr on top)
4483 [EBC entry point arguments]
4484 [VM stack]
4485 [EBC stack]
4486
4487 The EBC assumes that its arguments are at the top of its stack, which
4488 is where the VM stack is really. Therefore if the EBC does memory
4489 accesses into the VM stack area, then we need to convert the address
4490 to point to the EBC entry point arguments area. Do this here.
4491
4492 Arguments:
4493
4494 VmPtr - pointer to VM context
4495 Addr - address of interest
4496
4497 Returns:
4498
4499 The unchanged address if it's not in the VM stack region. Otherwise,
4500 adjust for the stack gap and return the modified address.
4501
4502 --*/
4503 {
4504 ASSERT(((Addr < VmPtr->LowStackTop) || (Addr > VmPtr->HighStackBottom)));
4505 return Addr;
4506 }
4507
4508 STATIC
4509 UINTN
4510 VmReadMemN (
4511 IN VM_CONTEXT *VmPtr,
4512 IN UINTN Addr
4513 )
4514 /*++
4515
4516 Routine Description:
4517 Read a natural value from memory. May or may not be aligned.
4518
4519 Arguments:
4520 VmPtr - current VM context
4521 Addr - the address to read from
4522
4523 Returns:
4524 The natural value at address Addr.
4525
4526 --*/
4527 {
4528 UINTN Data;
4529 volatile UINT32 Size;
4530 UINT8 *FromPtr;
4531 UINT8 *ToPtr;
4532 //
4533 // Convert the address if it's in the stack gap
4534 //
4535 Addr = ConvertStackAddr (VmPtr, Addr);
4536 //
4537 // Read direct if aligned
4538 //
4539 if (IS_ALIGNED (Addr, sizeof (UINTN))) {
4540 return * (UINTN *) Addr;
4541 }
4542 //
4543 // Return unaligned data
4544 //
4545 Data = 0;
4546 FromPtr = (UINT8 *) Addr;
4547 ToPtr = (UINT8 *) &Data;
4548
4549 for (Size = 0; Size < sizeof (Data); Size++) {
4550 *ToPtr = *FromPtr;
4551 ToPtr++;
4552 FromPtr++;
4553 }
4554
4555 return Data;
4556 }
4557
4558 UINT64
4559 GetVmVersion (
4560 VOID
4561 )
4562 {
4563 return (UINT64) (((VM_MAJOR_VERSION & 0xFFFF) << 16) | ((VM_MINOR_VERSION & 0xFFFF)));
4564 }