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