Commit 01df040b52474b463d00fe908d5a14e1ecdc75bc

Authored by aliguori
1 parent 2dc9f411

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 <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5747 c046a42c-6fe2-441c-8c8c-71466251a162
linux-user/main.c
... ... @@ -403,7 +403,7 @@ void cpu_loop(CPUX86State *env)
403 403 queue_signal(env, info.si_signo, &info);
404 404 }
405 405 break;
406   - case EXCP01_SSTP:
  406 + case EXCP01_DB:
407 407 case EXCP03_INT3:
408 408 #ifndef TARGET_X86_64
409 409 if (env->eflags & VM_MASK) {
... ... @@ -413,7 +413,7 @@ void cpu_loop(CPUX86State *env)
413 413 {
414 414 info.si_signo = SIGTRAP;
415 415 info.si_errno = 0;
416   - if (trapnr == EXCP01_SSTP) {
  416 + if (trapnr == EXCP01_DB) {
417 417 info.si_code = TARGET_TRAP_BRKPT;
418 418 info._sifields._sigfault._addr = env->eip;
419 419 } else {
... ...
target-i386/cpu.h
... ... @@ -205,6 +205,16 @@
205 205 #define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT)
206 206 #define CR4_OSXMMEXCPT_MASK (1 << 10)
207 207  
  208 +#define DR6_BD (1 << 13)
  209 +#define DR6_BS (1 << 14)
  210 +#define DR6_BT (1 << 15)
  211 +#define DR6_FIXED_1 0xffff0ff0
  212 +
  213 +#define DR7_GD (1 << 13)
  214 +#define DR7_TYPE_SHIFT 16
  215 +#define DR7_LEN_SHIFT 18
  216 +#define DR7_FIXED_1 0x00000400
  217 +
208 218 #define PG_PRESENT_BIT 0
209 219 #define PG_RW_BIT 1
210 220 #define PG_USER_BIT 2
... ... @@ -362,7 +372,7 @@
362 372 #define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */
363 373  
364 374 #define EXCP00_DIVZ 0
365   -#define EXCP01_SSTP 1
  375 +#define EXCP01_DB 1
366 376 #define EXCP02_NMI 2
367 377 #define EXCP03_INT3 3
368 378 #define EXCP04_INTO 4
... ... @@ -596,6 +606,10 @@ typedef struct CPUX86State {
596 606 int exception_is_int;
597 607 target_ulong exception_next_eip;
598 608 target_ulong dr[8]; /* debug registers */
  609 + union {
  610 + CPUBreakpoint *cpu_breakpoint[4];
  611 + CPUWatchpoint *cpu_watchpoint[4];
  612 + }; /* break/watchpoints for dr[0..3] */
599 613 uint32_t smbase;
600 614 int old_exception; /* exception in flight */
601 615  
... ... @@ -789,6 +803,26 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
789 803 }
790 804 #endif
791 805  
  806 +static inline int hw_breakpoint_enabled(unsigned long dr7, int index)
  807 +{
  808 + return (dr7 >> (index * 2)) & 3;
  809 +}
  810 +
  811 +static inline int hw_breakpoint_type(unsigned long dr7, int index)
  812 +{
  813 + return (dr7 >> (DR7_TYPE_SHIFT + (index * 2))) & 3;
  814 +}
  815 +
  816 +static inline int hw_breakpoint_len(unsigned long dr7, int index)
  817 +{
  818 + int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 2))) & 3);
  819 + return (len == 2) ? 8 : len + 1;
  820 +}
  821 +
  822 +void hw_breakpoint_insert(CPUState *env, int index);
  823 +void hw_breakpoint_remove(CPUState *env, int index);
  824 +int check_hw_breakpoints(CPUState *env, int force_dr6_update);
  825 +
792 826 #include "cpu-all.h"
793 827 #include "exec-all.h"
794 828  
... ...
target-i386/helper.c
... ... @@ -34,8 +34,6 @@
34 34  
35 35 //#define DEBUG_MMU
36 36  
37   -static int cpu_x86_register (CPUX86State *env, const char *cpu_model);
38   -
39 37 static void add_flagname_to_bitmaps(char *flagname, uint32_t *features,
40 38 uint32_t *ext_features,
41 39 uint32_t *ext2_features,
... ... @@ -93,35 +91,6 @@ static void add_flagname_to_bitmaps(char *flagname, uint32_t *features,
93 91 fprintf(stderr, "CPU feature %s not found\n", flagname);
94 92 }
95 93  
96   -CPUX86State *cpu_x86_init(const char *cpu_model)
97   -{
98   - CPUX86State *env;
99   - static int inited;
100   -
101   - env = qemu_mallocz(sizeof(CPUX86State));
102   - if (!env)
103   - return NULL;
104   - cpu_exec_init(env);
105   - env->cpu_model_str = cpu_model;
106   -
107   - /* init various static tables */
108   - if (!inited) {
109   - inited = 1;
110   - optimize_flags_init();
111   - }
112   - if (cpu_x86_register(env, cpu_model) < 0) {
113   - cpu_x86_close(env);
114   - return NULL;
115   - }
116   - cpu_reset(env);
117   -#ifdef USE_KQEMU
118   - kqemu_init(env);
119   -#endif
120   - if (kvm_enabled())
121   - kvm_init_vcpu(env);
122   - return env;
123   -}
124   -
125 94 typedef struct x86_def_t {
126 95 const char *name;
127 96 uint32_t level;
... ... @@ -499,6 +468,12 @@ void cpu_reset(CPUX86State *env)
499 468 env->fpuc = 0x37f;
500 469  
501 470 env->mxcsr = 0x1f80;
  471 +
  472 + memset(env->dr, 0, sizeof(env->dr));
  473 + env->dr[6] = DR6_FIXED_1;
  474 + env->dr[7] = DR7_FIXED_1;
  475 + cpu_breakpoint_remove_all(env, BP_CPU);
  476 + cpu_watchpoint_remove_all(env, BP_CPU);
502 477 }
503 478  
504 479 void cpu_x86_close(CPUX86State *env)
... ... @@ -1295,6 +1270,105 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
1295 1270 paddr = (pte & TARGET_PAGE_MASK) + page_offset;
1296 1271 return paddr;
1297 1272 }
  1273 +
  1274 +void hw_breakpoint_insert(CPUState *env, int index)
  1275 +{
  1276 + int type, err = 0;
  1277 +
  1278 + switch (hw_breakpoint_type(env->dr[7], index)) {
  1279 + case 0:
  1280 + if (hw_breakpoint_enabled(env->dr[7], index))
  1281 + err = cpu_breakpoint_insert(env, env->dr[index], BP_CPU,
  1282 + &env->cpu_breakpoint[index]);
  1283 + break;
  1284 + case 1:
  1285 + type = BP_CPU | BP_MEM_WRITE;
  1286 + goto insert_wp;
  1287 + case 2:
  1288 + /* No support for I/O watchpoints yet */
  1289 + break;
  1290 + case 3:
  1291 + type = BP_CPU | BP_MEM_ACCESS;
  1292 + insert_wp:
  1293 + err = cpu_watchpoint_insert(env, env->dr[index],
  1294 + hw_breakpoint_len(env->dr[7], index),
  1295 + type, &env->cpu_watchpoint[index]);
  1296 + break;
  1297 + }
  1298 + if (err)
  1299 + env->cpu_breakpoint[index] = NULL;
  1300 +}
  1301 +
  1302 +void hw_breakpoint_remove(CPUState *env, int index)
  1303 +{
  1304 + if (!env->cpu_breakpoint[index])
  1305 + return;
  1306 + switch (hw_breakpoint_type(env->dr[7], index)) {
  1307 + case 0:
  1308 + if (hw_breakpoint_enabled(env->dr[7], index))
  1309 + cpu_breakpoint_remove_by_ref(env, env->cpu_breakpoint[index]);
  1310 + break;
  1311 + case 1:
  1312 + case 3:
  1313 + cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[index]);
  1314 + break;
  1315 + case 2:
  1316 + /* No support for I/O watchpoints yet */
  1317 + break;
  1318 + }
  1319 +}
  1320 +
  1321 +int check_hw_breakpoints(CPUState *env, int force_dr6_update)
  1322 +{
  1323 + target_ulong dr6;
  1324 + int reg, type;
  1325 + int hit_enabled = 0;
  1326 +
  1327 + dr6 = env->dr[6] & ~0xf;
  1328 + for (reg = 0; reg < 4; reg++) {
  1329 + type = hw_breakpoint_type(env->dr[7], reg);
  1330 + if ((type == 0 && env->dr[reg] == env->eip) ||
  1331 + ((type & 1) && env->cpu_watchpoint[reg] &&
  1332 + (env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT))) {
  1333 + dr6 |= 1 << reg;
  1334 + if (hw_breakpoint_enabled(env->dr[7], reg))
  1335 + hit_enabled = 1;
  1336 + }
  1337 + }
  1338 + if (hit_enabled || force_dr6_update)
  1339 + env->dr[6] = dr6;
  1340 + return hit_enabled;
  1341 +}
  1342 +
  1343 +static CPUDebugExcpHandler *prev_debug_excp_handler;
  1344 +
  1345 +void raise_exception(int exception_index);
  1346 +
  1347 +static void breakpoint_handler(CPUState *env)
  1348 +{
  1349 + CPUBreakpoint *bp;
  1350 +
  1351 + if (env->watchpoint_hit) {
  1352 + if (env->watchpoint_hit->flags & BP_CPU) {
  1353 + env->watchpoint_hit = NULL;
  1354 + if (check_hw_breakpoints(env, 0))
  1355 + raise_exception(EXCP01_DB);
  1356 + else
  1357 + cpu_resume_from_signal(env, NULL);
  1358 + }
  1359 + } else {
  1360 + for (bp = env->breakpoints; bp != NULL; bp = bp->next)
  1361 + if (bp->pc == env->eip) {
  1362 + if (bp->flags & BP_CPU) {
  1363 + check_hw_breakpoints(env, 1);
  1364 + raise_exception(EXCP01_DB);
  1365 + }
  1366 + break;
  1367 + }
  1368 + }
  1369 + if (prev_debug_excp_handler)
  1370 + prev_debug_excp_handler(env);
  1371 +}
1298 1372 #endif /* !CONFIG_USER_ONLY */
1299 1373  
1300 1374 static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx,
... ... @@ -1532,3 +1606,36 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index,
1532 1606 break;
1533 1607 }
1534 1608 }
  1609 +
  1610 +CPUX86State *cpu_x86_init(const char *cpu_model)
  1611 +{
  1612 + CPUX86State *env;
  1613 + static int inited;
  1614 +
  1615 + env = qemu_mallocz(sizeof(CPUX86State));
  1616 + if (!env)
  1617 + return NULL;
  1618 + cpu_exec_init(env);
  1619 + env->cpu_model_str = cpu_model;
  1620 +
  1621 + /* init various static tables */
  1622 + if (!inited) {
  1623 + inited = 1;
  1624 + optimize_flags_init();
  1625 +#ifndef CONFIG_USER_ONLY
  1626 + prev_debug_excp_handler =
  1627 + cpu_set_debug_excp_handler(breakpoint_handler);
  1628 +#endif
  1629 + }
  1630 + if (cpu_x86_register(env, cpu_model) < 0) {
  1631 + cpu_x86_close(env);
  1632 + return NULL;
  1633 + }
  1634 + cpu_reset(env);
  1635 +#ifdef USE_KQEMU
  1636 + kqemu_init(env);
  1637 +#endif
  1638 + if (kvm_enabled())
  1639 + kvm_init_vcpu(env);
  1640 + return env;
  1641 +}
... ...
target-i386/machine.c
... ... @@ -259,6 +259,10 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
259 259  
260 260 for(i = 0; i < 8; i++)
261 261 qemu_get_betls(f, &env->dr[i]);
  262 + cpu_breakpoint_remove_all(env, BP_CPU);
  263 + cpu_watchpoint_remove_all(env, BP_CPU);
  264 + for (i = 0; i < 4; i++)
  265 + hw_breakpoint_insert(env, i);
262 266  
263 267 /* MMU */
264 268 qemu_get_sbe32s(f, &a20_mask);
... ...
target-i386/op_helper.c
... ... @@ -496,6 +496,17 @@ static void switch_tss(int tss_selector,
496 496 /* XXX: different exception if CALL ? */
497 497 raise_exception_err(EXCP0D_GPF, 0);
498 498 }
  499 +
  500 +#ifndef CONFIG_USER_ONLY
  501 + /* reset local breakpoints */
  502 + if (env->dr[7] & 0x55) {
  503 + for (i = 0; i < 4; i++) {
  504 + if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
  505 + hw_breakpoint_remove(env, i);
  506 + }
  507 + env->dr[7] &= ~0x55;
  508 + }
  509 +#endif
499 510 }
500 511  
501 512 /* check if Port I/O is allowed in TSS */
... ... @@ -1879,8 +1890,11 @@ void helper_cmpxchg16b(target_ulong a0)
1879 1890  
1880 1891 void helper_single_step(void)
1881 1892 {
1882   - env->dr[6] |= 0x4000;
1883   - raise_exception(EXCP01_SSTP);
  1893 +#ifndef CONFIG_USER_ONLY
  1894 + check_hw_breakpoints(env, 1);
  1895 + env->dr[6] |= DR6_BS;
  1896 +#endif
  1897 + raise_exception(EXCP01_DB);
1884 1898 }
1885 1899  
1886 1900 void helper_cpuid(void)
... ... @@ -2868,6 +2882,10 @@ target_ulong helper_read_crN(int reg)
2868 2882 void helper_write_crN(int reg, target_ulong t0)
2869 2883 {
2870 2884 }
  2885 +
  2886 +void helper_movl_drN_T0(int reg, target_ulong t0)
  2887 +{
  2888 +}
2871 2889 #else
2872 2890 target_ulong helper_read_crN(int reg)
2873 2891 {
... ... @@ -2913,6 +2931,24 @@ void helper_write_crN(int reg, target_ulong t0)
2913 2931 break;
2914 2932 }
2915 2933 }
  2934 +
  2935 +void helper_movl_drN_T0(int reg, target_ulong t0)
  2936 +{
  2937 + int i;
  2938 +
  2939 + if (reg < 4) {
  2940 + hw_breakpoint_remove(env, reg);
  2941 + env->dr[reg] = t0;
  2942 + hw_breakpoint_insert(env, reg);
  2943 + } else if (reg == 7) {
  2944 + for (i = 0; i < 4; i++)
  2945 + hw_breakpoint_remove(env, i);
  2946 + env->dr[7] = t0;
  2947 + for (i = 0; i < 4; i++)
  2948 + hw_breakpoint_insert(env, i);
  2949 + } else
  2950 + env->dr[reg] = t0;
  2951 +}
2916 2952 #endif
2917 2953  
2918 2954 void helper_lmsw(target_ulong t0)
... ... @@ -2929,12 +2965,6 @@ void helper_clts(void)
2929 2965 env->hflags &= ~HF_TS_MASK;
2930 2966 }
2931 2967  
2932   -/* XXX: do more */
2933   -void helper_movl_drN_T0(int reg, target_ulong t0)
2934   -{
2935   - env->dr[reg] = t0;
2936   -}
2937   -
2938 2968 void helper_invlpg(target_ulong addr)
2939 2969 {
2940 2970 helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
... ...