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