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