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