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