The core code interfaces to translate-all, cpu-tlb and cpu-exec are virtualised. This prepare support for multi-arch where these modules are multi-compiled for the different target backends and will need to co-exist.
The names of functions are not changed. They still have their generic names and can be linked to the final build as-is. In multi-arch, the arch-obj components have all symbols localised which includes all of the function defs for these hooks (despite them still having generic names and global linkage). So it is up to the target specific sub-class to install theses hooks. Multiple targets can do this and then link together. The CPU base class will harmlessly set the hooks to NULL but in multi-arch these must be overridden. The hooks are only called in MULTI_ARCH case, which has the advantage of preserving the devirtualised behaviour of these hooks. It also guards against unconverted archs that do not set the QOM_HOOKS, i.e. an unconverted arch will not attempt to call the virtualised hooks. Signed-off-by: Peter Crosthwaite <crosthwaite.pe...@gmail.com> --- Changed since RFC v2: * Major rewrite * remove tcg_ctx ptr (RTH) virtualise cpu_get_tb_cpu_state (Paolo) Don't use virtualised hooks outside of multi-arch (Paolo) Don't stub or install default hooks (woot!) Don't move header definitions Split API changes to separate patches Compile out hooks when cpu.h is unavailable Compile out hooks that dont exist in linux user mode remove tb_invalidate_phys_page_range from hooks (linux-u only) Add monitor hooks, dump_exec_info, dump_opcount_info Add tcg_enabled() as a hook --- cpus.c | 6 ++-- exec.c | 29 ++++++++--------- gdbstub.c | 2 +- include/exec/cpu-common.h | 2 -- include/exec/exec-all.h | 5 +++ include/qom/cpu.h | 79 +++++++++++++++++++++++++++++++++++++++++++++++ monitor.c | 4 +-- 7 files changed, 105 insertions(+), 22 deletions(-) diff --git a/cpus.c b/cpus.c index cd25c8c..738b96f 100644 --- a/cpus.c +++ b/cpus.c @@ -1116,7 +1116,7 @@ static void qemu_cpu_kick_thread(CPUState *cpu) void qemu_cpu_kick(CPUState *cpu) { qemu_cond_broadcast(cpu->halt_cond); - if (!tcg_enabled() && !cpu->thread_kicked) { + if (!CPU_HOOK(cpu, tcg_enabled)() && !cpu->thread_kicked) { qemu_cpu_kick_thread(cpu); cpu->thread_kicked = true; } @@ -1310,7 +1310,7 @@ void qemu_init_vcpu(CPUState *cpu) cpu->stopped = true; if (kvm_enabled()) { qemu_kvm_start_vcpu(cpu); - } else if (tcg_enabled()) { + } else if (CPU_HOOK(cpu, tcg_enabled)()) { qemu_tcg_init_vcpu(cpu); } else { qemu_dummy_start_vcpu(cpu); @@ -1393,7 +1393,7 @@ static int tcg_cpu_exec(CPUState *cpu) cpu->icount_decr.u16.low = decr; cpu->icount_extra = count; } - ret = cpu_exec(cpu); + ret = CPU_HOOK(cpu, cpu_exec)(cpu); #ifdef CONFIG_PROFILER tcg_time += profile_getclock() - ti; #endif diff --git a/exec.c b/exec.c index c37475a..4a722ce 100644 --- a/exec.c +++ b/exec.c @@ -445,7 +445,7 @@ static int cpu_common_post_load(void *opaque, int version_id) /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the version_id is increased. */ cpu->interrupt_request &= ~0x01; - tlb_flush(cpu, 1); + CPU_HOOK(cpu, tlb_flush)(cpu, 1); return 0; } @@ -463,7 +463,7 @@ static bool cpu_common_exception_index_needed(void *opaque) { CPUState *cpu = opaque; - return tcg_enabled() && cpu->exception_index != -1; + return CPU_HOOK(cpu, tcg_enabled)() && cpu->exception_index != -1; } static const VMStateDescription vmstate_cpu_common_exception_index = { @@ -621,8 +621,8 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { hwaddr phys = cpu_get_phys_page_debug(cpu, pc); if (phys != -1) { - tb_invalidate_phys_addr(cpu->as, - phys | (pc & ~TARGET_PAGE_MASK)); + CPU_HOOK(cpu, tb_invalidate_phys_addr)(cpu->as, + phys | (pc & ~TARGET_PAGE_MASK)); } } #endif @@ -674,7 +674,7 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); } - tlb_flush_page(cpu, addr); + CPU_HOOK(cpu, tlb_flush_page)(cpu, addr); if (watchpoint) *watchpoint = wp; @@ -702,7 +702,7 @@ void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) { QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); - tlb_flush_page(cpu, watchpoint->vaddr); + CPU_HOOK(cpu, tlb_flush_page)(cpu, watchpoint->vaddr); g_free(watchpoint); } @@ -814,7 +814,7 @@ void cpu_single_step(CPUState *cpu, int enabled) } else { /* must flush all the translated code to avoid inconsistencies */ /* XXX: only flush what is necessary */ - tb_flush(cpu); + CPU_HOOK(cpu,tb_flush)(cpu); } } } @@ -906,7 +906,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) assert(block == qemu_get_ram_block(end - 1)); start1 = (uintptr_t)ramblock_ptr(block, start - block->offset); CPU_FOREACH(cpu) { - tlb_reset_dirty(cpu, start1, length); + CPU_HOOK(cpu, tlb_reset_dirty)(cpu, start1, length); } rcu_read_unlock(); } @@ -1876,7 +1876,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, uint64_t val, unsigned size) { if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - tb_invalidate_phys_page_fast(ram_addr, size); + CPU_HOOK(current_cpu, tb_invalidate_phys_page_fast)(ram_addr, size); } switch (size) { case 1: @@ -1899,7 +1899,8 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (!cpu_physical_memory_is_clean(ram_addr)) { - tlb_set_dirty(current_cpu, current_cpu->mem_io_vaddr); + CPU_HOOK(current_cpu, tlb_set_dirty)(current_cpu, + current_cpu->mem_io_vaddr); } } @@ -1945,13 +1946,13 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) wp->hitattrs = attrs; if (!cpu->watchpoint_hit) { cpu->watchpoint_hit = wp; - tb_check_watchpoint(cpu); + CPU_HOOK(cpu, tb_check_watchpoint)(cpu); if (wp->flags & BP_STOP_BEFORE_ACCESS) { cpu->exception_index = EXCP_DEBUG; cpu_loop_exit(cpu); } else { - cpu_get_tb_cpu_state(env, &pc, &cs_base, &cpu_flags); - tb_gen_code(cpu, pc, cs_base, cpu_flags, 1); + CPU_HOOK(cpu, cpu_get_tb_cpu_state)(env, &pc, &cs_base, &cpu_flags); + CPU_HOOK(cpu, tb_gen_code)(cpu, pc, cs_base, cpu_flags, 1); cpu_resume_from_signal(cpu, NULL); } } @@ -2344,7 +2345,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, cpu_physical_memory_range_includes_clean(addr, length, dirty_log_mask); } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { - tb_invalidate_phys_range(addr, addr + length); + CPU_HOOK(current_cpu, tb_invalidate_phys_range)(addr, addr + length); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); diff --git a/gdbstub.c b/gdbstub.c index 92b2f81..2f280e6 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1257,7 +1257,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) cpu->watchpoint_hit = NULL; goto send_packet; } - tb_flush(cpu); + CPU_HOOK(cpu, tb_flush)(cpu); ret = GDB_SIGNAL_TRAP; break; case RUN_STATE_PAUSED: diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 47d416d..d35601e 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -53,9 +53,7 @@ typedef uintptr_t ram_addr_t; # define RAM_ADDR_FMT "%" PRIxPTR #endif -#ifndef CONFIG_USER_ONLY typedef ram_addr_t tb_page_addr_t; -#endif extern ram_addr_t ram_size; ram_addr_t get_current_ram_size(void); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8fd0540..0c70c17 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -363,4 +363,9 @@ extern volatile sig_atomic_t exit_request; #if !defined(CONFIG_USER_ONLY) void migration_bitmap_extend(ram_addr_t old, ram_addr_t new); #endif + +#ifdef NEED_CPU_H +#include "translate-all.h" +#endif + #endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index eb12d26..9fbae16 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -212,6 +212,8 @@ struct kvm_run; #define TB_JMP_CACHE_BITS 12 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) +#define MAX_CPU_HOOKS 16 + /** * CPUState: * @cpu_index: CPU index (informative). @@ -320,8 +322,85 @@ struct CPUState { (absolute value) offset as small as possible. This reduces code size, especially for hosts without large memory offsets. */ volatile sig_atomic_t tcg_exit_req; + + void (*cpu_get_tb_cpu_state)(void *env, void *pc, void *cs_base, + int *pflags); + + union { + struct { +#ifdef NEED_CPU_H + void (*tb_check_watchpoint)(CPUState *cpu); + void (*tb_flush)(CPUState *cpu); + struct TranslationBlock *(*tb_gen_code)(CPUState *cpu, + target_ulong pc, + target_ulong cs_base, + int flags, int cflags); +#ifndef CONFIG_USER_ONLY + void (*tlb_flush)(CPUState *cpu, int flush_global); + void (*tlb_flush_page)(CPUState *cpu, target_ulong addr); + void (*tlb_set_dirty)(CPUState *cpu, target_ulong addr); + void (*tlb_reset_dirty)(CPUState *cpu, ram_addr_t start, + ram_addr_t length); + + void (*tb_invalidate_phys_addr)(AddressSpace *as, hwaddr addr); + void (*tb_invalidate_phys_page_fast)(tb_page_addr_t start, int len); + void (*tb_invalidate_phys_range)(tb_page_addr_t start, tb_page_addr_t end); + + void (*dump_exec_info)(FILE *f, fprintf_function cpu_fprintf); + void (*dump_opcount_info)(FILE *f, fprintf_function cpu_fprintf); +#endif + int (*cpu_exec)(CPUState *cpu); + + bool (*tcg_enabled)(void); +#endif + void (*hooks_end_of_list)(void); + }; + void (*hooks_dummy[MAX_CPU_HOOKS + 1])(void); + }; }; +QEMU_BUILD_BUG_ON(offsetof(CPUState, hooks_end_of_list) > + offsetof(CPUState, hooks_dummy[MAX_CPU_HOOKS + 1])) + +#ifndef CONFIG_USER_ONLY +#define IFN_USER_ONLY(a) a +#else +#define IFN_USER_ONLY(a) +#endif + +#define CPU_SET_QOM_HOOKS(cpu) do { \ + cpu->cpu_get_tb_cpu_state = \ + (void (*)(void *, void *, void *, int *))cpu_get_tb_cpu_state; \ + \ + cpu->tb_check_watchpoint = tb_check_watchpoint; \ + cpu->tb_flush = tb_flush; \ + cpu->tb_gen_code = tb_gen_code; \ + \ + IFN_USER_ONLY( \ + cpu->tlb_flush = tlb_flush; \ + cpu->tlb_flush_page = tlb_flush_page; \ + cpu->tlb_set_dirty = tlb_set_dirty; \ + cpu->tlb_reset_dirty = tlb_reset_dirty; \ + \ + cpu->tb_invalidate_phys_addr = tb_invalidate_phys_addr; \ + cpu->tb_invalidate_phys_page_fast = tb_invalidate_phys_page_fast; \ + cpu->tb_invalidate_phys_range = tb_invalidate_phys_range; \ + \ + cpu->dump_exec_info = dump_exec_info; \ + cpu->dump_opcount_info = dump_opcount_info; \ + ) \ + \ + cpu->cpu_exec = cpu_exec; \ + \ + cpu->tcg_enabled = tcg_enabled; \ +} while (0); + +#ifdef TARGET_MULTI +#define CPU_HOOK(cpu, fn)(cpu->fn) +#else +#define CPU_HOOK(cpu, fn)(fn) +#endif + QTAILQ_HEAD(CPUTailQ, CPUState); extern struct CPUTailQ cpus; #define CPU_NEXT(cpu) QTAILQ_NEXT(cpu, node) diff --git a/monitor.c b/monitor.c index f283035..cb66692 100644 --- a/monitor.c +++ b/monitor.c @@ -962,13 +962,13 @@ static void hmp_info_registers(Monitor *mon, const QDict *qdict) static void hmp_info_jit(Monitor *mon, const QDict *qdict) { - dump_exec_info((FILE *)mon, monitor_fprintf); + CPU_HOOK(mon_get_cpu(), dump_exec_info)((FILE *)mon, monitor_fprintf); dump_drift_info((FILE *)mon, monitor_fprintf); } static void hmp_info_opcount(Monitor *mon, const QDict *qdict) { - dump_opcount_info((FILE *)mon, monitor_fprintf); + CPU_HOOK(mon_get_cpu(), dump_opcount_info)((FILE *)mon, monitor_fprintf); } static void hmp_info_history(Monitor *mon, const QDict *qdict) -- 1.9.1