diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b7e4ff0..6e467090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/uc_priv.h b/include/uc_priv.h index 60a9b486..1eb8f1a9 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -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; diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 54ffd251..3e1f9248 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -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. diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 7b8b40df..c678501b 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -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 diff --git a/qemu/arm.h b/qemu/arm.h index 871a604f..13e8ffee 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -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 diff --git a/qemu/include/hw/core/cpu.h b/qemu/include/hw/core/cpu.h index 097a7f63..e01ff76c 100644 --- a/qemu/include/hw/core/cpu.h +++ b/qemu/include/hw/core/cpu.h @@ -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); diff --git a/qemu/include/sysemu/cpus.h b/qemu/include/sysemu/cpus.h index ee8fb60d..37e7dd9c 100644 --- a/qemu/include/sysemu/cpus.h +++ b/qemu/include/sysemu/cpus.h @@ -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); diff --git a/qemu/m68k.h b/qemu/m68k.h index 1d0352c1..23f5d93a 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -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 diff --git a/qemu/mips.h b/qemu/mips.h index f1666d06..8aaa4de4 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -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 diff --git a/qemu/mips64.h b/qemu/mips64.h index bdcb4dae..30f6b0ac 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -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 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index ea1c1874..115aa427 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -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 diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 67b11060..552c968e 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -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 diff --git a/qemu/ppc.h b/qemu/ppc.h index eff4b69d..67e3c4b9 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -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 diff --git a/qemu/ppc64.h b/qemu/ppc64.h index c08ee2d6..36ffbc64 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -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 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index 432b9bb3..94a8fc02 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -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 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index f0aa87d1..bfc0bff4 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -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 diff --git a/qemu/s390x.h b/qemu/s390x.h index dcda0fd4..b4295cbc 100644 --- a/qemu/s390x.h +++ b/qemu/s390x.h @@ -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 diff --git a/qemu/softmmu/unicorn_vtlb.c b/qemu/softmmu/unicorn_vtlb.c new file mode 100644 index 00000000..3b629bda --- /dev/null +++ b/qemu/softmmu/unicorn_vtlb.c @@ -0,0 +1,106 @@ +#include +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/exec-all.h" +#include "uc_priv.h" + +#include + +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; +} diff --git a/qemu/sparc.h b/qemu/sparc.h index 5b57febc..7083a74f 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -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 diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 5a792deb..e70362dc 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -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 diff --git a/qemu/target/arm/cpu.c b/qemu/target/arm/cpu.c index 45540fe6..9bff7fff 100644 --- a/qemu/target/arm/cpu.c +++ b/qemu/target/arm/cpu.c @@ -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; } diff --git a/qemu/target/i386/cpu.c b/qemu/target/i386/cpu.c index 12953daa..56a0603c 100644 --- a/qemu/target/i386/cpu.c +++ b/qemu/target/i386/cpu.c @@ -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) diff --git a/qemu/target/m68k/cpu.c b/qemu/target/m68k/cpu.c index 915b82ac..19b31d93 100644 --- a/qemu/target/m68k/cpu.c +++ b/qemu/target/m68k/cpu.c @@ -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; } diff --git a/qemu/target/mips/cpu.c b/qemu/target/mips/cpu.c index 4ddf65e0..63c5cfa3 100644 --- a/qemu/target/mips/cpu.c +++ b/qemu/target/mips/cpu.c @@ -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) diff --git a/qemu/target/ppc/translate_init.inc.c b/qemu/target/ppc/translate_init.inc.c index caef69be..02eaee0a 100644 --- a/qemu/target/ppc/translate_init.inc.c +++ b/qemu/target/ppc/translate_init.inc.c @@ -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; } diff --git a/qemu/target/riscv/cpu.c b/qemu/target/riscv/cpu.c index d58204ad..6197abd0 100644 --- a/qemu/target/riscv/cpu.c +++ b/qemu/target/riscv/cpu.c @@ -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 { diff --git a/qemu/target/s390x/cpu.c b/qemu/target/s390x/cpu.c index f6ccf9d1..00e50831 100644 --- a/qemu/target/s390x/cpu.c +++ b/qemu/target/s390x/cpu.c @@ -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); } diff --git a/qemu/target/sparc/cpu.c b/qemu/target/sparc/cpu.c index 489819bc..87b531f7 100644 --- a/qemu/target/sparc/cpu.c +++ b/qemu/target/sparc/cpu.c @@ -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; diff --git a/qemu/target/tricore/cpu.c b/qemu/target/tricore/cpu.c index a55ca63e..b016c1a4 100644 --- a/qemu/target/tricore/cpu.c +++ b/qemu/target/tricore/cpu.c @@ -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; } diff --git a/qemu/tricore.h b/qemu/tricore.h index 3f41fc76..a7219654 100644 --- a/qemu/tricore.h +++ b/qemu/tricore.h @@ -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 diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 3e06ccac..d7802b3b 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -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; diff --git a/qemu/x86_64.h b/qemu/x86_64.h index ef4f4ad4..aa03182d 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -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 diff --git a/samples/sample_mmu.c b/samples/sample_mmu.c index ea39b87c..84baa2a0 100644 --- a/samples/sample_mmu.c +++ b/samples/sample_mmu.c @@ -1,6 +1,25 @@ #include #include +/* + * 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(); } diff --git a/symbols.sh b/symbols.sh index f2d0a114..cbf5f0db 100755 --- a/symbols.sh +++ b/symbols.sh @@ -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 \ diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 9bcb7d81..9edb5db7 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -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}}; diff --git a/uc.c b/uc.c index a1a71366..da576f9f 100644 --- a/uc.c +++ b/uc.c @@ -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() {