add basic mmu tests
Some simple tests for diffrent mmu. Basicly add some tlb entries, enable the mmu try to read from virtual address The aarm64 test was provided by imre-kis-arm in #1718
This commit is contained in:
parent
f2eb1f4711
commit
7f1eb4532d
@ -372,6 +372,106 @@ static void test_arm64_block_invalid_mem_read_write_sync(void)
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm64_mmu(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
char *data;
|
||||
char tlbe[8];
|
||||
uint64_t x0, x1, x2;
|
||||
/*
|
||||
* Not exact the binary, but aarch64-linux-gnu-as generate this code and reference sometimes data after ttb0_base.
|
||||
* // Read data from physical address
|
||||
* ldr X0, =0x40000000
|
||||
* ldr X1, [X0]
|
||||
|
||||
* // Initialize translation table control registers
|
||||
* ldr X0, =0x180803F20
|
||||
* msr TCR_EL1, X0
|
||||
* ldr X0, =0xFFFFFFFF
|
||||
* msr MAIR_EL1, X0
|
||||
|
||||
* // Set translation table
|
||||
* adr X0, ttb0_base
|
||||
* msr TTBR0_EL1, X0
|
||||
|
||||
* // Enable caches and the MMU
|
||||
* mrs X0, SCTLR_EL1
|
||||
* orr X0, X0, #(0x1 << 2) // The C bit (data cache).
|
||||
* orr X0, X0, #(0x1 << 12) // The I bit (instruction cache)
|
||||
* orr X0, X0, #0x1 // The M bit (MMU).
|
||||
* msr SCTLR_EL1, X0
|
||||
* dsb SY
|
||||
* isb
|
||||
|
||||
* // Read the same memory area through virtual address
|
||||
* ldr X0, =0x80000000
|
||||
* ldr X2, [X0]
|
||||
*
|
||||
* // Stop
|
||||
* b .
|
||||
*/
|
||||
char code[] = "\x00\x81\x00\x58\x01\x00\x40\xf9\x00\x81\x00\x58\x40\x20\x18\xd5\x00\x81\x00\x58\x00\xa2\x18\xd5\x40\x7f\x00\x10\x00\x20\x18\xd5\x00\x10\x38\xd5\x00\x00\x7e\xb2\x00\x00\x74\xb2\x00\x00\x40\xb2\x00\x10\x18\xd5\x9f\x3f\x03\xd5\xdf\x3f\x03\xd5\xe0\x7f\x00\x58\x02\x00\x40\xf9\x00\x00\x00\x14\x1f\x20\x03\xd5\x1f\x20\x03\xd5\x1F\x20\x03\xD5\x1F\x20\x03\xD5";
|
||||
|
||||
data = malloc(0x1000);
|
||||
TEST_CHECK(data != NULL);
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||
OK(uc_mem_map(uc, 0, 0x2000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, 0, code, sizeof(code) - 1));
|
||||
|
||||
// generate tlb entries
|
||||
tlbe[0] = 0x41;
|
||||
tlbe[1] = 0x07;
|
||||
tlbe[2] = 0;
|
||||
tlbe[3] = 0;
|
||||
tlbe[4] = 0;
|
||||
tlbe[5] = 0;
|
||||
tlbe[6] = 0;
|
||||
tlbe[7] = 0;
|
||||
OK(uc_mem_write(uc, 0x1000, tlbe, sizeof(tlbe)));
|
||||
tlbe[3] = 0x40;
|
||||
OK(uc_mem_write(uc, 0x1008, tlbe, sizeof(tlbe)));
|
||||
OK(uc_mem_write(uc, 0x1010, tlbe, sizeof(tlbe)));
|
||||
OK(uc_mem_write(uc, 0x1018, tlbe, sizeof(tlbe)));
|
||||
|
||||
//mentioned data referenced by the asm generated my aarch64-linux-gnu-as
|
||||
tlbe[0] = 0;
|
||||
tlbe[1] = 0;
|
||||
OK(uc_mem_write(uc, 0x1020, tlbe, sizeof(tlbe)));
|
||||
tlbe[0] = 0x20;
|
||||
tlbe[1] = 0x3f;
|
||||
tlbe[2] = 0x80;
|
||||
tlbe[3] = 0x80;
|
||||
tlbe[4] = 0x1;
|
||||
OK(uc_mem_write(uc, 0x1028, tlbe, sizeof(tlbe)));
|
||||
tlbe[0] = 0xff;
|
||||
tlbe[1] = 0xff;
|
||||
tlbe[2] = 0xff;
|
||||
tlbe[3] = 0xff;
|
||||
tlbe[4] = 0x00;
|
||||
OK(uc_mem_write(uc, 0x1030, tlbe, sizeof(tlbe)));
|
||||
tlbe[0] = 0x00;
|
||||
tlbe[1] = 0x00;
|
||||
tlbe[2] = 0x00;
|
||||
tlbe[3] = 0x80;
|
||||
OK(uc_mem_write(uc, 0x1038, tlbe, sizeof(tlbe)));
|
||||
|
||||
for (size_t i = 0; i < 0x1000; i++) {
|
||||
data[i] = 0x44;
|
||||
}
|
||||
OK(uc_mem_map_ptr(uc, 0x40000000, 0x1000, UC_PROT_READ, data));
|
||||
|
||||
OK(uc_emu_start(uc, 0, 0x44, 0, 0));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X0, &x0));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X1, &x1));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X2, &x2));
|
||||
|
||||
TEST_CHECK(x0 == 0x80000000);
|
||||
TEST_CHECK(x1 == 0x4444444444444444);
|
||||
TEST_CHECK(x2 == 0x4444444444444444);
|
||||
free(data);
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_arm64_until", test_arm64_until},
|
||||
{"test_arm64_code_patching", test_arm64_code_patching},
|
||||
{"test_arm64_code_patching_count", test_arm64_code_patching_count},
|
||||
@ -385,4 +485,5 @@ TEST_LIST = {{"test_arm64_until", test_arm64_until},
|
||||
{"test_arm64_block_sync_pc", test_arm64_block_sync_pc},
|
||||
{"test_arm64_block_invalid_mem_read_write_sync",
|
||||
test_arm64_block_invalid_mem_read_write_sync},
|
||||
{"test_arm64_mmu", test_arm64_mmu},
|
||||
{NULL, NULL}};
|
||||
|
@ -634,6 +634,76 @@ static void test_riscv_correct_address_in_long_jump_hook(void)
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv_mmu_prepare_tlb(uc_engine *uc, uint32_t data_address, uint32_t code_address)
|
||||
{
|
||||
uint64_t tlbe;
|
||||
uint32_t sptbr = 0x2000;
|
||||
|
||||
OK(uc_mem_map(uc, sptbr, 0x3000, UC_PROT_ALL)); //tlb base
|
||||
|
||||
tlbe = ((sptbr + 0x1000) >> 2) | 1;
|
||||
OK(uc_mem_write(uc, sptbr, &tlbe, sizeof(tlbe)));
|
||||
tlbe = ((sptbr + 0x2000) >> 2) | 1;
|
||||
OK(uc_mem_write(uc, sptbr + 0x1000, &tlbe, sizeof(tlbe)));
|
||||
|
||||
tlbe = (code_address >> 2) | (7 << 1) | 1;
|
||||
OK(uc_mem_write(uc, sptbr + 0x2000 + 0x15*8, &tlbe, sizeof(tlbe)));
|
||||
|
||||
tlbe = (data_address >> 2) | (7 << 1) | 1;
|
||||
OK(uc_mem_write(uc, sptbr + 0x2000 + 0x16*8, &tlbe, sizeof(tlbe)));
|
||||
}
|
||||
|
||||
static void test_riscv_mmu_hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *userdata)
|
||||
{
|
||||
if (address == 0x15010) {
|
||||
OK(uc_emu_stop(uc));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_riscv_mmu(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook h;
|
||||
uint32_t code_address = 0x5000;
|
||||
uint32_t data_address = 0x6000;
|
||||
uint32_t data_value = 0x41414141;
|
||||
uint32_t data_result = 0;
|
||||
|
||||
/*
|
||||
li t3, (8 << 60) | 2
|
||||
csrw sptbr, t3
|
||||
li t0, (1 << 11) | (1 << 5)
|
||||
csrw mstatus, t0
|
||||
la t1, 0x15000
|
||||
csrw mepc, t1
|
||||
mret
|
||||
*/
|
||||
char code_m[] = "\x1b\x0e\xf0\xff" "\x13\x1e\xfe\x03" "\x13\x0e\x2e\x00" "\x73\x10\x0e\x18" "\xb7\x12\x00\x00" "\x9b\x82\x02\x82" "\x73\x90\x02\x30" "\x37\x53\x01\x00" "\x73\x10\x13\x34" "\x73\x00\x20\x30";
|
||||
|
||||
/*
|
||||
li t0, 0x41414141
|
||||
li t1, 0x16000
|
||||
sw t0, 0(t1)
|
||||
nop
|
||||
*/
|
||||
char code_s[] = "\xb7\x42\x41\x41" "\x9b\x82\x12\x14" "\x37\x63\x01\x00" "\x23\x20\x53\x00" "\x13\x00\x00\x00";
|
||||
|
||||
OK(uc_open(UC_ARCH_RISCV, UC_MODE_RISCV64, &uc));
|
||||
OK(uc_hook_add(uc, &h, UC_HOOK_CODE, test_riscv_mmu_hook_code, NULL, 1, 0));
|
||||
OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, code_address, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, data_address, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, code_address, &code_s, sizeof(code_s)));
|
||||
OK(uc_mem_write(uc, 0x1000, &code_m, sizeof(code_m)));
|
||||
|
||||
test_riscv_mmu_prepare_tlb(uc, data_address, code_address);
|
||||
|
||||
OK(uc_emu_start(uc, 0x1000, sizeof(code_m) - 1, 0, 0));
|
||||
OK(uc_mem_read(uc, data_address, &data_result, sizeof(data_result)));
|
||||
|
||||
TEST_CHECK(data_value == data_result);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_riscv32_nop", test_riscv32_nop},
|
||||
{"test_riscv64_nop", test_riscv64_nop},
|
||||
@ -657,4 +727,5 @@ TEST_LIST = {
|
||||
test_riscv_correct_address_in_small_jump_hook},
|
||||
{"test_riscv_correct_address_in_long_jump_hook",
|
||||
test_riscv_correct_address_in_long_jump_hook},
|
||||
{"test_riscv_mmu", test_riscv_mmu},
|
||||
{NULL, NULL}};
|
||||
|
@ -1259,6 +1259,131 @@ static void test_x86_16_incorrect_ip(void)
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_x86_mmu_prepare_tlb(uc_engine *uc, uint64_t vaddr, uint64_t tlb_base)
|
||||
{
|
||||
uint64_t cr0;
|
||||
uint64_t cr4;
|
||||
uc_x86_msr msr = {.rid = 0x0c0000080, .value = 0};
|
||||
uint64_t pml4o = ((vaddr & 0x00ff8000000000) >> 39)*8;
|
||||
uint64_t pdpo = ((vaddr & 0x00007fc0000000) >> 30)*8;
|
||||
uint64_t pdo = ((vaddr & 0x0000003fe00000) >> 21)*8;
|
||||
uint64_t pml4e = (tlb_base + 0x1000) | 1 | (1 << 2);
|
||||
uint64_t pdpe = (tlb_base + 0x2000) | 1 | (1 << 2);
|
||||
uint64_t pde = (tlb_base + 0x3000) | 1 | (1 << 2);
|
||||
OK(uc_mem_write(uc, tlb_base + pml4o, &pml4e, sizeof(pml4o)));
|
||||
OK(uc_mem_write(uc, tlb_base + 0x1000 + pdpo, &pdpe, sizeof(pdpe)));
|
||||
OK(uc_mem_write(uc, tlb_base + 0x2000 + pdo, &pde, sizeof(pde)));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_CR3, &tlb_base));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_CR0, &cr0));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_CR4, &cr4));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_MSR, &msr));
|
||||
cr0 |= 1;
|
||||
cr0 |= 1l << 31;
|
||||
cr4 |= 1l << 5;
|
||||
msr.value |= 1l << 8;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_CR0, &cr0));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_CR4, &cr4));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_MSR, &msr));
|
||||
}
|
||||
|
||||
static void test_x86_mmu_pt_set(uc_engine *uc, uint64_t vaddr, uint64_t paddr, uint64_t tlb_base)
|
||||
{
|
||||
uint64_t pto = ((vaddr & 0x000000001ff000) >> 12)*8;
|
||||
uint32_t pte = (paddr) | 1 | (1 << 2);
|
||||
uc_mem_write(uc, tlb_base + 0x3000 + pto, &pte, sizeof(pte));
|
||||
}
|
||||
|
||||
static void test_x86_mmu_callback(uc_engine *uc, void *userdata)
|
||||
{
|
||||
bool *parrent_done = userdata;
|
||||
uint64_t rax;
|
||||
OK(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
switch (rax) {
|
||||
case 57:
|
||||
/* fork */
|
||||
break;
|
||||
case 60:
|
||||
/* exit */
|
||||
uc_emu_stop(uc);
|
||||
return;
|
||||
default:
|
||||
TEST_CHECK(false);
|
||||
}
|
||||
|
||||
if (!(*parrent_done)) {
|
||||
*parrent_done = true;
|
||||
rax = 27;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax));
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_x86_mmu(void)
|
||||
{
|
||||
bool parrent_done = false;
|
||||
uint64_t tlb_base = 0x3000;
|
||||
uint64_t parrent, child;
|
||||
uint64_t rax, rip;
|
||||
uc_context *context;
|
||||
uc_engine *uc;
|
||||
uc_hook h1;
|
||||
|
||||
/*
|
||||
* mov rax, 57
|
||||
* syscall
|
||||
* test rax, rax
|
||||
* jz child
|
||||
* xor rax, rax
|
||||
* mov rax, 60
|
||||
* mov [0x4000], rax
|
||||
* syscall
|
||||
*
|
||||
* child:
|
||||
* xor rcx, rcx
|
||||
* mov rcx, 42
|
||||
* mov [0x4000], rcx
|
||||
* mov rax, 60
|
||||
* syscall
|
||||
*/
|
||||
char code[] = "\xB8\x39\x00\x00\x00\x0F\x05\x48\x85\xC0\x74\x0F\xB8\x3C\x00\x00\x00\x48\x89\x04\x25\x00\x40\x00\x00\x0F\x05\xB9\x2A\x00\x00\x00\x48\x89\x0C\x25\x00\x40\x00\x00\xB8\x3C\x00\x00\x00\x0F\x05";
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
OK(uc_hook_add(uc, &h1, UC_HOOK_INSN, &test_x86_mmu_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL));
|
||||
OK(uc_context_alloc(uc, &context));
|
||||
|
||||
OK(uc_mem_map(uc, 0x0, 0x1000, UC_PROT_ALL)); //Code
|
||||
OK(uc_mem_write(uc, 0x0, code, sizeof(code) - 1));
|
||||
OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); //Parrent
|
||||
OK(uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL)); //Child
|
||||
OK(uc_mem_map(uc, tlb_base, 0x4000, UC_PROT_ALL)); //TLB
|
||||
|
||||
test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base);
|
||||
test_x86_mmu_pt_set(uc, 0x2000, 0x0, tlb_base);
|
||||
test_x86_mmu_pt_set(uc, 0x4000, 0x1000, tlb_base);
|
||||
|
||||
OK(uc_ctl_flush_tlb(uc));
|
||||
OK(uc_emu_start(uc, 0x2000, 0x0, 0, 0));
|
||||
|
||||
OK(uc_context_save(uc, context));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_RIP, &rip));
|
||||
|
||||
OK(uc_emu_start(uc, rip, 0x0, 0, 0));
|
||||
|
||||
/* restore for child */
|
||||
OK(uc_context_restore(uc, context));
|
||||
test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base);
|
||||
test_x86_mmu_pt_set(uc, 0x4000, 0x2000, tlb_base);
|
||||
rax = 0;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax));
|
||||
OK(uc_ctl_flush_tlb(uc));
|
||||
|
||||
OK(uc_emu_start(uc, rip, 0x0, 0, 0));
|
||||
OK(uc_mem_read(uc, 0x1000, &parrent, sizeof(parrent)));
|
||||
OK(uc_mem_read(uc, 0x2000, &child, sizeof(child)));
|
||||
TEST_CHECK(parrent == 60);
|
||||
TEST_CHECK(child == 42);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_x86_in", test_x86_in},
|
||||
{"test_x86_out", test_x86_out},
|
||||
@ -1302,4 +1427,5 @@ TEST_LIST = {
|
||||
#endif
|
||||
{"test_x86_lazy_mapping", test_x86_lazy_mapping},
|
||||
{"test_x86_16_incorrect_ip", test_x86_16_incorrect_ip},
|
||||
{"test_x86_mmu", test_x86_mmu},
|
||||
{NULL, NULL}};
|
||||
|
Loading…
Reference in New Issue
Block a user