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