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