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