add virtuall tlb

this virtuall tlb allows to use mmu indipendent of the architectur
This commit is contained in:
Takacs, Philipp 2022-10-05 16:53:24 +02:00
parent 759d694e24
commit e25419bb2d
36 changed files with 434 additions and 25 deletions

View File

@ -446,6 +446,8 @@ set(UNICORN_ARCH_COMMON
qemu/accel/tcg/tcg-runtime-gvec.c
qemu/accel/tcg/translate-all.c
qemu/accel/tcg/translator.c
qemu/softmmu/unicorn_vtlb.c
)
if(UNICORN_HAS_X86)

View File

@ -146,6 +146,8 @@ typedef uc_err (*uc_gen_tb_t)(struct uc_struct *uc, uint64_t pc, uc_tb *out_tb);
// tb flush
typedef uc_tcg_flush_tlb uc_tb_flush_t;
typedef uc_err (*uc_set_tlb_t)(struct uc_struct *uc, int mode);
struct hook {
int type; // UC_HOOK_*
int insn; // instruction for HOOK_INSN
@ -202,6 +204,7 @@ typedef enum uc_hook_idx {
UC_HOOK_INSN_INVALID_IDX,
UC_HOOK_EDGE_GENERATED_IDX,
UC_HOOK_TCG_OPCODE_IDX,
UC_HOOK_TLB_FILL_IDX,
UC_HOOK_MAX,
} uc_hook_idx;
@ -337,6 +340,8 @@ struct uc_struct {
GHashTable *flat_views;
bool memory_region_update_pending;
uc_set_tlb_t set_tlb;
// linked lists containing hooks per type
struct list hook[UC_HOOK_MAX];
struct list hooks_to_del;

View File

@ -244,6 +244,22 @@ typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size,
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size,
uint32_t value, void *user_data);
typedef struct uc_tlb_entry uc_tlb_entry;
typedef enum uc_mem_type uc_mem_type;
/*
Callback function for tlb lookups
@vaddr: virtuall address for lookup
@rw: the access mode
@result: result entry, contains physical address (paddr) and permitted access type (perms) for the entry
@return: return true if the entry was found. If a callback is present but
no one returns true a pagefault is generated.
*/
typedef bool (*uc_cb_tlbevent_t)(uc_engine *uc, uint64_t vaddr, uc_mem_type type,
uc_tlb_entry *result, void *user_data);
// Represent a TranslationBlock.
typedef struct uc_tb {
uint64_t pc;
@ -295,7 +311,7 @@ typedef void (*uc_cb_mmio_write_t)(uc_engine *uc, uint64_t offset,
void *user_data);
// All type of memory accesses for UC_HOOK_MEM_*
typedef enum uc_mem_type {
enum uc_mem_type {
UC_MEM_READ = 16, // Memory is read from
UC_MEM_WRITE, // Memory is written to
UC_MEM_FETCH, // Memory is fetched
@ -306,7 +322,7 @@ typedef enum uc_mem_type {
UC_MEM_READ_PROT, // Read from read protected, but mapped, memory
UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory
UC_MEM_READ_AFTER, // Memory is read from (successful access)
} uc_mem_type;
};
// These are all op codes we support to hook for UC_HOOK_TCG_OP_CODE.
// Be cautious since it may bring much more overhead than UC_HOOK_CODE without
@ -369,6 +385,10 @@ typedef enum uc_hook_type {
// Hook on specific tcg op code. The usage of this hook is similar to
// UC_HOOK_INSN.
UC_HOOK_TCG_OPCODE = 1 << 16,
// Hook on tlb fill requests.
// Register tlb fill request hook on the virtuall addresses.
// The callback will be triggert if the tlb cache don't contain an address.
UC_HOOK_TLB_FILL = 1 << 17,
} uc_hook_type;
// Hook type for all events of unmapped memory access
@ -490,6 +510,16 @@ typedef enum uc_query_type {
#define UC_CTL_WRITE(type, nr) UC_CTL(type, nr, UC_CTL_IO_WRITE)
#define UC_CTL_READ_WRITE(type, nr) UC_CTL(type, nr, UC_CTL_IO_READ_WRITE)
// unicorn tlb type selection
typedef enum uc_tlb_type {
// The tlb implementation of the CPU, best to use for full system emulation.
UC_TLB_CPU = 0,
// The default unicorn virtuall TLB implementation.
// This tlb defaults to virtuall address == physical address
// Also a hook is availible to override the tlb entries (see uc_cb_tlbevent_t).
UC_TLB_VIRTUAL
} uc_tlb_type;
// All type of controls for uc_ctl API.
// The controls are organized in a tree level.
// If a control don't have `Set` or `Get` for @args, it means it's r/o or w/o.
@ -536,7 +566,11 @@ typedef enum uc_control_type {
UC_CTL_TB_REMOVE_CACHE,
// Invalidate all translation blocks.
// No arguments.
UC_CTL_TB_FLUSH
UC_CTL_TB_FLUSH,
// Change the tlb implementation
// see uc_tlb_type for current implemented types
// Write: @args = (int)
UC_CTL_TLB_TYPE
} uc_control_type;
@ -612,6 +646,7 @@ See sample_ctl.c for a detailed example.
#define uc_ctl_request_cache(uc, address, tb) \
uc_ctl(uc, UC_CTL_READ_WRITE(UC_CTL_TB_REQUEST_CACHE, 2), (address), (tb))
#define uc_ctl_flush_tlb(uc) uc_ctl(uc, UC_CTL_WRITE(UC_CTL_TB_FLUSH, 0))
#define uc_ctl_tlb_mode(uc, mode) uc_ctl(uc, UC_CTL_WRITE(UC_CTL_TLB_TYPE, 1), (mode))
// Opaque storage for CPU context, used with uc_context_*()
struct uc_context;
typedef struct uc_context uc_context;
@ -898,6 +933,11 @@ typedef enum uc_prot {
UC_PROT_ALL = 7,
} uc_prot;
struct uc_tlb_entry {
uint64_t paddr;
uc_prot perms;
};
/*
Map memory in for emulation.
This API adds a memory region that can be used by emulation.

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _aarch64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_aarch64
#define uc_add_inline_hook uc_add_inline_hook_aarch64
#define uc_del_inline_hook uc_del_inline_hook_aarch64
#define tb_invalidate_phys_range tb_invalidate_phys_range_aarch64

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _arm
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_arm
#define uc_add_inline_hook uc_add_inline_hook_arm
#define uc_del_inline_hook uc_del_inline_hook_arm
#define tb_invalidate_phys_range tb_invalidate_phys_range_arm

View File

@ -117,6 +117,9 @@ typedef struct CPUClass {
bool (*tlb_fill)(CPUState *cpu, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
bool (*tlb_fill_cpu)(CPUState *cpu, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr);
hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr,
MemTxAttrs *attrs);

View File

@ -2,6 +2,7 @@
#define QEMU_CPUS_H
#include "qemu/timer.h"
#include "hw/core/cpu.h"
/* cpus.c */
bool qemu_in_vcpu_thread(void);

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _m68k
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_m68k
#define uc_add_inline_hook uc_add_inline_hook_m68k
#define uc_del_inline_hook uc_del_inline_hook_m68k
#define tb_invalidate_phys_range tb_invalidate_phys_range_m68k

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _mips
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_mips
#define uc_add_inline_hook uc_add_inline_hook_mips
#define uc_del_inline_hook uc_del_inline_hook_mips
#define tb_invalidate_phys_range tb_invalidate_phys_range_mips

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _mips64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_mips64
#define uc_add_inline_hook uc_add_inline_hook_mips64
#define uc_del_inline_hook uc_del_inline_hook_mips64
#define tb_invalidate_phys_range tb_invalidate_phys_range_mips64

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _mips64el
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_mips64el
#define uc_add_inline_hook uc_add_inline_hook_mips64el
#define uc_del_inline_hook uc_del_inline_hook_mips64el
#define tb_invalidate_phys_range tb_invalidate_phys_range_mips64el

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _mipsel
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_mipsel
#define uc_add_inline_hook uc_add_inline_hook_mipsel
#define uc_del_inline_hook uc_del_inline_hook_mipsel
#define tb_invalidate_phys_range tb_invalidate_phys_range_mipsel

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _ppc
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_ppc
#define uc_add_inline_hook uc_add_inline_hook_ppc
#define uc_del_inline_hook uc_del_inline_hook_ppc
#define tb_invalidate_phys_range tb_invalidate_phys_range_ppc

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _ppc64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_ppc64
#define uc_add_inline_hook uc_add_inline_hook_ppc64
#define uc_del_inline_hook uc_del_inline_hook_ppc64
#define tb_invalidate_phys_range tb_invalidate_phys_range_ppc64

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _riscv32
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_riscv32
#define uc_add_inline_hook uc_add_inline_hook_riscv32
#define uc_del_inline_hook uc_del_inline_hook_riscv32
#define tb_invalidate_phys_range tb_invalidate_phys_range_riscv32

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _riscv64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_riscv64
#define uc_add_inline_hook uc_add_inline_hook_riscv64
#define uc_del_inline_hook uc_del_inline_hook_riscv64
#define tb_invalidate_phys_range tb_invalidate_phys_range_riscv64

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _s390x
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_s390x
#define uc_add_inline_hook uc_add_inline_hook_s390x
#define uc_del_inline_hook uc_del_inline_hook_s390x
#define tb_invalidate_phys_range tb_invalidate_phys_range_s390x

106
qemu/softmmu/unicorn_vtlb.c Normal file
View File

@ -0,0 +1,106 @@
#include <stdint.h>
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "exec/exec-all.h"
#include "uc_priv.h"
#include <stdio.h>
static void raise_mmu_exception(CPUState *cs, target_ulong address,
int rw, uintptr_t retaddr)
{
cs->uc->invalid_error = UC_ERR_EXCEPTION;
cs->uc->invalid_addr = address;
cpu_exit(cs->uc->cpu);
cpu_loop_exit_restore(cs, retaddr);
}
static uc_mem_type rw_to_mem_type(int rw)
{
switch (rw) {
case MMU_DATA_LOAD:
return UC_MEM_READ;
case MMU_DATA_STORE:
return UC_MEM_WRITE;
case MMU_INST_FETCH:
return UC_MEM_FETCH;
default:
return UC_MEM_READ;
}
}
static int perms_to_prot(int perms)
{
int ret = 0;
if (perms & UC_PROT_READ) {
ret |= PAGE_READ;
}
if (perms & UC_PROT_WRITE) {
ret |= PAGE_WRITE;
}
if (perms & UC_PROT_EXEC) {
ret |= PAGE_EXEC;
}
return ret;
}
bool unicorn_fill_tlb(CPUState *cs, vaddr address, int size,
MMUAccessType rw, int mmu_idx,
bool probe, uintptr_t retaddr)
{
bool handled = false;
bool ret = false;
struct uc_struct *uc = cs->uc;
uc_tlb_entry e;
struct hook *hook;
HOOK_FOREACH_VAR_DECLARE;
HOOK_FOREACH(uc, hook, UC_HOOK_TLB_FILL) {
if (hook->to_delete) {
continue;
}
if (!HOOK_BOUND_CHECK(hook, address)) {
continue;
}
handled = true;
if ((ret = ((uc_cb_tlbevent_t)hook->callback)(uc, address & TARGET_PAGE_MASK, rw_to_mem_type(rw), &e, hook->user_data))) {
break;
}
}
if (handled && !ret) {
goto tlb_miss;
}
if (!handled) {
e.paddr = address & TARGET_PAGE_MASK;
e.perms = UC_PROT_READ|UC_PROT_WRITE|UC_PROT_EXEC;
}
switch (rw) {
case MMU_DATA_LOAD:
ret = e.perms & UC_PROT_READ;
break;
case MMU_DATA_STORE:
ret = e.perms & UC_PROT_WRITE;
break;
case MMU_INST_FETCH:
ret = e.perms & UC_PROT_EXEC;
break;
default:
ret = false;
break;
}
if (ret) {
tlb_set_page(cs, address & TARGET_PAGE_MASK, e.paddr & TARGET_PAGE_MASK, perms_to_prot(e.perms), mmu_idx, TARGET_PAGE_SIZE);
return true;
}
tlb_miss:
if (probe) {
return false;
}
raise_mmu_exception(cs, address, rw, retaddr);
return false;
}

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _sparc
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_sparc
#define uc_add_inline_hook uc_add_inline_hook_sparc
#define uc_del_inline_hook uc_del_inline_hook_sparc
#define tb_invalidate_phys_range tb_invalidate_phys_range_sparc

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _sparc64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_sparc64
#define uc_add_inline_hook uc_add_inline_hook_sparc64
#define uc_del_inline_hook uc_del_inline_hook_sparc64
#define tb_invalidate_phys_range tb_invalidate_phys_range_sparc64

View File

@ -2081,6 +2081,7 @@ void arm_cpu_class_init(struct uc_struct *uc, CPUClass *oc)
cc->asidx_from_attrs = arm_asidx_from_attrs;
cc->tcg_initialize = arm_translate_init;
cc->tlb_fill = arm_cpu_tlb_fill;
cc->tlb_fill_cpu = arm_cpu_tlb_fill;
cc->debug_excp_handler = arm_debug_excp_handler;
cc->do_unaligned_access = arm_cpu_do_unaligned_access;
}

View File

@ -5067,6 +5067,7 @@ static void x86_cpu_common_class_init(struct uc_struct *uc, CPUClass *oc, void *
cc->cpu_exec_exit = x86_cpu_exec_exit;
cc->tcg_initialize = tcg_x86_init;
cc->tlb_fill = x86_cpu_tlb_fill;
cc->tlb_fill_cpu = x86_cpu_tlb_fill;
}
X86CPU *cpu_x86_init(struct uc_struct *uc)

View File

@ -232,6 +232,7 @@ static void m68k_cpu_class_init(CPUClass *c)
cc->cpu_exec_interrupt = m68k_cpu_exec_interrupt;
cc->set_pc = m68k_cpu_set_pc;
cc->tlb_fill = m68k_cpu_tlb_fill;
cc->tlb_fill_cpu = m68k_cpu_tlb_fill;
cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug;
cc->tcg_initialize = m68k_tcg_init;
}

View File

@ -148,6 +148,7 @@ static void mips_cpu_class_init(CPUClass *c)
cc->get_phys_page_debug = mips_cpu_get_phys_page_debug;
cc->tcg_initialize = mips_tcg_init;
cc->tlb_fill = mips_cpu_tlb_fill;
cc->tlb_fill_cpu = mips_cpu_tlb_fill;
}
MIPSCPU *cpu_mips_init(struct uc_struct *uc)

View File

@ -10254,6 +10254,7 @@ static void ppc_cpu_class_init(struct uc_struct *uc, CPUClass *oc)
cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug;
cc->tcg_initialize = ppc_translate_init;
cc->tlb_fill = ppc_cpu_tlb_fill;
cc->tlb_fill_cpu = ppc_cpu_tlb_fill;
cc->cpu_exec_enter = ppc_cpu_exec_enter;
cc->cpu_exec_exit = ppc_cpu_exec_exit;
}

View File

@ -308,6 +308,7 @@ static void riscv_cpu_class_init(struct uc_struct *uc, CPUClass *c, void *data)
cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
cc->tcg_initialize = riscv_translate_init;
cc->tlb_fill = riscv_cpu_tlb_fill;
cc->tlb_fill_cpu = riscv_cpu_tlb_fill;
}
typedef struct CPUModelInfo {

View File

@ -234,6 +234,7 @@ static void s390_cpu_class_init(struct uc_struct *uc, CPUClass *oc)
cc->do_unaligned_access = s390x_cpu_do_unaligned_access;
cc->tcg_initialize = s390x_translate_init;
cc->tlb_fill = s390_cpu_tlb_fill;
cc->tlb_fill_cpu = s390_cpu_tlb_fill;
// s390_cpu_model_class_register_props(oc);
}

View File

@ -505,6 +505,7 @@ static void sparc_cpu_class_init(struct uc_struct *uc, CPUClass *oc)
cc->set_pc = sparc_cpu_set_pc;
cc->synchronize_from_tb = sparc_cpu_synchronize_from_tb;
cc->tlb_fill = sparc_cpu_tlb_fill;
cc->tlb_fill_cpu = sparc_cpu_tlb_fill;
cc->do_unaligned_access = sparc_cpu_do_unaligned_access;
cc->get_phys_page_debug = sparc_cpu_get_phys_page_debug;
cc->tcg_initialize = sparc_tcg_init;

View File

@ -138,6 +138,7 @@ static void tricore_cpu_class_init(CPUClass *c)
cc->get_phys_page_debug = tricore_cpu_get_phys_page_debug;
cc->tlb_fill = tricore_cpu_tlb_fill;
cc->tlb_fill_cpu = tricore_cpu_tlb_fill;
cc->tcg_initialize = tricore_tcg_init;
}

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _tricore
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_tricore
#define uc_add_inline_hook uc_add_inline_hook_tricore
#define uc_del_inline_hook uc_del_inline_hook_tricore
#define tb_invalidate_phys_range tb_invalidate_phys_range_tricore

View File

@ -11,6 +11,9 @@
void vm_start(struct uc_struct*);
void tcg_exec_init(struct uc_struct *uc, unsigned long tb_size);
bool unicorn_fill_tlb(CPUState *cs, vaddr address, int size,
MMUAccessType rw, int mmu_idx,
bool probe, uintptr_t retaddr);
// return true on success, false on failure
static inline bool cpu_physical_mem_read(AddressSpace *as, hwaddr addr,
@ -91,6 +94,19 @@ static inline void target_page_init(struct uc_struct* uc)
uc->target_page_align = TARGET_PAGE_SIZE - 1;
}
static uc_err uc_set_tlb(struct uc_struct *uc, int mode) {
switch (mode) {
case UC_TLB_VIRTUAL:
uc->cpu->cc->tlb_fill = unicorn_fill_tlb;
return UC_ERR_OK;
case UC_TLB_CPU:
uc->cpu->cc->tlb_fill = uc->cpu->cc->tlb_fill_cpu;
return UC_ERR_OK;
default:
return UC_ERR_ARG;
}
}
void softfloat_init(void);
static inline void uc_common_init(struct uc_struct* uc)
{
@ -107,6 +123,7 @@ static inline void uc_common_init(struct uc_struct* uc)
uc->softfloat_initialize = softfloat_init;
uc->tcg_flush_tlb = tcg_flush_softmmu_tlb;
uc->memory_map_io = memory_map_io;
uc->set_tlb = uc_set_tlb;
if (!uc->release)
uc->release = release_common;

View File

@ -4,6 +4,7 @@
#ifndef UNICORN_ARCH_POSTFIX
#define UNICORN_ARCH_POSTFIX _x86_64
#endif
#define unicorn_fill_tlb unicorn_fill_tlb_x86_64
#define uc_add_inline_hook uc_add_inline_hook_x86_64
#define uc_del_inline_hook uc_del_inline_hook_x86_64
#define tb_invalidate_phys_range tb_invalidate_phys_range_x86_64

View File

@ -1,6 +1,25 @@
#include <unicorn/unicorn.h>
#include <stdio.h>
/*
* 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";
static void mmu_write_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data)
{
printf("write at 0x%lx: 0x%lx\n", address, value);
@ -83,7 +102,7 @@ static void x86_mmu_pt_set(uc_engine *uc, uint64_t vaddr, uint64_t paddr, uint64
uc_mem_write(uc, tlb_base + 0x3000 + pto, &pte, sizeof(pte));
}
static void x86_mmu_callback(uc_engine *uc, void *userdata)
static void x86_mmu_syscall_callback(uc_engine *uc, void *userdata)
{
uc_err err;
bool *parrent_done = userdata;
@ -99,6 +118,7 @@ static void x86_mmu_callback(uc_engine *uc, void *userdata)
break;
case 60:
/* exit */
*parrent_done = true;
uc_emu_stop(uc);
return;
default:
@ -107,7 +127,6 @@ static void x86_mmu_callback(uc_engine *uc, void *userdata)
}
if (!(*parrent_done)) {
*parrent_done = true;
rax = 27;
err = uc_reg_write(uc, UC_X86_REG_RAX, &rax);
if (err) {
@ -118,7 +137,7 @@ static void x86_mmu_callback(uc_engine *uc, void *userdata)
}
}
int main(void)
void cpu_tlb(void)
{
uint64_t tlb_base = 0x3000;
uint64_t rax, rip;
@ -129,24 +148,6 @@ int main(void)
uc_err err;
uc_hook h1, h2;
/*
* 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";
printf("Emulate x86 amd64 code with mmu enabled and switch mappings\n");
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
@ -154,13 +155,14 @@ int main(void)
printf("Failed on uc_open() with error returned: %u\n", err);
exit(1);
}
uc_ctl_tlb_mode(uc, UC_TLB_CPU);
err = uc_context_alloc(uc, &context);
if (err) {
printf("Failed on uc_context_alloc() with error returned: %u\n", err);
exit(1);
}
err = uc_hook_add(uc, &h1, UC_HOOK_INSN, &x86_mmu_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL);
err = uc_hook_add(uc, &h1, UC_HOOK_INSN, &x86_mmu_syscall_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL);
if (err) {
printf("Failed on uc_hook_add() with error returned: %u\n", err);
exit(1);
@ -272,4 +274,157 @@ int main(void)
}
printf("parrent result == %lu\n", parrent);
printf("child result == %lu\n", child);
uc_close(uc);
}
static bool virtual_tlb_callback(uc_engine *uc, uint64_t addr, uc_mem_type type, uc_tlb_entry *result, void *user_data)
{
bool *parrent_done = user_data;
printf("tlb lookup for address: 0x%lX\n", addr);
switch (addr & ~(0xfff)) {
case 0x2000:
result->paddr = 0x0;
result->perms = UC_PROT_EXEC;
return true;
case 0x4000:
if (*parrent_done) {
result->paddr = 0x2000;
} else {
result->paddr = 0x1000;
}
result->perms = UC_PROT_READ | UC_PROT_WRITE;
return true;
default:
break;
}
return false;
}
void virtual_tlb(void)
{
uint64_t rax, rip;
bool parrent_done = false;
uint64_t parrent, child;
uc_context *context;
uc_engine *uc;
uc_err err;
uc_hook h1, h2, h3;
printf("Emulate x86 amd64 code with virtual mmu\n");
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u\n", err);
exit(1);
}
uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL);
err = uc_context_alloc(uc, &context);
if (err) {
printf("Failed on uc_context_alloc() with error returned: %u\n", err);
exit(1);
}
err = uc_hook_add(uc, &h1, UC_HOOK_INSN, &x86_mmu_syscall_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL);
if (err) {
printf("Failed on uc_hook_add() with error returned: %u\n", err);
exit(1);
}
// Memory hooks are called after the mmu translation, so hook the physicall addresses
err = uc_hook_add(uc, &h2, UC_HOOK_MEM_WRITE, &mmu_write_callback, NULL, 0x1000, 0x3000);
if (err) {
printf("Faled on uc_hook_add() with error returned: %u\n", err);
}
printf("map code\n");
err = uc_mem_map(uc, 0x0, 0x1000, UC_PROT_ALL); //Code
if (err) {
printf("Failed on uc_mem_map() with error return: %u\n", err);
exit(1);
}
err = uc_mem_write(uc, 0x0, code, sizeof(code) - 1);
if (err) {
printf("Failed on uc_mem_wirte() with error return: %u\n", err);
exit(1);
}
printf("map parrent memory\n");
err = uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL); //Parrent
if (err) {
printf("Failed on uc_mem_map() with error return: %u\n", err);
exit(1);
}
printf("map child memory\n");
err = uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL); //Child
if (err) {
printf("failed to map child memory\n");
exit(1);
}
err = uc_hook_add(uc, &h3, UC_HOOK_TLB_FILL, virtual_tlb_callback, &parrent_done, 1, 0);
printf("run the parrent\n");
err = uc_emu_start(uc, 0x2000, 0x0, 0, 0);
if (err) {
printf("failed to run parrent\n");
exit(1);
}
printf("save the context for the child\n");
err = uc_context_save(uc, context);
printf("finish the parrent\n");
err = uc_reg_read(uc, UC_X86_REG_RIP, &rip);
if (err) {
printf("failed to read rip\n");
exit(1);
}
err = uc_emu_start(uc, rip, 0x0, 0, 0);
if (err) {
printf("failed to flush tlb\n");
exit(1);
}
printf("restore the context for the child\n");
err = uc_context_restore(uc, context);
if (err) {
printf("failed to restore context\n");
exit(1);
}
rax = 0;
parrent_done = true;
err = uc_reg_write(uc, UC_X86_REG_RAX, &rax);
if (err) {
printf("failed to write rax\n");
exit(1);
}
err = uc_ctl_flush_tlb(uc);
if (err) {
printf("failed to flush tlb\n");
exit(1);
}
err = uc_emu_start(uc, rip, 0x0, 0, 0);
if (err) {
printf("failed to run child\n");
exit(1);
}
err = uc_mem_read(uc, 0x1000, &parrent, sizeof(parrent));
if (err) {
printf("failed to read from parrent memory\n");
exit(1);
}
err = uc_mem_read(uc, 0x2000, &child, sizeof(child));
if (err) {
printf("failed to read from child memory\n");
exit(1);
}
printf("parrent result == %lu\n", parrent);
printf("child result == %lu\n", child);
uc_close(uc);
}
int main(void)
{
cpu_tlb();
virtual_tlb();
}

View File

@ -4,6 +4,7 @@ CMD_PATH=$(realpath $0)
SOURCE_DIR=$(dirname ${CMD_PATH})
COMMON_SYMBOLS="
unicorn_fill_tlb \
uc_add_inline_hook \
uc_del_inline_hook \
tb_invalidate_phys_range \

View File

@ -1384,6 +1384,36 @@ static void test_x86_mmu(void)
TEST_CHECK(child == 42);
}
static bool test_x86_vtlb_callback(uc_engine *uc, uint64_t addr, uc_mem_type type, uc_tlb_entry *result, void *user_data)
{
result->paddr = addr;
result->perms = UC_PROT_ALL;
return true;
}
static void test_x86_vtlb(void)
{
uc_engine *uc;
uc_hook hook;
char code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop;
// nop; nop; nop
uint64_t r_eip = 0;
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
OK(uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL));
OK(uc_hook_add(uc, &hook, UC_HOOK_TLB_FILL, test_x86_vtlb_callback, NULL, 1, 0));
OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0));
OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_eip));
TEST_CHECK(r_eip == code_start + 4);
OK(uc_close(uc));
}
TEST_LIST = {
{"test_x86_in", test_x86_in},
{"test_x86_out", test_x86_out},
@ -1428,4 +1458,5 @@ TEST_LIST = {
{"test_x86_lazy_mapping", test_x86_lazy_mapping},
{"test_x86_16_incorrect_ip", test_x86_16_incorrect_ip},
{"test_x86_mmu", test_x86_mmu},
{"test_x86_vtlb", test_x86_vtlb},
{NULL, NULL}};

23
uc.c
View File

@ -2377,6 +2377,19 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
}
break;
case UC_CTL_TLB_TYPE: {
UC_INIT(uc);
if (rw == UC_CTL_IO_WRITE) {
int mode = va_arg(args, int);
err = uc->set_tlb(uc, mode);
} else {
err = UC_ERR_ARG;
}
break;
}
default:
err = UC_ERR_ARG;
break;
@ -2387,6 +2400,16 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
return err;
}
gint cmp_vaddr(gconstpointer a, gconstpointer b, gpointer user_data)
{
uint64_t va = (uint64_t)a;
uint64_t vb = (uint64_t)b;
if (va == vb) {
return 0;
}
return va < vb ? -1 : 1;
}
#ifdef UNICORN_TRACER
uc_tracer *get_tracer()
{