From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1L2Xo1-0006sI-Nf for qemu-devel@nongnu.org; Tue, 18 Nov 2008 16:08:17 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1L2Xo1-0006s6-5I for qemu-devel@nongnu.org; Tue, 18 Nov 2008 16:08:17 -0500 Received: from [199.232.76.173] (port=53583 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1L2Xo1-0006s3-3B for qemu-devel@nongnu.org; Tue, 18 Nov 2008 16:08:17 -0500 Received: from savannah.gnu.org ([199.232.41.3]:55021 helo=sv.gnu.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1L2Xo0-0003QA-SM for qemu-devel@nongnu.org; Tue, 18 Nov 2008 16:08:17 -0500 Received: from cvs.savannah.gnu.org ([199.232.41.69]) by sv.gnu.org with esmtp (Exim 4.63) (envelope-from ) id 1L2Xnz-0002T1-Tk for qemu-devel@nongnu.org; Tue, 18 Nov 2008 21:08:15 +0000 Received: from aliguori by cvs.savannah.gnu.org with local (Exim 4.63) (envelope-from ) id 1L2Xnz-0002Sx-Hl for qemu-devel@nongnu.org; Tue, 18 Nov 2008 21:08:15 +0000 MIME-Version: 1.0 Errors-To: aliguori Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Anthony Liguori Message-Id: Date: Tue, 18 Nov 2008 21:08:15 +0000 Subject: [Qemu-devel] [5747] x86: Debug register emulation (Jan Kiszka) 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 Revision: 5747 http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5747 Author: aliguori Date: 2008-11-18 21:08:15 +0000 (Tue, 18 Nov 2008) Log Message: ----------- x86: Debug register emulation (Jan Kiszka) Built on top of previously enhanced breakpoint/watchpoint support, this patch adds full debug register emulation for the x86 architecture. Many corner cases were considered, and the result was successfully tested inside a Linux guest with gdb, but I won't be surprised if one or two scenarios still behave differently in reality. Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori Modified Paths: -------------- trunk/linux-user/main.c trunk/target-i386/cpu.h trunk/target-i386/helper.c trunk/target-i386/machine.c trunk/target-i386/op_helper.c Modified: trunk/linux-user/main.c =================================================================== --- trunk/linux-user/main.c 2008-11-18 20:56:59 UTC (rev 5746) +++ trunk/linux-user/main.c 2008-11-18 21:08:15 UTC (rev 5747) @@ -403,7 +403,7 @@ queue_signal(env, info.si_signo, &info); } break; - case EXCP01_SSTP: + case EXCP01_DB: case EXCP03_INT3: #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { @@ -413,7 +413,7 @@ { info.si_signo = SIGTRAP; info.si_errno = 0; - if (trapnr == EXCP01_SSTP) { + if (trapnr == EXCP01_DB) { info.si_code = TARGET_TRAP_BRKPT; info._sifields._sigfault._addr = env->eip; } else { Modified: trunk/target-i386/cpu.h =================================================================== --- trunk/target-i386/cpu.h 2008-11-18 20:56:59 UTC (rev 5746) +++ trunk/target-i386/cpu.h 2008-11-18 21:08:15 UTC (rev 5747) @@ -205,6 +205,16 @@ #define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT) #define CR4_OSXMMEXCPT_MASK (1 << 10) +#define DR6_BD (1 << 13) +#define DR6_BS (1 << 14) +#define DR6_BT (1 << 15) +#define DR6_FIXED_1 0xffff0ff0 + +#define DR7_GD (1 << 13) +#define DR7_TYPE_SHIFT 16 +#define DR7_LEN_SHIFT 18 +#define DR7_FIXED_1 0x00000400 + #define PG_PRESENT_BIT 0 #define PG_RW_BIT 1 #define PG_USER_BIT 2 @@ -362,7 +372,7 @@ #define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */ #define EXCP00_DIVZ 0 -#define EXCP01_SSTP 1 +#define EXCP01_DB 1 #define EXCP02_NMI 2 #define EXCP03_INT3 3 #define EXCP04_INTO 4 @@ -596,6 +606,10 @@ int exception_is_int; target_ulong exception_next_eip; target_ulong dr[8]; /* debug registers */ + union { + CPUBreakpoint *cpu_breakpoint[4]; + CPUWatchpoint *cpu_watchpoint[4]; + }; /* break/watchpoints for dr[0..3] */ uint32_t smbase; int old_exception; /* exception in flight */ @@ -789,6 +803,26 @@ } #endif +static inline int hw_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 3; +} + +static inline int hw_breakpoint_type(unsigned long dr7, int index) +{ + return (dr7 >> (DR7_TYPE_SHIFT + (index * 2))) & 3; +} + +static inline int hw_breakpoint_len(unsigned long dr7, int index) +{ + int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 2))) & 3); + return (len == 2) ? 8 : len + 1; +} + +void hw_breakpoint_insert(CPUState *env, int index); +void hw_breakpoint_remove(CPUState *env, int index); +int check_hw_breakpoints(CPUState *env, int force_dr6_update); + #include "cpu-all.h" #include "exec-all.h" Modified: trunk/target-i386/helper.c =================================================================== --- trunk/target-i386/helper.c 2008-11-18 20:56:59 UTC (rev 5746) +++ trunk/target-i386/helper.c 2008-11-18 21:08:15 UTC (rev 5747) @@ -34,8 +34,6 @@ //#define DEBUG_MMU -static int cpu_x86_register (CPUX86State *env, const char *cpu_model); - static void add_flagname_to_bitmaps(char *flagname, uint32_t *features, uint32_t *ext_features, uint32_t *ext2_features, @@ -93,35 +91,6 @@ fprintf(stderr, "CPU feature %s not found\n", flagname); } -CPUX86State *cpu_x86_init(const char *cpu_model) -{ - CPUX86State *env; - static int inited; - - env = qemu_mallocz(sizeof(CPUX86State)); - if (!env) - return NULL; - cpu_exec_init(env); - env->cpu_model_str = cpu_model; - - /* init various static tables */ - if (!inited) { - inited = 1; - optimize_flags_init(); - } - if (cpu_x86_register(env, cpu_model) < 0) { - cpu_x86_close(env); - return NULL; - } - cpu_reset(env); -#ifdef USE_KQEMU - kqemu_init(env); -#endif - if (kvm_enabled()) - kvm_init_vcpu(env); - return env; -} - typedef struct x86_def_t { const char *name; uint32_t level; @@ -499,6 +468,12 @@ env->fpuc = 0x37f; env->mxcsr = 0x1f80; + + memset(env->dr, 0, sizeof(env->dr)); + env->dr[6] = DR6_FIXED_1; + env->dr[7] = DR7_FIXED_1; + cpu_breakpoint_remove_all(env, BP_CPU); + cpu_watchpoint_remove_all(env, BP_CPU); } void cpu_x86_close(CPUX86State *env) @@ -1295,6 +1270,105 @@ paddr = (pte & TARGET_PAGE_MASK) + page_offset; return paddr; } + +void hw_breakpoint_insert(CPUState *env, int index) +{ + int type, err = 0; + + switch (hw_breakpoint_type(env->dr[7], index)) { + case 0: + if (hw_breakpoint_enabled(env->dr[7], index)) + err = cpu_breakpoint_insert(env, env->dr[index], BP_CPU, + &env->cpu_breakpoint[index]); + break; + case 1: + type = BP_CPU | BP_MEM_WRITE; + goto insert_wp; + case 2: + /* No support for I/O watchpoints yet */ + break; + case 3: + type = BP_CPU | BP_MEM_ACCESS; + insert_wp: + err = cpu_watchpoint_insert(env, env->dr[index], + hw_breakpoint_len(env->dr[7], index), + type, &env->cpu_watchpoint[index]); + break; + } + if (err) + env->cpu_breakpoint[index] = NULL; +} + +void hw_breakpoint_remove(CPUState *env, int index) +{ + if (!env->cpu_breakpoint[index]) + return; + switch (hw_breakpoint_type(env->dr[7], index)) { + case 0: + if (hw_breakpoint_enabled(env->dr[7], index)) + cpu_breakpoint_remove_by_ref(env, env->cpu_breakpoint[index]); + break; + case 1: + case 3: + cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[index]); + break; + case 2: + /* No support for I/O watchpoints yet */ + break; + } +} + +int check_hw_breakpoints(CPUState *env, int force_dr6_update) +{ + target_ulong dr6; + int reg, type; + int hit_enabled = 0; + + dr6 = env->dr[6] & ~0xf; + for (reg = 0; reg < 4; reg++) { + type = hw_breakpoint_type(env->dr[7], reg); + if ((type == 0 && env->dr[reg] == env->eip) || + ((type & 1) && env->cpu_watchpoint[reg] && + (env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT))) { + dr6 |= 1 << reg; + if (hw_breakpoint_enabled(env->dr[7], reg)) + hit_enabled = 1; + } + } + if (hit_enabled || force_dr6_update) + env->dr[6] = dr6; + return hit_enabled; +} + +static CPUDebugExcpHandler *prev_debug_excp_handler; + +void raise_exception(int exception_index); + +static void breakpoint_handler(CPUState *env) +{ + CPUBreakpoint *bp; + + if (env->watchpoint_hit) { + if (env->watchpoint_hit->flags & BP_CPU) { + env->watchpoint_hit = NULL; + if (check_hw_breakpoints(env, 0)) + raise_exception(EXCP01_DB); + else + cpu_resume_from_signal(env, NULL); + } + } else { + for (bp = env->breakpoints; bp != NULL; bp = bp->next) + if (bp->pc == env->eip) { + if (bp->flags & BP_CPU) { + check_hw_breakpoints(env, 1); + raise_exception(EXCP01_DB); + } + break; + } + } + if (prev_debug_excp_handler) + prev_debug_excp_handler(env); +} #endif /* !CONFIG_USER_ONLY */ static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, @@ -1532,3 +1606,36 @@ break; } } + +CPUX86State *cpu_x86_init(const char *cpu_model) +{ + CPUX86State *env; + static int inited; + + env = qemu_mallocz(sizeof(CPUX86State)); + if (!env) + return NULL; + cpu_exec_init(env); + env->cpu_model_str = cpu_model; + + /* init various static tables */ + if (!inited) { + inited = 1; + optimize_flags_init(); +#ifndef CONFIG_USER_ONLY + prev_debug_excp_handler = + cpu_set_debug_excp_handler(breakpoint_handler); +#endif + } + if (cpu_x86_register(env, cpu_model) < 0) { + cpu_x86_close(env); + return NULL; + } + cpu_reset(env); +#ifdef USE_KQEMU + kqemu_init(env); +#endif + if (kvm_enabled()) + kvm_init_vcpu(env); + return env; +} Modified: trunk/target-i386/machine.c =================================================================== --- trunk/target-i386/machine.c 2008-11-18 20:56:59 UTC (rev 5746) +++ trunk/target-i386/machine.c 2008-11-18 21:08:15 UTC (rev 5747) @@ -259,6 +259,10 @@ for(i = 0; i < 8; i++) qemu_get_betls(f, &env->dr[i]); + cpu_breakpoint_remove_all(env, BP_CPU); + cpu_watchpoint_remove_all(env, BP_CPU); + for (i = 0; i < 4; i++) + hw_breakpoint_insert(env, i); /* MMU */ qemu_get_sbe32s(f, &a20_mask); Modified: trunk/target-i386/op_helper.c =================================================================== --- trunk/target-i386/op_helper.c 2008-11-18 20:56:59 UTC (rev 5746) +++ trunk/target-i386/op_helper.c 2008-11-18 21:08:15 UTC (rev 5747) @@ -496,6 +496,17 @@ /* XXX: different exception if CALL ? */ raise_exception_err(EXCP0D_GPF, 0); } + +#ifndef CONFIG_USER_ONLY + /* reset local breakpoints */ + if (env->dr[7] & 0x55) { + for (i = 0; i < 4; i++) { + if (hw_breakpoint_enabled(env->dr[7], i) == 0x1) + hw_breakpoint_remove(env, i); + } + env->dr[7] &= ~0x55; + } +#endif } /* check if Port I/O is allowed in TSS */ @@ -1879,8 +1890,11 @@ void helper_single_step(void) { - env->dr[6] |= 0x4000; - raise_exception(EXCP01_SSTP); +#ifndef CONFIG_USER_ONLY + check_hw_breakpoints(env, 1); + env->dr[6] |= DR6_BS; +#endif + raise_exception(EXCP01_DB); } void helper_cpuid(void) @@ -2868,6 +2882,10 @@ void helper_write_crN(int reg, target_ulong t0) { } + +void helper_movl_drN_T0(int reg, target_ulong t0) +{ +} #else target_ulong helper_read_crN(int reg) { @@ -2913,6 +2931,24 @@ break; } } + +void helper_movl_drN_T0(int reg, target_ulong t0) +{ + int i; + + if (reg < 4) { + hw_breakpoint_remove(env, reg); + env->dr[reg] = t0; + hw_breakpoint_insert(env, reg); + } else if (reg == 7) { + for (i = 0; i < 4; i++) + hw_breakpoint_remove(env, i); + env->dr[7] = t0; + for (i = 0; i < 4; i++) + hw_breakpoint_insert(env, i); + } else + env->dr[reg] = t0; +} #endif void helper_lmsw(target_ulong t0) @@ -2929,12 +2965,6 @@ env->hflags &= ~HF_TS_MASK; } -/* XXX: do more */ -void helper_movl_drN_T0(int reg, target_ulong t0) -{ - env->dr[reg] = t0; -} - void helper_invlpg(target_ulong addr) { helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);