From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KszD4-0006nv-4T for qemu-devel@nongnu.org; Thu, 23 Oct 2008 08:22:38 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KszD2-0006nX-Og for qemu-devel@nongnu.org; Thu, 23 Oct 2008 08:22:37 -0400 Received: from [199.232.76.173] (port=51680 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KszD2-0006nU-IM for qemu-devel@nongnu.org; Thu, 23 Oct 2008 08:22:36 -0400 Received: from mx2.redhat.com ([66.187.237.31]:54589) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1KszD1-00066g-NY for qemu-devel@nongnu.org; Thu, 23 Oct 2008 08:22:36 -0400 From: Glauber Costa Date: Thu, 23 Oct 2008 12:18:44 -0200 Message-Id: <1224771556-11146-1-git-send-email-glommer@redhat.com> Subject: [Qemu-devel] [PATCH 0/32] New shot at accelerators Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: jan.kiszka@siemens.com, aliguori@us.ibm.com, jes@sgi.com, avi@qumranet.com, dmitry.baryshkov@siemens.com Hi guys, I'm sending here a new version of the accel patch. It comprises the cleaned up version I've last sent, plus a few additions You may want to pay special attention to the additions, since they are newcomers to the series. To ease your job, they are: 0001-use-anonymous-memory-for-kqemu.patch 0002-protect-exec-all.h-frm-multiple-inclusion.patch 0003-change-definition-of-FILE-for-linux.patch 0005-use-more-meaningful-values-for-kqemu_cpu_exec.patch 0025-provide-accel-hook-for-cpu_exec.patch 0026-provide-two-accelerators-for-kqemu.patch 0027-arch-specific-hooks-for-accelerator.patch 0028-iret-arch-specific-accelerator.patch 0029-sysret-sysexit-arch-specific-accelerator.patch 0030-lcall-lret-arch-specific-accel-hooks.patch 0031-remove-kqemu_is_ok-tests.patch 0032-clean-up-kqemu-code.patch git users can get a snapshot of it located at git://git.kernel.org/pub/scm/virt/glommer/qemu-accel.git accel-v2 diffstat and OBP (One Big Patch ;-)) attached. ------ Makefile.target | 2 +- accel.c | 51 +++++++++ accel.h | 171 ++++++++++++++++++++++++++++ cpu-all.h | 9 +- cpu-exec.c | 47 +++----- dyngen-exec.h | 6 + exec-all.h | 34 +----- exec.c | 115 ++++++++++---------- hw/pc.c | 13 +-- kqemu.c | 284 +++++++++++++++++++++++++++++++++++++++++++---- kqemu.h | 21 ++++ monitor.c | 70 +++++------- osdep.c | 111 ------------------ softmmu_template.h | 10 +- sysemu.h | 5 - target-i386/accel86.h | 60 ++++++++++ target-i386/cpu.h | 13 -- target-i386/helper.c | 8 +- target-i386/op_helper.c | 52 ++------- vl.c | 93 ++++++---------- 20 files changed, 736 insertions(+), 439 deletions(-) diff --git a/Makefile.target b/Makefile.target index e2edf9d..623ecd8 100644 --- a/Makefile.target +++ b/Makefile.target @@ -188,7 +188,7 @@ all: $(PROGS) ######################################################### # cpu emulator library LIBOBJS=exec.o kqemu.o translate-all.o cpu-exec.o\ - translate.o host-utils.o + translate.o host-utils.o accel.o ifdef CONFIG_DYNGEN_OP exec.o: dyngen-opc.h LIBOBJS+=op.o diff --git a/accel.c b/accel.c new file mode 100644 index 0000000..8d635f0 --- /dev/null +++ b/accel.c @@ -0,0 +1,51 @@ +#include "hw/hw.h" +#include "exec-all.h" +#include "accel.h" + +QEMUAccel *current_accel; +QEMUCont *head = NULL; + +int _accel_nop(void) +{ + return 0; +} + +int noaccel_info(CPUState *env, char *buf) +{ + return snprintf(buf, MAX_INFO_BUF, "no accelerator present.\n"); +} + +CPUState *noaccel_get_env(void) +{ + return qemu_mallocz(sizeof(CPUState)); +} + +int noaccel_cpu_exec(CPUState *env) +{ + return EXEC_EXIT_SOFTMMU; +} + +#define accel_nop ((void *)_accel_nop) + +/* Accelerator wrapper for the no-accel (raw qemu) case */ +QEMUAccel noaccel = { + .name = "none", + .cpu_interrupt = accel_nop, + .init_env = accel_nop, + .get_env = noaccel_get_env, + .start = accel_nop, + .flush_cache = accel_nop, + .flush_page = accel_nop, + .info = noaccel_info, + .profile = accel_nop, + .set_notdirty = accel_nop, + .modify_page = accel_nop, +#ifndef CONFIG_USER_ONLY + .get_real_ticks = cpu_get_ticks, +#endif + .register_physical_memory = accel_nop, + .trace_io = accel_nop, + .break_loop = accel_nop, + .cpu_exec = noaccel_cpu_exec, +}; + diff --git a/accel.h b/accel.h new file mode 100644 index 0000000..00a495c --- /dev/null +++ b/accel.h @@ -0,0 +1,171 @@ +#ifndef _ACCEL_H_ +#define _ACCEL_H_ + +#define MAX_INFO_BUF 1024 + +typedef struct QEMUAccel { + char *name; + void (*cpu_interrupt)(CPUState *env); + CPUState *(*get_env)(void); + void (*init_env)(CPUState *env); + int (*start)(int cpus); + void (*flush_cache)(CPUState *env, int global); + void (*flush_page)(CPUState *env, target_ulong addr); + int (*info)(CPUState *env, char *buf); + int (*profile)(CPUState *env, char *buf); + void (*set_notdirty)(ram_addr_t addr); + void (*modify_page)(ram_addr_t addr, int dirty_flags); +#ifndef CONFIG_USER_ONLY + uint64_t (*get_real_ticks)(void); +#endif + void (*register_physical_memory)(uint64_t start_addr, + ram_addr_t size, ram_addr_t phys_offset); + void (*trace_io)(CPUState *env); + int (*break_loop)(CPUState *env); + int (*cpu_exec)(CPUState *env); + void *arch; /* arch-specific accel functions */ +} QEMUAccel; + +typedef struct QEMUCont { + QEMUAccel *acc; + int active; + struct QEMUCont *next; +} QEMUCont; + +extern QEMUAccel *current_accel; +extern QEMUAccel noaccel; +#ifdef USE_KQEMU +extern QEMUAccel kqemu_accel; +extern QEMUAccel kqemu_kernel_accel; +#endif + +extern QEMUCont *head; +void *qemu_mallocz(size_t size); +extern CPUState *noaccel_get_env(void); + +static inline int register_qemu_accel(QEMUAccel *accel) +{ + QEMUCont *new, *tmp, *last = NULL; + + for (tmp = head, last; tmp; tmp = tmp->next) { + /* we disallow registering the same accelerator twice */ + if (tmp->acc == accel) + return -1; + + if (!tmp->next) + last = tmp; + } + + new = qemu_mallocz(sizeof(*head)); + + new->acc = accel; + new->active = 0; + new->next = NULL; + + if (!head) + head = new; + else + last->next = new; + + return 0; +} + +static inline QEMUCont *get_accel_head(void) +{ + return head; +} + +static inline void accel_cpu_interrupt(CPUState *env) +{ + current_accel->cpu_interrupt(env); +} + +static inline int accel_start(int cpus) +{ + int status = -1; + /* The top accelerator in the list gets tried first, but if it fails, + * keep trying until one of them succeeds or we exhaust the list */ + QEMUCont *tmp = head; + while (tmp) { + if (tmp->acc && tmp->acc->start && (!(tmp->acc->start(cpus))) ) { + tmp->active = 1; + current_accel = tmp->acc; + status = 0; + break; + } + tmp = tmp->next; + } + return status; +} + +static inline CPUState *accel_get_env(void) +{ + return current_accel->get_env(); +} + +static inline void accel_init_env(CPUState *env) +{ + current_accel->init_env(env); +} + +static inline void accel_flush_cache(CPUState *env, int global) +{ + current_accel->flush_cache(env, global); +} + +static inline void accel_flush_page(CPUState *env, target_ulong addr) +{ + current_accel->flush_page(env, addr); +} + +static inline int accel_info(CPUState *env, char *buf) +{ + return current_accel->info(env, buf); +} + +static inline int accel_profile(CPUState *env, char *buf) +{ + return current_accel->profile(env, buf); +} + +static inline void accel_set_notdirty(target_ulong addr) +{ + current_accel->set_notdirty(addr); +} + +static inline void accel_modify_page(target_ulong addr, int dirty_flags) +{ + current_accel->modify_page(addr, dirty_flags); +} + +int64_t cpu_get_ticks(void); + +#ifndef CONFIG_USER_ONLY +static inline uint64_t accel_get_real_ticks(void) +{ + return current_accel->get_real_ticks(); +} +#endif + +static inline void accel_register_phys_mem(uint64_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) +{ + current_accel->register_physical_memory(start_addr, size, phys_offset); +} + +static inline void accel_trace_io(CPUState *env) +{ + current_accel->trace_io(env); +} + +static inline int accel_break_loop(CPUState *env) +{ + return current_accel->break_loop(env); +} + +static inline int accel_cpu_exec(CPUState *env) +{ + return current_accel->cpu_exec(env); +} +#endif diff --git a/cpu-all.h b/cpu-all.h index cdd79bc..edefb45 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -863,6 +863,10 @@ extern ram_addr_t ram_size; typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value); typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr); +/* this is a private version, meant for internal use of accelerators */ +void __cpu_register_physical_memory(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset); void cpu_register_physical_memory(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset); @@ -1077,14 +1081,9 @@ static inline int64_t profile_getclock(void) return cpu_get_real_ticks(); } -extern int64_t kqemu_time, kqemu_time_start; extern int64_t qemu_time, qemu_time_start; extern int64_t tlb_flush_time; -extern int64_t kqemu_exec_count; extern int64_t dev_time; -extern int64_t kqemu_ret_int_count; -extern int64_t kqemu_ret_excp_count; -extern int64_t kqemu_ret_intr_count; #endif #endif /* CPU_ALL_H */ diff --git a/cpu-exec.c b/cpu-exec.c index 6d4dcdd..a0b6055 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -36,6 +36,7 @@ #include #include #endif +#include "accel.h" #if defined(__sparc__) && !defined(HOST_SOLARIS) // Work around ugly bugs in glibc that mangle global register contents @@ -335,31 +336,23 @@ int cpu_exec(CPUState *env1) } env->exception_index = -1; } -#ifdef USE_KQEMU - if (kqemu_is_ok(env) && env->interrupt_request == 0) { - int ret; - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); - ret = kqemu_cpu_exec(env); - /* put eflags in CPU temporary format */ - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((env->eflags >> 10) & 1)); - CC_OP = CC_OP_EFLAGS; - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - if (ret == 1) { - /* exception */ - longjmp(env->jmp_env, 1); - } else if (ret == 2) { - /* softmmu execution needed */ + + env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); + ret = accel_cpu_exec(env); + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + if (ret == EXEC_EXIT_INTR) { + /* exception */ + longjmp(env->jmp_env, 1); + } else if (ret == EXEC_EXIT_SOFTMMU) { + /* softmmu execution needed */ + } else { + if (env->interrupt_request != 0) { + /* hardware interrupt will be executed just after */ } else { - if (env->interrupt_request != 0) { - /* hardware interrupt will be executed just after */ - } else { - /* otherwise, we restart */ - longjmp(env->jmp_env, 1); - } + /* otherwise, we restart */ + longjmp(env->jmp_env, 1); } } -#endif next_tb = 0; /* force lookup of first TB */ for(;;) { @@ -605,7 +598,7 @@ int cpu_exec(CPUState *env1) { if (next_tb != 0 && #ifdef USE_KQEMU - (env->kqemu_enabled != 2) && + (!kqemu_kernel_enabled(env)) && #endif tb->page_addr[1] == -1) { tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); @@ -653,13 +646,7 @@ int cpu_exec(CPUState *env1) } /* reset soft MMU for next block (it can currently only be set by a memory fault) */ -#if defined(USE_KQEMU) -#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000) - if (kqemu_is_ok(env) && - (cpu_get_time_fast() - env->last_io_time) >= MIN_CYCLE_BEFORE_SWITCH) { - cpu_loop_exit(); - } -#endif + accel_break_loop(env); } /* for(;;) */ } else { env_to_regs(); diff --git a/dyngen-exec.h b/dyngen-exec.h index 9260b6f..826ff46 100644 --- a/dyngen-exec.h +++ b/dyngen-exec.h @@ -27,6 +27,10 @@ #define _FILEDEFED #endif +#ifdef __linux__ +#define __FILE_defined +#endif + /* NOTE: standard headers should be used with special care at this point because host CPU registers are used as global variables. Some host headers do not allow that. */ @@ -84,6 +88,8 @@ typedef void * host_reg_t; #ifdef _BSD typedef struct __sFILE FILE; +#elif defined(__linux__) +typedef struct _IO_FILE FILE; #else typedef struct FILE FILE; #endif diff --git a/exec-all.h b/exec-all.h index 6609c9a..fa4a4eb 100644 --- a/exec-all.h +++ b/exec-all.h @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _EXEC_ALL_H_ +#define _EXEC_ALL_H_ /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS @@ -354,34 +356,8 @@ static inline int can_do_io(CPUState *env) } #endif -#ifdef USE_KQEMU -#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG)) - -#define MSR_QPI_COMMBASE 0xfabe0010 - -int kqemu_init(CPUState *env); -int kqemu_cpu_exec(CPUState *env); -void kqemu_flush_page(CPUState *env, target_ulong addr); -void kqemu_flush(CPUState *env, int global); -void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr); -void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr); -void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, - ram_addr_t phys_offset); -void kqemu_cpu_interrupt(CPUState *env); -void kqemu_record_dump(void); - -extern uint32_t kqemu_comm_base; - -static inline int kqemu_is_ok(CPUState *env) -{ - return(env->kqemu_enabled && - (env->cr[0] & CR0_PE_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK) && - (env->eflags & IF_MASK) && - !(env->eflags & VM_MASK) && - (env->kqemu_enabled == 2 || - ((env->hflags & HF_CPL_MASK) == 3 && - (env->eflags & IOPL_MASK) != IOPL_MASK))); -} +#define EXEC_EXIT_DONE 0 +#define EXEC_EXIT_INTR 1 +#define EXEC_EXIT_SOFTMMU 2 #endif diff --git a/exec.c b/exec.c index f1fcec8..7fe7eeb 100644 --- a/exec.c +++ b/exec.c @@ -43,6 +43,8 @@ #include #endif +#include "accel.h" + //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH //#define DEBUG_TLB @@ -524,25 +526,31 @@ static int cpu_common_load(QEMUFile *f, void *opaque, int version_id) } #endif -void cpu_exec_init(CPUState *env) +int next_cpu_index(void) { CPUState **penv; - int cpu_index; + int cpu_index = 0; - env->next_cpu = NULL; penv = &first_cpu; - cpu_index = 0; + while (*penv != NULL) { penv = (CPUState **)&(*penv)->next_cpu; cpu_index++; } - env->cpu_index = cpu_index; + return cpu_index; +} + +void cpu_exec_init(CPUState *env) +{ + env->next_cpu = NULL; + env->cpu_index = next_cpu_index(); env->nb_watchpoints = 0; - *penv = env; + if (env->cpu_index == 0) + first_cpu = env; #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) - register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION, + register_savevm("cpu_common", env->cpu_index, CPU_COMMON_SAVE_VERSION, cpu_common_save, cpu_common_load, env); - register_savevm("cpu", cpu_index, CPU_SAVE_VERSION, + register_savevm("cpu", env->cpu_index, CPU_SAVE_VERSION, cpu_save, cpu_load, env); #endif } @@ -1427,6 +1435,7 @@ void cpu_single_step(CPUState *env, int enabled) tb_flush(env); } #endif + accel_cpu_interrupt(env); } /* enable or disable low levels log */ @@ -1678,11 +1687,8 @@ void tlb_flush(CPUState *env, int flush_global) memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - kqemu_flush(env, flush_global); - } -#endif + accel_flush_cache(env, flush_global); + tlb_flush_count++; } @@ -1724,11 +1730,7 @@ void tlb_flush_page(CPUState *env, target_ulong addr) tlb_flush_jmp_cache(env, addr); -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - kqemu_flush_page(env, addr); - } -#endif + accel_flush_page(env, addr); } /* update the TLBs so that writes to code in the virtual page 'addr' @@ -1775,18 +1777,14 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, if (length == 0) return; len = length >> TARGET_PAGE_BITS; -#ifdef USE_KQEMU - /* XXX: should not depend on cpu context */ - env = first_cpu; - if (env->kqemu_enabled) { - ram_addr_t addr; - addr = start; - for(i = 0; i < len; i++) { - kqemu_set_notdirty(env, addr); - addr += TARGET_PAGE_SIZE; - } + + ram_addr_t addr; + addr = start; + for(i = 0; i < len; i++) { + accel_set_notdirty(addr); + addr += TARGET_PAGE_SIZE; } -#endif + mask = ~dirty_flags; p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); for(i = 0; i < len; i++) @@ -2191,12 +2189,13 @@ static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, } \ } while (0) -/* register physical memory. 'size' must be a multiple of the target - page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an - io memory page */ -void cpu_register_physical_memory(target_phys_addr_t start_addr, - ram_addr_t size, - ram_addr_t phys_offset) +/* Use this version of cpu registering physical memory in accel-specific code. It exists + * to avoid chicken and egg problems with code that might need to register memory in qemu, + * but not with the underlying accelerator + */ +void __cpu_register_physical_memory(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) { target_phys_addr_t addr, end_addr; PhysPageDesc *p; @@ -2204,13 +2203,6 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, ram_addr_t orig_size = size; void *subpage; -#ifdef USE_KQEMU - /* XXX: should not depend on cpu context */ - env = first_cpu; - if (env->kqemu_enabled) { - kqemu_set_phys_mem(start_addr, size, phys_offset); - } -#endif size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (target_phys_addr_t)size; for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { @@ -2268,6 +2260,18 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, } } +/* register physical memory. 'size' must be a multiple of the target + page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an + io memory page */ +void cpu_register_physical_memory(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) +{ + accel_register_phys_mem(start_addr, size, phys_offset); + + __cpu_register_physical_memory(start_addr, size, phys_offset); +} + /* XXX: temporary until new memory mapping API */ ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr) { @@ -2383,12 +2387,11 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } + stb_p(phys_ram_base + ram_addr, val); -#ifdef USE_KQEMU - if (cpu_single_env->kqemu_enabled && - (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) - kqemu_modify_page(cpu_single_env, ram_addr); -#endif + + accel_modify_page(ram_addr, dirty_flags); + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; /* we remove the notdirty callback only if the code has been @@ -2408,12 +2411,11 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } + stw_p(phys_ram_base + ram_addr, val); -#ifdef USE_KQEMU - if (cpu_single_env->kqemu_enabled && - (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) - kqemu_modify_page(cpu_single_env, ram_addr); -#endif + + accel_modify_page(ram_addr, dirty_flags); + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; /* we remove the notdirty callback only if the code has been @@ -2433,12 +2435,11 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } + stl_p(phys_ram_base + ram_addr, val); -#ifdef USE_KQEMU - if (cpu_single_env->kqemu_enabled && - (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) - kqemu_modify_page(cpu_single_env, ram_addr); -#endif + + accel_modify_page(ram_addr, dirty_flags); + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; /* we remove the notdirty callback only if the code has been diff --git a/hw/pc.c b/hw/pc.c index 34683e7..2f56c1f 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -33,6 +33,7 @@ #include "boards.h" #include "console.h" #include "fw_cfg.h" +#include "accel.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -75,17 +76,7 @@ static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data) /* TSC handling */ uint64_t cpu_get_tsc(CPUX86State *env) { - /* Note: when using kqemu, it is more logical to return the host TSC - because kqemu does not trap the RDTSC instruction for - performance reasons */ -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - return cpu_get_real_ticks(); - } else -#endif - { - return cpu_get_ticks(); - } + return accel_get_real_ticks(); } /* SMM support */ diff --git a/kqemu.c b/kqemu.c index 4783aa2..58a149b 100644 --- a/kqemu.c +++ b/kqemu.c @@ -30,6 +30,7 @@ #ifdef HOST_SOLARIS #include #endif +#include "exec.h" #include #include #include @@ -44,12 +45,21 @@ #ifdef USE_KQEMU +#define KQEMU_USER 1 +#define KQEMU_KERNEL 2 + +static int kqemu_state; #define DEBUG //#define PROFILE #include #include #include "kqemu.h" +#include "accel86.h" + +#ifdef CONFIG_PROFILER +#include "qemu-timer.h" /* for ticks_per_sec */ +#endif #ifdef _WIN32 #define KQEMU_DEVICE "\\\\.\\kqemu" @@ -69,11 +79,6 @@ int kqemu_fd = KQEMU_INVALID_FD; #define kqemu_closefd(x) close(x) #endif -/* 0 = not allowed - 1 = user kqemu - 2 = kernel kqemu -*/ -int kqemu_allowed = 1; uint64_t *pages_to_flush; unsigned int nb_pages_to_flush; uint64_t *ram_pages_to_update; @@ -84,6 +89,13 @@ uint8_t *modified_ram_pages_table; int qpi_io_memory; uint32_t kqemu_comm_base; /* physical address of the QPI communication page */ +static inline int cpu_get_time_fast(void) +{ + int low, high; + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + return low; +} + #define cpuid(index, eax, ebx, ecx, edx) \ asm volatile ("cpuid" \ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \ @@ -113,6 +125,19 @@ static int is_cpuid_supported(void) } #endif +/* FIXME: Should not be needed, since ideally, QEMUAccel would avoid all kqemu tests + * altogether + */ +int kqemu_is_enabled(CPUState *env) +{ + return kqemu_state == KQEMU_USER; +} + +int kqemu_kernel_enabled(CPUState *env) +{ + return kqemu_state == KQEMU_KERNEL; +} + static void kqemu_update_cpuid(CPUState *env) { int critical_features_mask, features, ext_features, ext_features_mask; @@ -150,7 +175,9 @@ static void kqemu_update_cpuid(CPUState *env) accelerated code */ } -int kqemu_init(CPUState *env) +QEMUAccel kqemu_accel; + +static int kqemu_do_start(int cpus) { struct kqemu_init kinit; int ret, version; @@ -158,7 +185,7 @@ int kqemu_init(CPUState *env) DWORD temp; #endif - if (!kqemu_allowed) + if (cpus > 1) return -1; #ifdef _WIN32 @@ -230,8 +257,6 @@ int kqemu_init(CPUState *env) kqemu_fd = KQEMU_INVALID_FD; return -1; } - kqemu_update_cpuid(env); - env->kqemu_enabled = kqemu_allowed; nb_pages_to_flush = 0; nb_ram_pages_to_update = 0; @@ -239,7 +264,24 @@ int kqemu_init(CPUState *env) return 0; } -void kqemu_flush_page(CPUState *env, target_ulong addr) +static int kqemu_start(int cpus) +{ + kqemu_state = KQEMU_USER; + return kqemu_do_start(cpus); +} + +static int kqemu_start_kernel(int cpus) +{ + kqemu_state = KQEMU_KERNEL; + return kqemu_do_start(cpus); +} + +static void kqemu_init_env(CPUState *env) +{ + kqemu_update_cpuid(env); +} + +static void kqemu_flush_page(CPUState *env, target_ulong addr) { #if defined(DEBUG) if (loglevel & CPU_LOG_INT) { @@ -252,7 +294,7 @@ void kqemu_flush_page(CPUState *env, target_ulong addr) pages_to_flush[nb_pages_to_flush++] = addr; } -void kqemu_flush(CPUState *env, int global) +static void kqemu_flush(CPUState *env, int global) { #ifdef DEBUG if (loglevel & CPU_LOG_INT) { @@ -262,7 +304,7 @@ void kqemu_flush(CPUState *env, int global) nb_pages_to_flush = KQEMU_FLUSH_ALL; } -void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr) +static void kqemu_set_notdirty(ram_addr_t ram_addr) { #ifdef DEBUG if (loglevel & CPU_LOG_INT) { @@ -291,7 +333,7 @@ static void kqemu_reset_modified_ram_pages(void) nb_modified_ram_pages = 0; } -void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr) +static void kqemu_modify_page(ram_addr_t ram_addr, int dirty_flags) { unsigned long page_index; int ret; @@ -299,6 +341,8 @@ void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr) DWORD temp; #endif + if ((dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) + return; page_index = ram_addr >> TARGET_PAGE_BITS; if (!modified_ram_pages_table[page_index]) { #if 0 @@ -689,7 +733,7 @@ static inline void kqemu_save_seg(SegmentCache *sc, sc->base = ksc->base; } -int kqemu_cpu_exec(CPUState *env) +static int kqemu_do_cpu_exec(CPUState *env) { struct kqemu_cpu_state kcpu_state, *kenv = &kcpu_state; int ret, cpl, i; @@ -749,7 +793,7 @@ int kqemu_cpu_exec(CPUState *env) cpl = (env->hflags & HF_CPL_MASK); kenv->cpl = cpl; kenv->nb_pages_to_flush = nb_pages_to_flush; - kenv->user_only = (env->kqemu_enabled == 1); + kenv->user_only = kqemu_is_enabled(env); kenv->nb_ram_pages_to_update = nb_ram_pages_to_update; nb_ram_pages_to_update = 0; kenv->nb_modified_ram_pages = nb_modified_ram_pages; @@ -891,7 +935,7 @@ int kqemu_cpu_exec(CPUState *env) cpu_dump_state(env, logfile, fprintf, 0); } #endif - return 1; + return EXEC_EXIT_INTR; } else if ((ret & 0xff00) == KQEMU_RET_EXCEPTION) { env->exception_index = ret & 0xff; env->error_code = kenv->error_code; @@ -907,7 +951,7 @@ int kqemu_cpu_exec(CPUState *env) cpu_dump_state(env, logfile, fprintf, 0); } #endif - return 1; + return EXEC_EXIT_INTR; } else if (ret == KQEMU_RET_INTR) { #ifdef CONFIG_PROFILER kqemu_ret_intr_count++; @@ -917,7 +961,7 @@ int kqemu_cpu_exec(CPUState *env) cpu_dump_state(env, logfile, fprintf, 0); } #endif - return 0; + return EXEC_EXIT_DONE; } else if (ret == KQEMU_RET_SOFTMMU) { #ifdef CONFIG_PROFILER { @@ -930,16 +974,31 @@ int kqemu_cpu_exec(CPUState *env) cpu_dump_state(env, logfile, fprintf, 0); } #endif - return 2; + return EXEC_EXIT_SOFTMMU; } else { cpu_dump_state(env, stderr, fprintf, 0); fprintf(stderr, "Unsupported return value: 0x%x\n", ret); exit(1); } - return 0; + return EXEC_EXIT_DONE; } -void kqemu_cpu_interrupt(CPUState *env) +static int kqemu_cpu_exec(CPUState *env) +{ + + int ret = EXEC_EXIT_SOFTMMU; + if (kqemu_kernel_flags_ok(env) && env->interrupt_request == 0) { + ret = kqemu_do_cpu_exec(env); + /* put eflags in CPU temporary format */ + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((env->eflags >> 10) & 1)); + CC_OP = CC_OP_EFLAGS; + } + return ret; +} + + +static void kqemu_cpu_interrupt(CPUState *env) { #if defined(_WIN32) /* cancelling the I/O request causes KQEMU to finish executing the @@ -1019,7 +1078,188 @@ static void qpi_init(void) qpi_io_memory = cpu_register_io_memory(0, qpi_mem_read, qpi_mem_write, NULL); - cpu_register_physical_memory(kqemu_comm_base & ~0xfff, + __cpu_register_physical_memory(kqemu_comm_base & ~0xfff, 0x1000, qpi_io_memory); } + +static int kqemu_info(CPUState *env, char *buf) +{ + return snprintf(buf, MAX_INFO_BUF, "kqemu support: enabled for user code\n"); +} + +static int kqemu_kernel_info(CPUState *env, char *buf) +{ + return snprintf(buf, MAX_INFO_BUF, "kqemu support: enabled for user and kernel code\n"); +} + +int64_t kqemu_time; +int64_t kqemu_exec_count; +int64_t kqemu_ret_int_count; +int64_t kqemu_ret_excp_count; +int64_t kqemu_ret_intr_count; +extern int64_t qemu_time; + +static int kqemu_profile(CPUState *env, char *buf) +{ + int len = 0; +#ifdef CONFIG_PROFILER + len = sprintf(buf, "kqemu time %" PRId64 " (%0.3f %0.1f%%) count=%" PRId64 + " int=%" PRId64 " excp=%" PRId64 " intr=%" PRId64 "\n", + kqemu_time, kqemu_time / (double)ticks_per_sec, + kqemu_time / qemu_time * 100.0, + kqemu_exec_count, + kqemu_ret_int_count, + kqemu_ret_excp_count, + kqemu_ret_intr_count); + + kqemu_time = 0; + kqemu_exec_count = 0; + kqemu_ret_int_count = 0; + kqemu_ret_excp_count = 0; + kqemu_ret_intr_count = 0; + kqemu_record_dump(); +#endif + return len; +} + +static void kqemu_trace_io(CPUState *env) +{ + if (env) + kqemu_cpu_field(env,last_io_time) = cpu_get_time_fast(); +} + +#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000) + +static inline int kqemu_flags_ok(CPUState *env) +{ + return((env->cr[0] & CR0_PE_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK) && + (env->eflags & IF_MASK) && + !(env->eflags & VM_MASK)); +} + +static inline int kqemu_kernel_flags_ok(CPUState *env) +{ + return (kqemu_flags_ok(env) && (kqemu_kernel_enabled(env) || + ((env->hflags & HF_CPL_MASK) == 3 && + (env->eflags & IOPL_MASK) != IOPL_MASK))); + +} + +static int kqemu_break_loop(CPUState *env) +{ + if (kqemu_kernel_flags_ok(env) && + (cpu_get_time_fast() - kqemu_cpu_field(env,last_io_time)) >= MIN_CYCLE_BEFORE_SWITCH) { + return 1; + } + return 0; +} + +static CPUState *kqemu_get_env(void) +{ + KQEMUCPUState *kenv; + kenv = qemu_mallocz(sizeof(KQEMUCPUState)); + return &kenv->env; +} + +static int kqemu_get_msr(int msr, uint64_t *val) +{ + int ret = -1; + switch (msr) { + case MSR_QPI_COMMBASE: + val = kqemu_comm_base; + ret = 0; + break; + } + return ret; +} + +static int kqemu_set_msr(int msr, target_ulong val) +{ + return -1; +} + +static void kqemu_interrupt_return(CPUState *env) +{ + if (kqemu_kernel_flags_ok(env)) { + CC_OP = CC_OP_EFLAGS; + env->exception_index = -1; + cpu_loop_exit(); + } +} + +static void kqemu_syscall_return(CPUState *env) +{ + if (kqemu_kernel_flags_ok(env)) { + if (env->hflags & HF_LMA_MASK) + CC_OP = CC_OP_EFLAGS; + env->exception_index = -1; + cpu_loop_exit(); + } +} + +static void kqemu_long_exit_loop(CPUState *env) +{ + if (kqemu_kernel_flags_ok(env)) { + env->exception_index = -1; + cpu_loop_exit(); + } +} + +QEMUAccel86 kqemu_accel86 = { + .get_msr = kqemu_get_msr, + .set_msr = kqemu_set_msr, + .interrupt_return = kqemu_interrupt_return, + .syscall_return = kqemu_syscall_return, + .long_call = kqemu_long_exit_loop, + .long_ret = kqemu_long_exit_loop, +}; + +QEMUAccel kqemu_accel = { + .name = "KQEMU", + .cpu_interrupt = kqemu_cpu_interrupt, + .init_env = kqemu_init_env, + .get_env = kqemu_get_env, + .start = kqemu_start, + .flush_cache = kqemu_flush, + .flush_page = kqemu_flush_page, + .info = kqemu_info, + .profile = kqemu_profile, + .set_notdirty = kqemu_set_notdirty, + .modify_page = kqemu_modify_page, +#ifndef CONFIG_USER_ONLY + /* Note: when using kqemu, it is more logical to return the host TSC + because kqemu does not trap the RDTSC instruction for + performance reasons */ + .get_real_ticks = cpu_get_real_ticks, +#endif + .register_physical_memory = kqemu_set_phys_mem, + .trace_io = kqemu_trace_io, + .break_loop = kqemu_break_loop, + .cpu_exec = kqemu_cpu_exec, + .arch = &kqemu_accel86, +}; + +QEMUAccel kqemu_kernel_accel = { + .name = "kernel-KQEMU", + .cpu_interrupt = kqemu_cpu_interrupt, + .init_env = kqemu_init_env, + .get_env = kqemu_get_env, + .start = kqemu_start_kernel, + .flush_cache = kqemu_flush, + .flush_page = kqemu_flush_page, + .info = kqemu_kernel_info, + .profile = kqemu_profile, + .set_notdirty = kqemu_set_notdirty, + .modify_page = kqemu_modify_page, +#ifndef CONFIG_USER_ONLY + .get_real_ticks = cpu_get_real_ticks, +#endif + .register_physical_memory = kqemu_set_phys_mem, + .trace_io = kqemu_trace_io, + .break_loop = kqemu_break_loop, + .cpu_exec = kqemu_cpu_exec, + .arch = &kqemu_accel86, +}; + #endif diff --git a/kqemu.h b/kqemu.h index ed25c75..4152fbd 100644 --- a/kqemu.h +++ b/kqemu.h @@ -32,6 +32,12 @@ #define KQEMU_VERSION 0x010400 +extern int64_t kqemu_time, kqemu_time_start; +extern int64_t kqemu_exec_count; +extern int64_t kqemu_ret_int_count; +extern int64_t kqemu_ret_excp_count; +extern int64_t kqemu_ret_intr_count; + struct kqemu_segment_cache { uint16_t selector; uint16_t padding1; @@ -151,4 +157,19 @@ struct kqemu_phys_mem { #define KQEMU_SET_PHYS_MEM _IOW('q', 5, struct kqemu_phys_mem) #endif +int kqemu_is_enabled(CPUState *env); +int kqemu_kernel_enabled(CPUState *env); + +typedef struct KQEMUCPUstate { + int last_io_time; + CPUState env; +} KQEMUCPUState; + +#define kqemu_cpu_field(env, field) (*({ \ + KQEMUCPUState *__c = container_of(env, KQEMUCPUState, env); \ + &__c->field; })) + +#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG)) + +#define MSR_QPI_COMMBASE 0xfabe0010 #endif /* KQEMU_H */ diff --git a/monitor.c b/monitor.c index f0a0bc3..fe915d8 100644 --- a/monitor.c +++ b/monitor.c @@ -34,6 +34,7 @@ #include "block.h" #include "audio/audio.h" #include "disas.h" +#include "accel.h" #include #include "qemu-timer.h" #include "migration.h" @@ -1233,49 +1234,42 @@ static void mem_info(void) } #endif -static void do_info_kqemu(void) +static int do_accel_do_list(void) { -#ifdef USE_KQEMU + QEMUCont *tmp; + for (tmp= get_accel_head(); tmp != NULL; tmp = tmp->next) + { + term_printf("%c %s\n", tmp->active ? '*' : ' ', tmp->acc->name); + } +} + +static void do_info_accelerator(void) +{ + char buf[MAX_INFO_BUF]; CPUState *env; - int val; - val = 0; + env = mon_get_cpu(); + if (!env) { term_printf("No cpu initialized yet"); return; } - val = env->kqemu_enabled; - term_printf("kqemu support: "); - switch(val) { - default: - case 0: - term_printf("disabled\n"); - break; - case 1: - term_printf("enabled for user code\n"); - break; - case 2: - term_printf("enabled for user and kernel code\n"); - break; - } -#else - term_printf("kqemu support: not compiled\n"); -#endif + + do_accel_do_list(); + if (accel_info(env, buf)) + term_printf(buf); } #ifdef CONFIG_PROFILER -int64_t kqemu_time; int64_t qemu_time; -int64_t kqemu_exec_count; int64_t dev_time; -int64_t kqemu_ret_int_count; -int64_t kqemu_ret_excp_count; -int64_t kqemu_ret_intr_count; - static void do_info_profile(void) { int64_t total; + char buf[MAX_BUF]; + CPUState *env = mon_get_cpu(); + total = qemu_time; if (total == 0) total = 1; @@ -1283,24 +1277,12 @@ static void do_info_profile(void) dev_time, dev_time / (double)ticks_per_sec); term_printf("qemu time %" PRId64 " (%0.3f)\n", qemu_time, qemu_time / (double)ticks_per_sec); - term_printf("kqemu time %" PRId64 " (%0.3f %0.1f%%) count=%" PRId64 " int=%" PRId64 " excp=%" PRId64 " intr=%" PRId64 "\n", - kqemu_time, kqemu_time / (double)ticks_per_sec, - kqemu_time / (double)total * 100.0, - kqemu_exec_count, - kqemu_ret_int_count, - kqemu_ret_excp_count, - kqemu_ret_intr_count); + if (accel_profile(env, buf)) + term_printf(buf); qemu_time = 0; - kqemu_time = 0; - kqemu_exec_count = 0; dev_time = 0; - kqemu_ret_int_count = 0; - kqemu_ret_excp_count = 0; - kqemu_ret_intr_count = 0; -#ifdef USE_KQEMU - kqemu_record_dump(); -#endif } + #else static void do_info_profile(void) { @@ -1493,8 +1475,8 @@ static const term_cmd_t info_cmds[] = { #endif { "jit", "", do_info_jit, "", "show dynamic compiler info", }, - { "kqemu", "", do_info_kqemu, - "", "show kqemu information", }, + { "accelerator", "", do_info_accelerator, + "", "show accelerator information", }, { "usb", "", usb_info, "", "show guest USB devices", }, { "usbhost", "", usb_host_info, diff --git a/osdep.c b/osdep.c index 683aad0..31c96e6 100644 --- a/osdep.c +++ b/osdep.c @@ -68,112 +68,9 @@ void qemu_vfree(void *ptr) #else -#if defined(USE_KQEMU) - -#ifdef __OpenBSD__ -#include -#include -#include -#else -#include -#endif - #include #include -static void *kqemu_vmalloc(size_t size) -{ - static int phys_ram_fd = -1; - static int phys_ram_size = 0; - void *ptr; - -#ifdef __OpenBSD__ /* no need (?) for a dummy file on OpenBSD */ - int map_anon = MAP_ANON; -#else - int map_anon = 0; - const char *tmpdir; - char phys_ram_file[1024]; -#ifdef HOST_SOLARIS - struct statvfs stfs; -#else - struct statfs stfs; -#endif - - if (phys_ram_fd < 0) { - tmpdir = getenv("QEMU_TMPDIR"); - if (!tmpdir) -#ifdef HOST_SOLARIS - tmpdir = "/tmp"; - if (statvfs(tmpdir, &stfs) == 0) { -#else - tmpdir = "/dev/shm"; - if (statfs(tmpdir, &stfs) == 0) { -#endif - int64_t free_space; - int ram_mb; - - free_space = (int64_t)stfs.f_bavail * stfs.f_bsize; - if ((ram_size + 8192 * 1024) >= free_space) { - ram_mb = (ram_size / (1024 * 1024)); - fprintf(stderr, - "You do not have enough space in '%s' for the %d MB of QEMU virtual RAM.\n", - tmpdir, ram_mb); - if (strcmp(tmpdir, "/dev/shm") == 0) { - fprintf(stderr, "To have more space available provided you have enough RAM and swap, do as root:\n" - "mount -o remount,size=%dm /dev/shm\n", - ram_mb + 16); - } else { - fprintf(stderr, - "Use the '-m' option of QEMU to diminish the amount of virtual RAM or use the\n" - "QEMU_TMPDIR environment variable to set another directory where the QEMU\n" - "temporary RAM file will be opened.\n"); - } - fprintf(stderr, "Or disable the accelerator module with -no-kqemu\n"); - exit(1); - } - } - snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX", - tmpdir); - phys_ram_fd = mkstemp(phys_ram_file); - if (phys_ram_fd < 0) { - fprintf(stderr, - "warning: could not create temporary file in '%s'.\n" - "Use QEMU_TMPDIR to select a directory in a tmpfs filesystem.\n" - "Using '/tmp' as fallback.\n", - tmpdir); - snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX", - "/tmp"); - phys_ram_fd = mkstemp(phys_ram_file); - if (phys_ram_fd < 0) { - fprintf(stderr, "Could not create temporary memory file '%s'\n", - phys_ram_file); - exit(1); - } - } - unlink(phys_ram_file); - } - size = (size + 4095) & ~4095; - ftruncate(phys_ram_fd, phys_ram_size + size); -#endif /* !__OpenBSD__ */ - ptr = mmap(NULL, - size, - PROT_WRITE | PROT_READ, map_anon | MAP_SHARED, - phys_ram_fd, phys_ram_size); - if (ptr == MAP_FAILED) { - fprintf(stderr, "Could not map physical memory\n"); - exit(1); - } - phys_ram_size += size; - return ptr; -} - -static void kqemu_vfree(void *ptr) -{ - /* may be useful some day, but currently we do not need to free */ -} - -#endif - void *qemu_memalign(size_t alignment, size_t size) { #if defined(_POSIX_C_SOURCE) @@ -193,10 +90,6 @@ void *qemu_memalign(size_t alignment, size_t size) /* alloc shared memory pages */ void *qemu_vmalloc(size_t size) { -#if defined(USE_KQEMU) - if (kqemu_allowed) - return kqemu_vmalloc(size); -#endif #ifdef _BSD return valloc(size); #else @@ -206,10 +99,6 @@ void *qemu_vmalloc(size_t size) void qemu_vfree(void *ptr) { -#if defined(USE_KQEMU) - if (kqemu_allowed) - kqemu_vfree(ptr); -#endif free(ptr); } diff --git a/softmmu_template.h b/softmmu_template.h index 98dd378..4945352 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -47,6 +47,8 @@ #define ADDR_READ addr_read #endif +#include "accel.h" + static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, int mmu_idx, void *retaddr); @@ -75,9 +77,7 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32; #endif #endif /* SHIFT > 2 */ -#ifdef USE_KQEMU - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); return res; } @@ -220,9 +220,7 @@ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32); #endif #endif /* SHIFT > 2 */ -#ifdef USE_KQEMU - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); } void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, diff --git a/sysemu.h b/sysemu.h index 976fecc..5a19626 100644 --- a/sysemu.h +++ b/sysemu.h @@ -99,11 +99,6 @@ extern int semihosting_enabled; extern int old_param; extern const char *bootp_filename; - -#ifdef USE_KQEMU -extern int kqemu_allowed; -#endif - #define MAX_OPTION_ROMS 16 extern const char *option_rom[MAX_OPTION_ROMS]; extern int nb_option_roms; diff --git a/target-i386/accel86.h b/target-i386/accel86.h new file mode 100644 index 0000000..a7ba39b --- /dev/null +++ b/target-i386/accel86.h @@ -0,0 +1,60 @@ +#ifndef _ACCEL_86_H_ +#define _ACCEL_86_H_ + +#include "accel.h" + +typedef struct QEMUAccel86 { + int (*get_msr)(int msr, uint64_t *value); + int (*set_msr)(int msr, uint64_t value); + void (*interrupt_return)(CPUState *env); + void (*syscall_return)(CPUState *env); + void (*long_call)(CPUState *env); + void (*long_ret)(CPUState *env); +} QEMUAccel86; + +#define accel86_call_func ((QEMUAccel86 *)(current_accel->arch)) + +static inline int accel_get_msr(int msr, uint64_t *value) +{ + if (!current_accel->arch) + return -1; + return accel86_call_func->get_msr(msr, value); +} + +static inline int accel_set_msr(int msr, uint64_t value) +{ + if (!current_accel->arch) + return -1; + return accel86_call_func->set_msr(msr, value); +} + +static inline void accel_interrupt_return(CPUState *env) +{ + if (!current_accel->arch) + return; + accel86_call_func->interrupt_return(env); +} + +static inline void accel_syscall_return(CPUState *env) +{ + if (!current_accel->arch) + return; + accel86_call_func->syscall_return(env); +} + +static inline void accel_long_call(CPUState *env) +{ + if (!current_accel->arch) + return; + accel86_call_func->syscall_return(env); +} + +static inline void accel_long_ret(CPUState *env) +{ + if (!current_accel->arch) + return; + accel86_call_func->syscall_return(env); +} + +#endif + diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 3c11e0f..e5e91cc 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -606,10 +606,6 @@ typedef struct CPUX86State { uint32_t cpuid_ext3_features; uint32_t cpuid_apic_id; -#ifdef USE_KQEMU - int kqemu_enabled; - int last_io_time; -#endif /* in order to simplify APIC support, we leave this pointer to the user */ struct APICState *apic_state; @@ -727,15 +723,6 @@ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); #define X86_DUMP_FPU 0x0001 /* dump FPU state too */ #define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */ -#ifdef USE_KQEMU -static inline int cpu_get_time_fast(void) -{ - int low, high; - asm volatile("rdtsc" : "=a" (low), "=d" (high)); - return low; -} -#endif - #define TARGET_PAGE_BITS 12 #define CPUState CPUX86State diff --git a/target-i386/helper.c b/target-i386/helper.c index c2e1a88..5ff051f 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -30,6 +30,8 @@ #include "svm.h" #include "qemu-common.h" +#include "accel.h" + //#define DEBUG_MMU static int cpu_x86_register (CPUX86State *env, const char *cpu_model); @@ -96,7 +98,7 @@ CPUX86State *cpu_x86_init(const char *cpu_model) CPUX86State *env; static int inited; - env = qemu_mallocz(sizeof(CPUX86State)); + env = accel_get_env(); if (!env) return NULL; cpu_exec_init(env); @@ -112,9 +114,7 @@ CPUX86State *cpu_x86_init(const char *cpu_model) return NULL; } cpu_reset(env); -#ifdef USE_KQEMU - kqemu_init(env); -#endif + accel_init_env(env); return env; } diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c index ebb5824..addd42a 100644 --- a/target-i386/op_helper.c +++ b/target-i386/op_helper.c @@ -20,6 +20,7 @@ #define CPU_NO_GLOBAL_REGS #include "exec.h" #include "host-utils.h" +#include "accel86.h" //#define DEBUG_PCALL @@ -1103,14 +1104,7 @@ void helper_sysret(int dflag) env->eflags |= IF_MASK; cpu_x86_set_cpl(env, 3); } -#ifdef USE_KQEMU - if (kqemu_is_ok(env)) { - if (env->hflags & HF_LMA_MASK) - CC_OP = CC_OP_EFLAGS; - env->exception_index = -1; - cpu_loop_exit(); - } -#endif + accel_syscall_return(env); } /* real mode interrupt */ @@ -2623,12 +2617,7 @@ void helper_lcall_protected(int new_cs, target_ulong new_eip, SET_ESP(sp, sp_mask); EIP = offset; } -#ifdef USE_KQEMU - if (kqemu_is_ok(env)) { - env->exception_index = -1; - cpu_loop_exit(); - } -#endif + accel_long_call(env); } /* real and vm86 mode iret */ @@ -2917,24 +2906,14 @@ void helper_iret_protected(int shift, int next_eip) helper_ret_protected(shift, 1, 0); } env->hflags2 &= ~HF2_NMI_MASK; -#ifdef USE_KQEMU - if (kqemu_is_ok(env)) { - CC_OP = CC_OP_EFLAGS; - env->exception_index = -1; - cpu_loop_exit(); - } -#endif + + accel_interrupt_return(env); } void helper_lret_protected(int shift, int addend) { helper_ret_protected(shift, 0, addend); -#ifdef USE_KQEMU - if (kqemu_is_ok(env)) { - env->exception_index = -1; - cpu_loop_exit(); - } -#endif + accel_long_ret(env); } void helper_sysenter(void) @@ -3007,12 +2986,6 @@ void helper_sysexit(int dflag) } ESP = ECX; EIP = EDX; -#ifdef USE_KQEMU - if (kqemu_is_ok(env)) { - env->exception_index = -1; - cpu_loop_exit(); - } -#endif } #if defined(CONFIG_USER_ONLY) @@ -3262,18 +3235,9 @@ void helper_rdmsr(void) val = env->kernelgsbase; break; #endif -#ifdef USE_KQEMU - case MSR_QPI_COMMBASE: - if (env->kqemu_enabled) { - val = kqemu_comm_base; - } else { - val = 0; - } - break; -#endif default: - /* XXX: exception ? */ - val = 0; + if (accel_get_msr((uint32_t)ECX, &val) < 0) + val = 0; break; } EAX = (uint32_t)(val); diff --git a/vl.c b/vl.c index c0e43ac..42720e8 100644 --- a/vl.c +++ b/vl.c @@ -140,6 +140,7 @@ #include "disas.h" #include "exec-all.h" +#include "accel.h" #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" @@ -149,6 +150,8 @@ #define SMBD_COMMAND "/usr/sbin/smbd" #endif +#include "accel.h" + //#define DEBUG_UNUSED_IOPORT //#define DEBUG_IOPORT //#define DEBUG_NET @@ -252,6 +255,14 @@ static QEMUTimer *icount_vm_timer; uint8_t qemu_uuid[16]; +QEMUAccel *available_accels[] = { +/* list of available accelerators */ +#ifdef USE_KQEMU + &kqemu_accel, + &kqemu_kernel_accel, +#endif +}; + #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) /***********************************************************/ @@ -410,10 +421,7 @@ void cpu_outb(CPUState *env, int addr, int val) fprintf(logfile, "outb: %04x %02x\n", addr, val); #endif ioport_write(0, addr, val); -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); } void cpu_outw(CPUState *env, int addr, int val) @@ -423,10 +431,7 @@ void cpu_outw(CPUState *env, int addr, int val) fprintf(logfile, "outw: %04x %04x\n", addr, val); #endif ioport_write(1, addr, val); -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); } void cpu_outl(CPUState *env, int addr, int val) @@ -436,10 +441,7 @@ void cpu_outl(CPUState *env, int addr, int val) fprintf(logfile, "outl: %04x %08x\n", addr, val); #endif ioport_write(2, addr, val); -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); } int cpu_inb(CPUState *env, int addr) @@ -450,10 +452,7 @@ int cpu_inb(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inb : %04x %02x\n", addr, val); #endif -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); return val; } @@ -465,10 +464,7 @@ int cpu_inw(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inw : %04x %04x\n", addr, val); #endif -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); return val; } @@ -480,10 +476,7 @@ int cpu_inl(CPUState *env, int addr) if (loglevel & CPU_LOG_IOPORT) fprintf(logfile, "inl : %04x %08x\n", addr, val); #endif -#ifdef USE_KQEMU - if (env) - env->last_io_time = cpu_get_time_fast(); -#endif + accel_trace_io(env); return val; } @@ -1317,11 +1310,6 @@ static void host_alarm_handler(int host_signum) if (env) { /* stop the currently executing cpu because a timer occured */ cpu_interrupt(env, CPU_INTERRUPT_EXIT); -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - kqemu_cpu_interrupt(env); - } -#endif } event_pending = 1; } @@ -7560,14 +7548,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) void qemu_service_io(void) { CPUState *env = cpu_single_env; - if (env) { + if (env) cpu_interrupt(env, CPU_INTERRUPT_EXIT); -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - kqemu_cpu_interrupt(env); - } -#endif - } } /***********************************************************/ @@ -8255,10 +8237,6 @@ static void help(int exitcode) "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" " translation (t=none or lba) (usually qemu can guess them)\n" "-L path set the directory for the BIOS, VGA BIOS and keymaps\n" -#ifdef USE_KQEMU - "-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n" - "-no-kqemu disable KQEMU kernel module usage\n" -#endif #ifdef TARGET_I386 "-no-acpi disable ACPI\n" #endif @@ -8362,8 +8340,7 @@ enum { QEMU_OPTION_alt_grab, QEMU_OPTION_no_quit, QEMU_OPTION_pidfile, - QEMU_OPTION_no_kqemu, - QEMU_OPTION_kernel_kqemu, + QEMU_OPTION_accel, QEMU_OPTION_win2k_hack, QEMU_OPTION_usb, QEMU_OPTION_usbdevice, @@ -8446,10 +8423,7 @@ static const QEMUOption qemu_options[] = { { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, { "L", HAS_ARG, QEMU_OPTION_L }, { "bios", HAS_ARG, QEMU_OPTION_bios }, -#ifdef USE_KQEMU - { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, - { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu }, -#endif + { "accel", HAS_ARG, QEMU_OPTION_accel}, #if defined(TARGET_PPC) || defined(TARGET_SPARC) { "g", 1, QEMU_OPTION_g }, #endif @@ -9264,14 +9238,15 @@ int main(int argc, char **argv) win2k_install_hack = 1; break; #endif -#ifdef USE_KQEMU - case QEMU_OPTION_no_kqemu: - kqemu_allowed = 0; - break; - case QEMU_OPTION_kernel_kqemu: - kqemu_allowed = 2; + case QEMU_OPTION_accel: + { + int i; + for (i = 0; i < ARRAY_SIZE(available_accels); i++) { + if (!strcasecmp(optarg, available_accels[i]->name)) + register_qemu_accel(available_accels[i]); + } + } break; -#endif case QEMU_OPTION_usb: usb_enabled = 1; break; @@ -9413,6 +9388,9 @@ int main(int argc, char **argv) exit(1); } + /* Basic handler for the noaccel case */ + register_qemu_accel(&noaccel); + if (nographic) { if (serial_device_index == 0) serial_devices[0] = "stdio"; @@ -9476,10 +9454,6 @@ int main(int argc, char **argv) exit(1); } -#ifdef USE_KQEMU - if (smp_cpus > 1) - kqemu_allowed = 0; -#endif linux_boot = (kernel_filename != NULL); net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF; @@ -9600,6 +9574,11 @@ int main(int argc, char **argv) /* init the dynamic translator */ cpu_exec_init_all(tb_size * 1024 * 1024); + if (accel_start(smp_cpus)) { + fprintf(stderr, "qemu: error, no suitable accelerator found\n"); + exit(1); + } + bdrv_init(); /* we always create the cdrom drive, even if no disk is there */