@@ -775,6 +775,172 @@ fn emulate_riscv64_invalid_insn_interrupt() {
775
775
) ;
776
776
}
777
777
778
+ #[ test]
779
+ fn emulate_riscv64_mem_error_hook ( ) {
780
+ let riscv_code: Vec < u8 > = vec ! [ 0x1b , 0x05 , 0xf0 , 0xff ] ; // la a0, ~0
781
+
782
+ struct Data {
783
+ hook_calls : usize ,
784
+ call : Option < HookCall > ,
785
+ }
786
+ #[ derive( Debug , PartialEq ) ]
787
+ struct HookCall {
788
+ typ : MemType ,
789
+ addr : u64 ,
790
+ size : usize ,
791
+ }
792
+
793
+ let mut emu = unicorn_engine:: Unicorn :: new_with_data (
794
+ Arch :: RISCV ,
795
+ Mode :: RISCV64 ,
796
+ Data {
797
+ hook_calls : 0 ,
798
+ call : None ,
799
+ } ,
800
+ )
801
+ . expect ( "failed to initialize unicorn instance" ) ;
802
+
803
+ // Attempt to write to memory before mapping it.
804
+ assert_eq ! (
805
+ emu. mem_write( 0x1000 , & riscv_code) ,
806
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
807
+ ) ;
808
+
809
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
810
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
811
+ assert_eq ! (
812
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
813
+ Ok ( riscv_code. clone( ) )
814
+ ) ;
815
+
816
+ emu. ctl_tlb_type ( unicorn_engine:: TlbType :: VIRTUAL )
817
+ . expect ( "failed to select virtual TLB" ) ;
818
+ emu. add_tlb_hook ( 0 , !0 , |_, vaddr, _| {
819
+ if vaddr < 0x4000 {
820
+ // The first page is identity-mapped.
821
+ Some ( TlbEntry {
822
+ paddr : vaddr,
823
+ perms : Permission :: ALL ,
824
+ } )
825
+ } else {
826
+ // All other memory is unmapped
827
+ None
828
+ }
829
+ } )
830
+ . expect ( "failed to add TLB hook" ) ;
831
+
832
+ emu. add_mem_hook ( HookType :: MEM_INVALID , 0 , !0 , |emu, typ, addr, size, _| {
833
+ let data = emu. get_data_mut ( ) ;
834
+ data. hook_calls += 1 ;
835
+ data. call = Some ( HookCall { typ, addr, size } ) ;
836
+ false
837
+ } )
838
+ . expect ( "failed to add memory hook" ) ;
839
+
840
+ assert_eq ! (
841
+ emu. emu_start(
842
+ 0x1000 ,
843
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
844
+ 10 * SECOND_SCALE ,
845
+ 1000
846
+ ) ,
847
+ Ok ( ( ) )
848
+ ) ;
849
+
850
+ assert_eq ! (
851
+ emu. get_data( ) . hook_calls,
852
+ 1 ,
853
+ "interrupt hook should have been called exactly once"
854
+ ) ;
855
+ assert_eq ! (
856
+ emu. get_data( ) . call,
857
+ Some ( HookCall {
858
+ typ: MemType :: READ_PROT ,
859
+ addr: !0 ,
860
+ size: 8 ,
861
+ } ) ,
862
+ "wrong hook call for read from unmapped memory"
863
+ ) ;
864
+ }
865
+
866
+ #[ test]
867
+ fn emulate_riscv64_mem_error_interrupt ( ) {
868
+ let riscv_code: Vec < u8 > = vec ! [ 0x1b , 0x05 , 0xf0 , 0xff ] ; // la a0, ~0
869
+
870
+ struct Data {
871
+ hook_calls : usize ,
872
+ mcause : Option < u32 > ,
873
+ }
874
+
875
+ let mut emu = unicorn_engine:: Unicorn :: new_with_data (
876
+ Arch :: RISCV ,
877
+ Mode :: RISCV64 ,
878
+ Data {
879
+ hook_calls : 0 ,
880
+ mcause : None ,
881
+ } ,
882
+ )
883
+ . expect ( "failed to initialize unicorn instance" ) ;
884
+
885
+ // Attempt to write to memory before mapping it.
886
+ assert_eq ! (
887
+ emu. mem_write( 0x1000 , & riscv_code) ,
888
+ ( Err ( uc_error:: WRITE_UNMAPPED ) )
889
+ ) ;
890
+
891
+ assert_eq ! ( emu. mem_map( 0x1000 , 0x4000 , Permission :: ALL ) , Ok ( ( ) ) ) ;
892
+ assert_eq ! ( emu. mem_write( 0x1000 , & riscv_code) , Ok ( ( ) ) ) ;
893
+ assert_eq ! (
894
+ emu. mem_read_as_vec( 0x1000 , riscv_code. len( ) ) ,
895
+ Ok ( riscv_code. clone( ) )
896
+ ) ;
897
+
898
+ emu. ctl_tlb_type ( unicorn_engine:: TlbType :: VIRTUAL )
899
+ . expect ( "failed to select virtual TLB" ) ;
900
+ emu. add_tlb_hook ( 0 , !0 , |_, vaddr, _| {
901
+ if vaddr < 0x4000 {
902
+ // The first page is identity-mapped.
903
+ Some ( TlbEntry {
904
+ paddr : vaddr,
905
+ perms : Permission :: ALL ,
906
+ } )
907
+ } else {
908
+ // All other memory is unmapped
909
+ None
910
+ }
911
+ } )
912
+ . expect ( "failed to add TLB hook" ) ;
913
+
914
+ emu. add_intr_hook ( |emu, mcause| {
915
+ let data = emu. get_data_mut ( ) ;
916
+ data. hook_calls += 1 ;
917
+ data. mcause = Some ( mcause) ;
918
+ emu. emu_stop ( ) . expect ( "failed to stop" ) ;
919
+ } )
920
+ . expect ( "failed to add interrupt hook" ) ;
921
+
922
+ assert_eq ! (
923
+ emu. emu_start(
924
+ 0x1000 ,
925
+ ( 0x1000 + riscv_code. len( ) ) as u64 ,
926
+ 10 * SECOND_SCALE ,
927
+ 1000
928
+ ) ,
929
+ Ok ( ( ) )
930
+ ) ;
931
+
932
+ assert_eq ! (
933
+ emu. get_data( ) . hook_calls,
934
+ 1 ,
935
+ "interrupt hook should have been called exactly once"
936
+ ) ;
937
+ assert_eq ! (
938
+ emu. get_data( ) . mcause,
939
+ Some ( 13_u32 ) ,
940
+ "wrong mcause value for load page fault"
941
+ ) ;
942
+ }
943
+
778
944
#[ test]
779
945
fn emulate_riscv64_ecall_interrupt ( ) {
780
946
let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x00 , 0x00 , 0x00 ] ; // ecall
0 commit comments