@@ -775,6 +775,180 @@ 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 ! [
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+
778952#[ test]
779953fn emulate_riscv64_ecall_interrupt ( ) {
780954 let riscv_code: Vec < u8 > = vec ! [ 0x73 , 0x00 , 0x00 , 0x00 ] ; // ecall
0 commit comments