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