Skip to content

Commit 5329930

Browse files
rust-tests: Some RISC-V emulation tests
There were previously no RISC-V emulation tests at all. Now there is a happy-path test of successfully executing a single instruction, along with two different ways of hooking an invalid instruction (using either general interrupt hook or invalid instruction hook) and also using general interrupt hook to handle an ecall instruction.
1 parent 87610ba commit 5329930

File tree

1 file changed

+216
-2
lines changed

1 file changed

+216
-2
lines changed

tests/rust-tests/main.rs

Lines changed: 216 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ extern crate alloc;
33
use alloc::rc::Rc;
44
use core::cell::RefCell;
55
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,
710
};
8-
use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86, Unicorn};
911

1012
pub static X86_REGISTERS: [RegisterX86; 125] = [
1113
RegisterX86::AH,
@@ -623,6 +625,218 @@ fn emulate_ppc() {
623625
assert_eq!(emu.reg_read(RegisterPPC::R26), Ok(1379));
624626
}
625627

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+
626840
#[test]
627841
fn mem_unmapping() {
628842
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)

0 commit comments

Comments
 (0)