@@ -3,9 +3,11 @@ extern crate alloc;
3
3
use alloc:: rc:: Rc ;
4
4
use core:: cell:: RefCell ;
5
5
use unicorn_engine:: unicorn_const:: {
6
- uc_error, Arch , HookType , MemType , Mode , Permission , SECOND_SCALE , TlbEntry , TlbType
6
+ uc_error, Arch , HookType , MemType , Mode , Permission , TlbEntry , TlbType , SECOND_SCALE ,
7
+ } ;
8
+ use unicorn_engine:: {
9
+ InsnSysX86 , RegisterARM , RegisterMIPS , RegisterPPC , RegisterRISCV , RegisterX86 , Unicorn ,
7
10
} ;
8
- use unicorn_engine:: { InsnSysX86 , RegisterARM , RegisterMIPS , RegisterPPC , RegisterX86 , Unicorn } ;
9
11
10
12
pub static X86_REGISTERS : [ RegisterX86 ; 125 ] = [
11
13
RegisterX86 :: AH ,
@@ -623,6 +625,218 @@ fn emulate_ppc() {
623
625
assert_eq ! ( emu. reg_read( RegisterPPC :: R26 ) , Ok ( 1379 ) ) ;
624
626
}
625
627
628
+ #[ test]
629
+ fn emulate_riscv64 ( ) {
630
+ let riscv_code: Vec < u8 > = vec ! [ 0x13 , 0x05 , 0xa5 , 0x00 ] ; // addi a0, a0, 10
631
+
632
+ let mut emu = unicorn_engine:: Unicorn :: new ( Arch :: RISCV , Mode :: RISCV64 )
633
+ . expect ( "failed to initialize unicorn instance" ) ;
634
+ assert_eq ! ( emu. reg_write( RegisterRISCV :: A0 , 123 ) , Ok ( ( ) ) ) ;
635
+ assert_eq ! ( emu. reg_read( RegisterRISCV :: A0 ) , Ok ( 123 ) ) ;
636
+
637
+ // Attempt to write to memory before mapping it.
638
+ assert_eq ! (
639
+ emu. mem_write( 0x1000 , & riscv_code) ,
640
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
641
+ ) ;
642
+
643
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
644
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
645
+ assert_eq ! (
646
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
647
+ Ok ( riscv_code. clone( ) )
648
+ ) ;
649
+
650
+ assert_eq ! (
651
+ emu. emu_start(
652
+ 0x1000 ,
653
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
654
+ 10 * SECOND_SCALE ,
655
+ 1000
656
+ ) ,
657
+ Ok ( ( ) )
658
+ ) ;
659
+ assert_eq ! ( emu. reg_read( RegisterRISCV :: A0 ) , Ok ( 123 + 10 ) ) ;
660
+ }
661
+
662
+ #[ test]
663
+ fn emulate_riscv64_invalid_insn_hook ( ) {
664
+ let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x10 , 0x00 , 0xc0 ] ; // "unimp"
665
+
666
+ struct Data {
667
+ invalid_hook_called : bool ,
668
+ }
669
+
670
+ let mut emu = unicorn_engine:: Unicorn :: new_with_data (
671
+ Arch :: RISCV ,
672
+ Mode :: RISCV64 ,
673
+ Data {
674
+ invalid_hook_called : false ,
675
+ } ,
676
+ )
677
+ . expect ( "failed to initialize unicorn instance" ) ;
678
+
679
+ // Attempt to write to memory before mapping it.
680
+ assert_eq ! (
681
+ emu. mem_write( 0x1000 , & riscv_code) ,
682
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
683
+ ) ;
684
+
685
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
686
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
687
+ assert_eq ! (
688
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
689
+ Ok ( riscv_code. clone( ) )
690
+ ) ;
691
+
692
+ emu. add_insn_invalid_hook ( |emu| {
693
+ let data = emu. get_data_mut ( ) ;
694
+ data. invalid_hook_called = true ;
695
+ emu. emu_stop ( ) . expect ( "failed to stop" ) ;
696
+ false
697
+ } )
698
+ . expect ( "failed to add invalid instruction hook" ) ;
699
+
700
+ assert_eq ! (
701
+ emu. emu_start(
702
+ 0x1000 ,
703
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
704
+ 10 * SECOND_SCALE ,
705
+ 1000
706
+ ) ,
707
+ Ok ( ( ) )
708
+ ) ;
709
+
710
+ assert ! (
711
+ emu. get_data( ) . invalid_hook_called,
712
+ "invalid instruction hook was not called"
713
+ ) ;
714
+ }
715
+
716
+ #[ test]
717
+ fn emulate_riscv64_invalid_insn_interrupt ( ) {
718
+ let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x10 , 0x00 , 0xc0 ] ; // "unimp"
719
+
720
+ struct Data {
721
+ hook_calls : usize ,
722
+ mcause : Option < u32 > ,
723
+ }
724
+
725
+ let mut emu = unicorn_engine:: Unicorn :: new_with_data (
726
+ Arch :: RISCV ,
727
+ Mode :: RISCV64 ,
728
+ Data {
729
+ hook_calls : 0 ,
730
+ mcause : None ,
731
+ } ,
732
+ )
733
+ . expect ( "failed to initialize unicorn instance" ) ;
734
+
735
+ // Attempt to write to memory before mapping it.
736
+ assert_eq ! (
737
+ emu. mem_write( 0x1000 , & riscv_code) ,
738
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
739
+ ) ;
740
+
741
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
742
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
743
+ assert_eq ! (
744
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
745
+ Ok ( riscv_code. clone( ) )
746
+ ) ;
747
+
748
+ emu. add_intr_hook ( |emu, mcause| {
749
+ let data = emu. get_data_mut ( ) ;
750
+ data. hook_calls += 1 ;
751
+ data. mcause = Some ( mcause) ;
752
+ emu. emu_stop ( ) . expect ( "failed to stop" ) ;
753
+ } )
754
+ . expect ( "failed to add interrupt hook" ) ;
755
+
756
+ assert_eq ! (
757
+ emu. emu_start(
758
+ 0x1000 ,
759
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
760
+ 10 * SECOND_SCALE ,
761
+ 1000
762
+ ) ,
763
+ Ok ( ( ) )
764
+ ) ;
765
+
766
+ assert_eq ! (
767
+ emu. get_data( ) . hook_calls,
768
+ 1 ,
769
+ "interrupt hook should have been called exactly once"
770
+ ) ;
771
+ assert_eq ! (
772
+ emu. get_data( ) . mcause,
773
+ Some ( 2_u32 ) ,
774
+ "wrong mcause value for illegal instruction"
775
+ ) ;
776
+ }
777
+
778
+ #[ test]
779
+ fn emulate_riscv64_ecall_interrupt ( ) {
780
+ let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x00 , 0x00 , 0x00 ] ; // ecall
781
+
782
+ struct Data {
783
+ hook_calls : usize ,
784
+ mcause : Option < u32 > ,
785
+ }
786
+
787
+ let mut emu = unicorn_engine:: Unicorn :: new_with_data (
788
+ Arch :: RISCV ,
789
+ Mode :: RISCV64 ,
790
+ Data {
791
+ hook_calls : 0 ,
792
+ mcause : None ,
793
+ } ,
794
+ )
795
+ . expect ( "failed to initialize unicorn instance" ) ;
796
+
797
+ // Attempt to write to memory before mapping it.
798
+ assert_eq ! (
799
+ emu. mem_write( 0x1000 , & riscv_code) ,
800
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
801
+ ) ;
802
+
803
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
804
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
805
+ assert_eq ! (
806
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
807
+ Ok ( riscv_code. clone( ) )
808
+ ) ;
809
+
810
+ emu. add_intr_hook ( |emu, mcause| {
811
+ let data = emu. get_data_mut ( ) ;
812
+ data. hook_calls += 1 ;
813
+ data. mcause = Some ( mcause) ;
814
+ emu. emu_stop ( ) . expect ( "failed to stop" ) ;
815
+ } )
816
+ . expect ( "failed to add interrupt hook" ) ;
817
+
818
+ assert_eq ! (
819
+ emu. emu_start(
820
+ 0x1000 ,
821
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
822
+ 10 * SECOND_SCALE ,
823
+ 1000
824
+ ) ,
825
+ Ok ( ( ) )
826
+ ) ;
827
+
828
+ assert_eq ! (
829
+ emu. get_data( ) . hook_calls,
830
+ 1 ,
831
+ "interrupt hook should have been called exactly once"
832
+ ) ;
833
+ assert_eq ! (
834
+ emu. get_data( ) . mcause,
835
+ Some ( 8_u32 ) ,
836
+ "wrong mcause value for ecall from U-Mode"
837
+ ) ;
838
+ }
839
+
626
840
#[ test]
627
841
fn mem_unmapping ( ) {
628
842
let mut emu = unicorn_engine:: Unicorn :: new ( Arch :: X86 , Mode :: MODE_32 )
0 commit comments