From dd75d4fcb4a82c34d4f466e7fc166162b71ff740 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 12 Oct 2015 18:25:40 +0200 Subject: [PATCH 01/13] target-i386: allow any alignment for SMBASE Processors up to the Pentium (says Bochs---I do not have old enough manuals) require a 32KiB alignment for the SMBASE, but newer processors do not need that, and Tiano Core will use non-aligned SMBASE values. Reported-by: Michael D Kinney Cc: Laszlo Ersek Cc: Jordan Justen Cc: Eduardo Habkost Signed-off-by: Paolo Bonzini Reviewed-by: Laszlo Ersek Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- target-i386/smm_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index 02e24b9236..c272a98407 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -266,7 +266,7 @@ void helper_rsm(CPUX86State *env) val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00); } #else cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); @@ -319,7 +319,7 @@ void helper_rsm(CPUX86State *env) val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8) & ~0x7fff; + env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8); } #endif if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { From e265e3e48049fbece9eaf536aa00ca41aa3c54d0 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Wed, 2 Sep 2015 11:19:11 -0300 Subject: [PATCH 02/13] target-i386: Disable cache info passthrough by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host cache information may not make sense for the guest if the VM CPU topology doesn't match the host CPU topology. To make sure we won't expose broken cache information to the guest, disable cache info passthrough by default, and add a new "host-cache-info" property that can be used to enable the old behavior for users that really need it. Cc: BenoƮt Canet Reviewed-by: Igor Mammedov Signed-off-by: Eduardo Habkost --- include/hw/i386/pc.h | 5 +++++ target-i386/cpu.c | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index c5961d7c03..7037de044d 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -318,6 +318,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "Broadwell-noTSX-" TYPE_X86_CPU,\ .property = "abm",\ .value = "off",\ + },\ + {\ + .driver = "host" "-" TYPE_X86_CPU,\ + .property = "host-cache-info",\ + .value = "on",\ }, #define PC_COMPAT_2_3 \ diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 5f53af248f..987253df3b 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -656,7 +656,6 @@ struct X86CPUDefinition { int stepping; FeatureWordArray features; char model_id[48]; - bool cache_info_passthrough; }; static X86CPUDefinition builtin_x86_defs[] = { @@ -1420,6 +1419,7 @@ static X86CPUDefinition host_cpudef; static Property host_x86_cpu_properties[] = { DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), + DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false), DEFINE_PROP_END_OF_LIST() }; @@ -1446,7 +1446,6 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data) cpu_x86_fill_model_id(host_cpudef.model_id); xcc->cpu_def = &host_cpudef; - host_cpudef.cache_info_passthrough = true; /* level, xlevel, xlevel2, and the feature words are initialized on * instance_init, because they require KVM to be initialized. @@ -2094,7 +2093,6 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) object_property_set_int(OBJECT(cpu), def->stepping, "stepping", errp); object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", errp); object_property_set_int(OBJECT(cpu), def->xlevel2, "xlevel2", errp); - cpu->cache_info_passthrough = def->cache_info_passthrough; object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); for (w = 0; w < FEATURE_WORDS; w++) { env->features[w] = def->features[w]; From 93d00d0fbe4711061834730fb70525d167b6f908 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Sep 2015 11:45:08 -0700 Subject: [PATCH 03/13] target-i386: Introduce cpu_x86_update_dr7 This moves the last of the iteration over breakpoints into the bpt_helper.c file. This also allows us to make several breakpoint functions static. Signed-off-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 29 ++++++++++++++++++----------- target-i386/cpu.h | 4 ++-- target-i386/machine.c | 8 ++++++-- target-i386/seg_helper.c | 8 +------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index c071c24782..f14788a1e9 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -21,7 +21,8 @@ #include "exec/helper-proto.h" -void hw_breakpoint_insert(CPUX86State *env, int index) +#ifndef CONFIG_USER_ONLY +static void hw_breakpoint_insert(CPUX86State *env, int index) { CPUState *cs = CPU(x86_env_get_cpu(env)); int type = 0, err = 0; @@ -55,7 +56,7 @@ void hw_breakpoint_insert(CPUX86State *env, int index) } } -void hw_breakpoint_remove(CPUX86State *env, int index) +static void hw_breakpoint_remove(CPUX86State *env, int index) { CPUState *cs; @@ -79,6 +80,20 @@ void hw_breakpoint_remove(CPUX86State *env, int index) } } +void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) +{ + int i; + + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_remove(env, i); + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_insert(env, i); + } +} +#endif + static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) { target_ulong dr6; @@ -161,20 +176,12 @@ void helper_single_step(CPUX86State *env) void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) { #ifndef CONFIG_USER_ONLY - 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 < DR7_MAX_BP; i++) { - hw_breakpoint_remove(env, i); - } - env->dr[7] = t0; - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); - } + cpu_x86_update_dr7(env, t0); } else { env->dr[reg] = t0; } diff --git a/target-i386/cpu.h b/target-i386/cpu.h index a395b4b07a..70d1b219d9 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -235,6 +235,7 @@ #define DR7_TYPE_SHIFT 16 #define DR7_LEN_SHIFT 18 #define DR7_FIXED_1 0x00000400 +#define DR7_GLOBAL_BP_MASK 0xaa #define DR7_LOCAL_BP_MASK 0x55 #define DR7_MAX_BP 4 #define DR7_TYPE_BP_INST 0x0 @@ -1154,14 +1155,13 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index) return (len == 2) ? 8 : len + 1; } -void hw_breakpoint_insert(CPUX86State *env, int index); -void hw_breakpoint_remove(CPUX86State *env, int index); void breakpoint_handler(CPUState *cs); /* will be suppressed */ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); +void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7); /* hw/pc.c */ uint64_t cpu_get_tsc(CPUX86State *env); diff --git a/target-i386/machine.c b/target-i386/machine.c index 67373663d0..a18e16e0de 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -367,8 +367,12 @@ static int cpu_post_load(void *opaque, int version_id) cpu_breakpoint_remove_all(cs, BP_CPU); cpu_watchpoint_remove_all(cs, BP_CPU); - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); + { + /* Indicate all breakpoints disabled, as they are, then + let the helper re-enable them. */ + target_ulong dr7 = env->dr[7]; + env->dr[7] = dr7 & ~(DR7_GLOBAL_BP_MASK | DR7_LOCAL_BP_MASK); + cpu_x86_update_dr7(env, dr7); } tlb_flush(cs, 1); diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index 1cbe559366..20ee892224 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -501,13 +501,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, #ifndef CONFIG_USER_ONLY /* reset local breakpoints */ if (env->dr[7] & DR7_LOCAL_BP_MASK) { - for (i = 0; i < DR7_MAX_BP; i++) { - if (hw_local_breakpoint_enabled(env->dr[7], i) && - !hw_global_breakpoint_enabled(env->dr[7], i)) { - hw_breakpoint_remove(env, i); - } - } - env->dr[7] &= ~DR7_LOCAL_BP_MASK; + cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); } #endif } From 36eb6e096729f9aade3a6af7dbe4d0a990335d7e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Sep 2015 11:45:09 -0700 Subject: [PATCH 04/13] target-i386: Re-introduce optimal breakpoint removal Before the last patch, we had an efficient loop that disabled local breakpoints on task switch. Re-add that, but in a more general way that handles changes to the global enable bits too. Signed-off-by: Richard Henderson Signed-off-by: Paolo Bonzini Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index f14788a1e9..23ce828491 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -82,14 +82,36 @@ static void hw_breakpoint_remove(CPUX86State *env, int index) void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) { + target_ulong old_dr7 = env->dr[7]; int i; - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_remove(env, i); - } - env->dr[7] = new_dr7; - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); + /* If nothing is changing except the global/local enable bits, + then we can make the change more efficient. */ + if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { + /* Fold the global and local enable bits together into the + global fields, then xor to show which registers have + changed collective enable state. */ + int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; + + for (i = 0; i < DR7_MAX_BP; i++) { + if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { + hw_breakpoint_remove(env, i); + } + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { + hw_breakpoint_insert(env, i); + } + } + } else { + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_remove(env, i); + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_insert(env, i); + } } } #endif From 9055330ffbf5ca85f024c29874799d9c8bd17aa9 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 8 Oct 2015 17:10:27 -0300 Subject: [PATCH 05/13] target-i386: Ensure bit 10 on DR7 is never cleared Bit 10 of DR7 is documented as always set to 1, so ensure that's always the case. Reviewed-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index 23ce828491..49472ea85b 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -85,6 +85,8 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) target_ulong old_dr7 = env->dr[7]; int i; + new_dr7 |= DR7_FIXED_1; + /* If nothing is changing except the global/local enable bits, then we can make the change more efficient. */ if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { From 696ad9e4b27a49a9706010d00b31b17fe1f0d569 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Sep 2015 11:45:10 -0700 Subject: [PATCH 06/13] target-i386: Move hw_*breakpoint_* functions They're only used from bpt_helper.c now. Signed-off-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 29 ++++++++++++++++++++++++++++- target-i386/cpu.h | 27 --------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index 49472ea85b..ca58ab78c7 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -22,6 +22,33 @@ #ifndef CONFIG_USER_ONLY +static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 1; +} + +static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 2; + +} +static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) +{ + return hw_global_breakpoint_enabled(dr7, index) || + hw_local_breakpoint_enabled(dr7, index); +} + +static inline int hw_breakpoint_type(unsigned long dr7, int index) +{ + return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; +} + +static inline int hw_breakpoint_len(unsigned long dr7, int index) +{ + int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); + return (len == 2) ? 8 : len + 1; +} + static void hw_breakpoint_insert(CPUX86State *env, int index) { CPUState *cs = CPU(x86_env_get_cpu(env)); @@ -116,7 +143,6 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) } } } -#endif static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) { @@ -187,6 +213,7 @@ void breakpoint_handler(CPUState *cs) } } } +#endif void helper_single_step(CPUX86State *env) { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 70d1b219d9..0ccd5130fb 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -1128,33 +1128,6 @@ void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val); void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val); #endif -static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 1; -} - -static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 2; - -} -static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) -{ - return hw_global_breakpoint_enabled(dr7, index) || - hw_local_breakpoint_enabled(dr7, index); -} - -static inline int hw_breakpoint_type(unsigned long dr7, int index) -{ - return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; -} - -static inline int hw_breakpoint_len(unsigned long dr7, int index) -{ - int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); - return (len == 2) ? 8 : len + 1; -} - void breakpoint_handler(CPUState *cs); /* will be suppressed */ From 7525b55051277717329cf64a9e1d5cff840d6f38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Sep 2015 11:45:11 -0700 Subject: [PATCH 07/13] target-i386: Optimize setting dr[0-3] If the debug register is not enabled, we need do nothing besides update the register. Signed-off-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index ca58ab78c7..7ff41a6c97 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -228,9 +228,14 @@ void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) { #ifndef CONFIG_USER_ONLY if (reg < 4) { - hw_breakpoint_remove(env, reg); - env->dr[reg] = t0; - hw_breakpoint_insert(env, reg); + if (hw_breakpoint_enabled(env->dr[7], reg) + && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { + hw_breakpoint_remove(env, reg); + env->dr[reg] = t0; + hw_breakpoint_insert(env, reg); + } else { + env->dr[reg] = t0; + } } else if (reg == 7) { cpu_x86_update_dr7(env, t0); } else { From 5223a9423c5fb9e32b0c3eaaa2c0bf8c5cfd6866 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 19 Oct 2015 15:14:35 -0200 Subject: [PATCH 08/13] target-i386: Handle I/O breakpoints Signed-off-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 99 +++++++++++++++++++++++++++++----------- target-i386/cpu.h | 2 + target-i386/helper.h | 1 + target-i386/translate.c | 20 +++++++- 4 files changed, 94 insertions(+), 28 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index 7ff41a6c97..117cea2564 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -49,60 +49,72 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index) return (len == 2) ? 8 : len + 1; } -static void hw_breakpoint_insert(CPUX86State *env, int index) +static int hw_breakpoint_insert(CPUX86State *env, int index) { CPUState *cs = CPU(x86_env_get_cpu(env)); - int type = 0, err = 0; + target_ulong dr7 = env->dr[7]; + target_ulong drN = env->dr[index]; + int err = 0; - switch (hw_breakpoint_type(env->dr[7], index)) { + switch (hw_breakpoint_type(dr7, index)) { case DR7_TYPE_BP_INST: - if (hw_breakpoint_enabled(env->dr[7], index)) { - err = cpu_breakpoint_insert(cs, env->dr[index], BP_CPU, + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_breakpoint_insert(cs, drN, BP_CPU, &env->cpu_breakpoint[index]); } break; - case DR7_TYPE_DATA_WR: - type = BP_CPU | BP_MEM_WRITE; - break; + case DR7_TYPE_IO_RW: - /* No support for I/O watchpoints yet */ + /* Notice when we should enable calls to bpt_io. */ + return hw_breakpoint_enabled(env->dr[7], index) + ? HF_IOBPT_MASK : 0; + + case DR7_TYPE_DATA_WR: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_WRITE, + &env->cpu_watchpoint[index]); + } break; + case DR7_TYPE_DATA_RW: - type = BP_CPU | BP_MEM_ACCESS; + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_ACCESS, + &env->cpu_watchpoint[index]); + } break; } - - if (type != 0) { - err = cpu_watchpoint_insert(cs, env->dr[index], - hw_breakpoint_len(env->dr[7], index), - type, &env->cpu_watchpoint[index]); - } - if (err) { env->cpu_breakpoint[index] = NULL; } + return 0; } static void hw_breakpoint_remove(CPUX86State *env, int index) { - CPUState *cs; + CPUState *cs = CPU(x86_env_get_cpu(env)); - if (!env->cpu_breakpoint[index]) { - return; - } - cs = CPU(x86_env_get_cpu(env)); switch (hw_breakpoint_type(env->dr[7], index)) { case DR7_TYPE_BP_INST: - if (hw_breakpoint_enabled(env->dr[7], index)) { + if (env->cpu_breakpoint[index]) { cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); + env->cpu_breakpoint[index] = NULL; } break; + case DR7_TYPE_DATA_WR: case DR7_TYPE_DATA_RW: - cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + if (env->cpu_breakpoint[index]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + env->cpu_breakpoint[index] = NULL; + } break; + case DR7_TYPE_IO_RW: - /* No support for I/O watchpoints yet */ + /* HF_IOBPT_MASK cleared elsewhere. */ break; } } @@ -110,6 +122,7 @@ static void hw_breakpoint_remove(CPUX86State *env, int index) void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) { target_ulong old_dr7 = env->dr[7]; + int iobpt = 0; int i; new_dr7 |= DR7_FIXED_1; @@ -130,7 +143,10 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) env->dr[7] = new_dr7; for (i = 0; i < DR7_MAX_BP; i++) { if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { - hw_breakpoint_insert(env, i); + iobpt |= hw_breakpoint_insert(env, i); + } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(new_dr7, i)) { + iobpt |= HF_IOBPT_MASK; } } } else { @@ -139,9 +155,11 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) } env->dr[7] = new_dr7; for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_insert(env, i); + iobpt |= hw_breakpoint_insert(env, i); } } + + env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; } static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) @@ -243,3 +261,30 @@ void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) } #endif } + +/* Check if Port I/O is trapped by a breakpoint. */ +void helper_bpt_io(CPUX86State *env, uint32_t port, + uint32_t size, target_ulong next_eip) +{ +#ifndef CONFIG_USER_ONLY + target_ulong dr7 = env->dr[7]; + int i, hit = 0; + + for (i = 0; i < DR7_MAX_BP; ++i) { + if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(dr7, i)) { + int bpt_len = hw_breakpoint_len(dr7, i); + if (port + size - 1 >= env->dr[i] + && port <= env->dr[i] + bpt_len - 1) { + hit |= 1 << i; + } + } + } + + if (hit) { + env->dr[6] = (env->dr[6] & ~0xf) | hit; + env->eip = next_eip; + raise_exception(env, EXCP01_DB); + } +#endif +} diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 0ccd5130fb..0bf6f889cb 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -155,6 +155,7 @@ #define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ #define HF_SMAP_SHIFT 23 /* CR4.SMAP */ +#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -178,6 +179,7 @@ #define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) #define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) +#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT) /* hflags2 */ diff --git a/target-i386/helper.h b/target-i386/helper.h index 8454a048d2..e9858c04e7 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -92,6 +92,7 @@ DEF_HELPER_3(outw, void, env, i32, i32) DEF_HELPER_2(inw, tl, env, i32) DEF_HELPER_3(outl, void, env, i32, i32) DEF_HELPER_2(inl, tl, env, i32) +DEF_HELPER_FLAGS_4(bpt_io, TCG_CALL_NO_WG, void, env, i32, i32, tl) DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) DEF_HELPER_3(vmexit, void, env, i32, i64) diff --git a/target-i386/translate.c b/target-i386/translate.c index ef10e685cc..ceed4d1efb 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -1154,6 +1154,19 @@ static inline void gen_cmps(DisasContext *s, TCGMemOp ot) gen_op_add_reg_T0(s->aflag, R_EDI); } +static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) +{ + if (s->flags & HF_IOBPT_MASK) { + TCGv_i32 t_size = tcg_const_i32(1 << ot); + TCGv t_next = tcg_const_tl(s->pc - s->cs_base); + + gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); + tcg_temp_free_i32(t_size); + tcg_temp_free(t_next); + } +} + + static inline void gen_ins(DisasContext *s, TCGMemOp ot) { if (s->tb->cflags & CF_USE_ICOUNT) { @@ -1170,6 +1183,7 @@ static inline void gen_ins(DisasContext *s, TCGMemOp ot) gen_op_st_v(s, ot, cpu_T[0], cpu_A0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } @@ -1187,9 +1201,9 @@ static inline void gen_outs(DisasContext *s, TCGMemOp ot) tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[0]); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); - gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } @@ -6269,6 +6283,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_i32(cpu_tmp2_i32, val); gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6289,6 +6304,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_i32(cpu_tmp2_i32, val); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6306,6 +6322,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6325,6 +6342,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); + gen_bpt_io(s, cpu_tmp2_i32, ot); if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); From d0052339236072bbf08c1d600c0906126b1ab258 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 15 Sep 2015 11:45:13 -0700 Subject: [PATCH 09/13] target-i386: Check CR4[DE] for processing DR4/DR5 Introduce helper_get_dr so that we don't have to put CR4[DE] into the scarce HFLAGS resource. At the same time, rename helper_movl_drN_T0 to helper_set_dr and set the helper flags. Signed-off-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 46 +++++++++++++++++++++++++++++++++++----- target-i386/cpu.h | 2 +- target-i386/helper.h | 3 ++- target-i386/translate.c | 10 +++++---- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index 117cea2564..144cfd43fc 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -242,10 +242,11 @@ void helper_single_step(CPUX86State *env) raise_exception(env, EXCP01_DB); } -void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) +void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) { #ifndef CONFIG_USER_ONLY - if (reg < 4) { + switch (reg) { + case 0: case 1: case 2: case 3: if (hw_breakpoint_enabled(env->dr[7], reg) && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { hw_breakpoint_remove(env, reg); @@ -254,14 +255,49 @@ void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) } else { env->dr[reg] = t0; } - } else if (reg == 7) { + return; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 6: + env->dr[6] = t0; + return; + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 7: cpu_x86_update_dr7(env, t0); - } else { - env->dr[reg] = t0; + return; } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); #endif } +target_ulong helper_get_dr(CPUX86State *env, int reg) +{ + switch (reg) { + case 0: case 1: case 2: case 3: case 6: case 7: + return env->dr[reg]; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[6]; + } + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[7]; + } + } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); +} + /* Check if Port I/O is trapped by a breakpoint. */ void helper_bpt_io(CPUX86State *env, uint32_t port, uint32_t size, target_ulong next_eip) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 0bf6f889cb..62f78798b6 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -920,7 +920,7 @@ typedef struct CPUX86State { int error_code; int exception_is_int; target_ulong exception_next_eip; - target_ulong dr[8]; /* debug registers */ + target_ulong dr[8]; /* debug registers; note dr4 and dr5 are unused */ union { struct CPUBreakpoint *cpu_breakpoint[4]; struct CPUWatchpoint *cpu_watchpoint[4]; diff --git a/target-i386/helper.h b/target-i386/helper.h index e9858c04e7..ecfcfd1a97 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -40,7 +40,8 @@ DEF_HELPER_2(read_crN, tl, env, int) DEF_HELPER_3(write_crN, void, env, int, tl) DEF_HELPER_2(lmsw, void, env, tl) DEF_HELPER_1(clts, void, env) -DEF_HELPER_3(movl_drN_T0, void, env, int, tl) +DEF_HELPER_FLAGS_3(set_dr, TCG_CALL_NO_WG, void, env, int, tl) +DEF_HELPER_FLAGS_2(get_dr, TCG_CALL_NO_WG, tl, env, int) DEF_HELPER_2(invlpg, void, env, tl) DEF_HELPER_4(enter_level, void, env, int, int, tl) diff --git a/target-i386/translate.c b/target-i386/translate.c index ceed4d1efb..764b1e44b7 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -7627,18 +7627,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, ot = MO_64; else ot = MO_32; - /* XXX: do it dynamically with CR4.DE bit */ - if (reg == 4 || reg == 5 || reg >= 8) + if (reg >= 8) { goto illegal_op; + } if (b & 2) { gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); gen_op_mov_v_reg(ot, cpu_T[0], rm); - gen_helper_movl_drN_T0(cpu_env, tcg_const_i32(reg), cpu_T[0]); + tcg_gen_movi_i32(cpu_tmp2_i32, reg); + gen_helper_set_dr(cpu_env, cpu_tmp2_i32, cpu_T[0]); gen_jmp_im(s->pc - s->cs_base); gen_eob(s); } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); - tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,dr[reg])); + tcg_gen_movi_i32(cpu_tmp2_i32, reg); + gen_helper_get_dr(cpu_T[0], cpu_env, cpu_tmp2_i32); gen_op_mov_reg_v(ot, rm, cpu_T[0]); } } From 462f8ed1f1eac189ef50d9586eae8af90dbe426f Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Wed, 7 Oct 2015 17:19:18 -0300 Subject: [PATCH 10/13] target-i386: Ensure always-1 bits on DR6 can't be cleared Bits 4-11 and 16-31 on DR6 are documented as always 1, so ensure they can't be cleared by software. Reviewed-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/bpt_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index 144cfd43fc..dac1b1a360 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -262,7 +262,7 @@ void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) } /* fallthru */ case 6: - env->dr[6] = t0; + env->dr[6] = t0 | DR6_FIXED_1; return; case 5: if (env->cr[4] & CR4_DE_MASK) { From b6c5a6f021f485fc36bca678b2c867e9b6783924 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Wed, 7 Oct 2015 16:39:43 -0300 Subject: [PATCH 11/13] target-i386: Add DE to TCG_FEATURES Now DE is supported by TCG so it can be enabled in CPUID bits. Reviewed-by: Richard Henderson Signed-off-by: Eduardo Habkost --- target-i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 987253df3b..c92dd066fc 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -312,7 +312,7 @@ static const char *cpuid_6_feature_name[] = { CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \ - CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS) + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE) /* partly implemented: CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ /* missing: From 72370dc1149d7c90d2c2218e0d0658bee23a5bf7 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 29 Sep 2015 17:34:22 -0300 Subject: [PATCH 12/13] target-i386: Use 1UL for bit shift Fix undefined behavior detected by clang runtime check: qemu/target-i386/cpu.c:1494:15: runtime error: left shift of 1 by 31 places cannot be represented in type 'int' While doing that, add extra parenthesis for clarity. Reported-by: Peter Maydell Signed-off-by: Eduardo Habkost --- target-i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index c92dd066fc..c1a9e09743 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -1491,7 +1491,7 @@ static void report_unavailable_features(FeatureWord w, uint32_t mask) int i; for (i = 0; i < 32; ++i) { - if (1 << i & mask) { + if ((1UL << i) & mask) { const char *reg = get_register_name_32(f->cpuid_reg); assert(reg); fprintf(stderr, "warning: %s doesn't support requested feature: " From 31bfa2a40004204aee503c6417fbafb5d17e0a51 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Sun, 18 Oct 2015 19:35:27 +0200 Subject: [PATCH 13/13] vl: trivial: minor tweaks to a max-cpu error msg Signed-off-by: Andrew Jones --- vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index 332d8287d8..dffaf09a44 100644 --- a/vl.c +++ b/vl.c @@ -4101,8 +4101,8 @@ int main(int argc, char **argv, char **envp) machine_class->max_cpus = machine_class->max_cpus ?: 1; /* Default to UP */ if (max_cpus > machine_class->max_cpus) { - fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus " - "supported by machine `%s' (%d)\n", max_cpus, + fprintf(stderr, "Number of SMP CPUs requested (%d) exceeds max CPUs " + "supported by machine '%s' (%d)\n", max_cpus, machine_class->name, machine_class->max_cpus); exit(1); }