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