@@ -775,6 +775,172 @@ fn emulate_riscv64_invalid_insn_interrupt() {
775775 ) ;
776776}
777777
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+
778944#[ test]
779945fn emulate_riscv64_ecall_interrupt ( ) {
780946 let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x00 , 0x00 , 0x00 ] ; // ecall
0 commit comments