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