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