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