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