16d8d1a03SClaudio Fontana /* 26d8d1a03SClaudio Fontana * i386 breakpoint helpers - sysemu code 36d8d1a03SClaudio Fontana * 46d8d1a03SClaudio Fontana * Copyright (c) 2003 Fabrice Bellard 56d8d1a03SClaudio Fontana * 66d8d1a03SClaudio Fontana * This library is free software; you can redistribute it and/or 76d8d1a03SClaudio Fontana * modify it under the terms of the GNU Lesser General Public 86d8d1a03SClaudio Fontana * License as published by the Free Software Foundation; either 96d8d1a03SClaudio Fontana * version 2.1 of the License, or (at your option) any later version. 106d8d1a03SClaudio Fontana * 116d8d1a03SClaudio Fontana * This library is distributed in the hope that it will be useful, 126d8d1a03SClaudio Fontana * but WITHOUT ANY WARRANTY; without even the implied warranty of 136d8d1a03SClaudio Fontana * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 146d8d1a03SClaudio Fontana * Lesser General Public License for more details. 156d8d1a03SClaudio Fontana * 166d8d1a03SClaudio Fontana * You should have received a copy of the GNU Lesser General Public 176d8d1a03SClaudio Fontana * License along with this library; if not, see <http://www.gnu.org/licenses/>. 186d8d1a03SClaudio Fontana */ 196d8d1a03SClaudio Fontana 206d8d1a03SClaudio Fontana #include "qemu/osdep.h" 216d8d1a03SClaudio Fontana #include "cpu.h" 226d8d1a03SClaudio Fontana #include "exec/exec-all.h" 236d8d1a03SClaudio Fontana #include "exec/helper-proto.h" 246d8d1a03SClaudio Fontana #include "tcg/helper-tcg.h" 256d8d1a03SClaudio Fontana 266d8d1a03SClaudio Fontana 276d8d1a03SClaudio Fontana static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) 286d8d1a03SClaudio Fontana { 296d8d1a03SClaudio Fontana return (dr7 >> (index * 2)) & 1; 306d8d1a03SClaudio Fontana } 316d8d1a03SClaudio Fontana 326d8d1a03SClaudio Fontana static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) 336d8d1a03SClaudio Fontana { 346d8d1a03SClaudio Fontana return (dr7 >> (index * 2)) & 2; 356d8d1a03SClaudio Fontana 366d8d1a03SClaudio Fontana } 376d8d1a03SClaudio Fontana static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) 386d8d1a03SClaudio Fontana { 396d8d1a03SClaudio Fontana return hw_global_breakpoint_enabled(dr7, index) || 406d8d1a03SClaudio Fontana hw_local_breakpoint_enabled(dr7, index); 416d8d1a03SClaudio Fontana } 426d8d1a03SClaudio Fontana 436d8d1a03SClaudio Fontana static inline int hw_breakpoint_type(unsigned long dr7, int index) 446d8d1a03SClaudio Fontana { 456d8d1a03SClaudio Fontana return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; 466d8d1a03SClaudio Fontana } 476d8d1a03SClaudio Fontana 486d8d1a03SClaudio Fontana static inline int hw_breakpoint_len(unsigned long dr7, int index) 496d8d1a03SClaudio Fontana { 506d8d1a03SClaudio Fontana int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); 516d8d1a03SClaudio Fontana return (len == 2) ? 8 : len + 1; 526d8d1a03SClaudio Fontana } 536d8d1a03SClaudio Fontana 546d8d1a03SClaudio Fontana static int hw_breakpoint_insert(CPUX86State *env, int index) 556d8d1a03SClaudio Fontana { 566d8d1a03SClaudio Fontana CPUState *cs = env_cpu(env); 576d8d1a03SClaudio Fontana target_ulong dr7 = env->dr[7]; 586d8d1a03SClaudio Fontana target_ulong drN = env->dr[index]; 596d8d1a03SClaudio Fontana int err = 0; 606d8d1a03SClaudio Fontana 616d8d1a03SClaudio Fontana switch (hw_breakpoint_type(dr7, index)) { 626d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 636d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 646d8d1a03SClaudio Fontana err = cpu_breakpoint_insert(cs, drN, BP_CPU, 656d8d1a03SClaudio Fontana &env->cpu_breakpoint[index]); 666d8d1a03SClaudio Fontana } 676d8d1a03SClaudio Fontana break; 686d8d1a03SClaudio Fontana 696d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 706d8d1a03SClaudio Fontana /* Notice when we should enable calls to bpt_io. */ 716d8d1a03SClaudio Fontana return hw_breakpoint_enabled(env->dr[7], index) 726d8d1a03SClaudio Fontana ? HF_IOBPT_MASK : 0; 736d8d1a03SClaudio Fontana 746d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 756d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 766d8d1a03SClaudio Fontana err = cpu_watchpoint_insert(cs, drN, 776d8d1a03SClaudio Fontana hw_breakpoint_len(dr7, index), 786d8d1a03SClaudio Fontana BP_CPU | BP_MEM_WRITE, 796d8d1a03SClaudio Fontana &env->cpu_watchpoint[index]); 806d8d1a03SClaudio Fontana } 816d8d1a03SClaudio Fontana break; 826d8d1a03SClaudio Fontana 836d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 846d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 856d8d1a03SClaudio Fontana err = cpu_watchpoint_insert(cs, drN, 866d8d1a03SClaudio Fontana hw_breakpoint_len(dr7, index), 876d8d1a03SClaudio Fontana BP_CPU | BP_MEM_ACCESS, 886d8d1a03SClaudio Fontana &env->cpu_watchpoint[index]); 896d8d1a03SClaudio Fontana } 906d8d1a03SClaudio Fontana break; 916d8d1a03SClaudio Fontana } 926d8d1a03SClaudio Fontana if (err) { 936d8d1a03SClaudio Fontana env->cpu_breakpoint[index] = NULL; 946d8d1a03SClaudio Fontana } 956d8d1a03SClaudio Fontana return 0; 966d8d1a03SClaudio Fontana } 976d8d1a03SClaudio Fontana 986d8d1a03SClaudio Fontana static void hw_breakpoint_remove(CPUX86State *env, int index) 996d8d1a03SClaudio Fontana { 1006d8d1a03SClaudio Fontana CPUState *cs = env_cpu(env); 1016d8d1a03SClaudio Fontana 1026d8d1a03SClaudio Fontana switch (hw_breakpoint_type(env->dr[7], index)) { 1036d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 1046d8d1a03SClaudio Fontana if (env->cpu_breakpoint[index]) { 1056d8d1a03SClaudio Fontana cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); 1066d8d1a03SClaudio Fontana env->cpu_breakpoint[index] = NULL; 1076d8d1a03SClaudio Fontana } 1086d8d1a03SClaudio Fontana break; 1096d8d1a03SClaudio Fontana 1106d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 1116d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 112080ac335SDmitry Voronetskiy if (env->cpu_watchpoint[index]) { 1136d8d1a03SClaudio Fontana cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); 114080ac335SDmitry Voronetskiy env->cpu_watchpoint[index] = NULL; 1156d8d1a03SClaudio Fontana } 1166d8d1a03SClaudio Fontana break; 1176d8d1a03SClaudio Fontana 1186d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 1196d8d1a03SClaudio Fontana /* HF_IOBPT_MASK cleared elsewhere. */ 1206d8d1a03SClaudio Fontana break; 1216d8d1a03SClaudio Fontana } 1226d8d1a03SClaudio Fontana } 1236d8d1a03SClaudio Fontana 1246d8d1a03SClaudio Fontana void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) 1256d8d1a03SClaudio Fontana { 1266d8d1a03SClaudio Fontana target_ulong old_dr7 = env->dr[7]; 1276d8d1a03SClaudio Fontana int iobpt = 0; 1286d8d1a03SClaudio Fontana int i; 1296d8d1a03SClaudio Fontana 1306d8d1a03SClaudio Fontana new_dr7 |= DR7_FIXED_1; 1316d8d1a03SClaudio Fontana 1326d8d1a03SClaudio Fontana /* If nothing is changing except the global/local enable bits, 1336d8d1a03SClaudio Fontana then we can make the change more efficient. */ 1346d8d1a03SClaudio Fontana if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { 1356d8d1a03SClaudio Fontana /* Fold the global and local enable bits together into the 1366d8d1a03SClaudio Fontana global fields, then xor to show which registers have 1376d8d1a03SClaudio Fontana changed collective enable state. */ 1386d8d1a03SClaudio Fontana int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; 1396d8d1a03SClaudio Fontana 1406d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1416d8d1a03SClaudio Fontana if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { 1426d8d1a03SClaudio Fontana hw_breakpoint_remove(env, i); 1436d8d1a03SClaudio Fontana } 1446d8d1a03SClaudio Fontana } 1456d8d1a03SClaudio Fontana env->dr[7] = new_dr7; 1466d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1476d8d1a03SClaudio Fontana if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { 1486d8d1a03SClaudio Fontana iobpt |= hw_breakpoint_insert(env, i); 1496d8d1a03SClaudio Fontana } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW 1506d8d1a03SClaudio Fontana && hw_breakpoint_enabled(new_dr7, i)) { 1516d8d1a03SClaudio Fontana iobpt |= HF_IOBPT_MASK; 1526d8d1a03SClaudio Fontana } 1536d8d1a03SClaudio Fontana } 1546d8d1a03SClaudio Fontana } else { 1556d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1566d8d1a03SClaudio Fontana hw_breakpoint_remove(env, i); 1576d8d1a03SClaudio Fontana } 1586d8d1a03SClaudio Fontana env->dr[7] = new_dr7; 1596d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1606d8d1a03SClaudio Fontana iobpt |= hw_breakpoint_insert(env, i); 1616d8d1a03SClaudio Fontana } 1626d8d1a03SClaudio Fontana } 1636d8d1a03SClaudio Fontana 1646d8d1a03SClaudio Fontana env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; 1656d8d1a03SClaudio Fontana } 1666d8d1a03SClaudio Fontana 1676d8d1a03SClaudio Fontana bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) 1686d8d1a03SClaudio Fontana { 1696d8d1a03SClaudio Fontana target_ulong dr6; 1706d8d1a03SClaudio Fontana int reg; 1716d8d1a03SClaudio Fontana bool hit_enabled = false; 1726d8d1a03SClaudio Fontana 1736d8d1a03SClaudio Fontana dr6 = env->dr[6] & ~0xf; 1746d8d1a03SClaudio Fontana for (reg = 0; reg < DR7_MAX_BP; reg++) { 1756d8d1a03SClaudio Fontana bool bp_match = false; 1766d8d1a03SClaudio Fontana bool wp_match = false; 1776d8d1a03SClaudio Fontana 1786d8d1a03SClaudio Fontana switch (hw_breakpoint_type(env->dr[7], reg)) { 1796d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 1806d8d1a03SClaudio Fontana if (env->dr[reg] == env->eip) { 1816d8d1a03SClaudio Fontana bp_match = true; 1826d8d1a03SClaudio Fontana } 1836d8d1a03SClaudio Fontana break; 1846d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 1856d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 1866d8d1a03SClaudio Fontana if (env->cpu_watchpoint[reg] && 1876d8d1a03SClaudio Fontana env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { 1886d8d1a03SClaudio Fontana wp_match = true; 1896d8d1a03SClaudio Fontana } 1906d8d1a03SClaudio Fontana break; 1916d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 1926d8d1a03SClaudio Fontana break; 1936d8d1a03SClaudio Fontana } 1946d8d1a03SClaudio Fontana if (bp_match || wp_match) { 1956d8d1a03SClaudio Fontana dr6 |= 1 << reg; 1966d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(env->dr[7], reg)) { 1976d8d1a03SClaudio Fontana hit_enabled = true; 1986d8d1a03SClaudio Fontana } 1996d8d1a03SClaudio Fontana } 2006d8d1a03SClaudio Fontana } 2016d8d1a03SClaudio Fontana 2026d8d1a03SClaudio Fontana if (hit_enabled || force_dr6_update) { 2036d8d1a03SClaudio Fontana env->dr[6] = dr6; 2046d8d1a03SClaudio Fontana } 2056d8d1a03SClaudio Fontana 2066d8d1a03SClaudio Fontana return hit_enabled; 2076d8d1a03SClaudio Fontana } 2086d8d1a03SClaudio Fontana 2096d8d1a03SClaudio Fontana void breakpoint_handler(CPUState *cs) 2106d8d1a03SClaudio Fontana { 2116d8d1a03SClaudio Fontana X86CPU *cpu = X86_CPU(cs); 2126d8d1a03SClaudio Fontana CPUX86State *env = &cpu->env; 2136d8d1a03SClaudio Fontana 2146d8d1a03SClaudio Fontana if (cs->watchpoint_hit) { 2156d8d1a03SClaudio Fontana if (cs->watchpoint_hit->flags & BP_CPU) { 2166d8d1a03SClaudio Fontana cs->watchpoint_hit = NULL; 2176d8d1a03SClaudio Fontana if (check_hw_breakpoints(env, false)) { 218*cdc829b3SPaolo Bonzini /* 219*cdc829b3SPaolo Bonzini * FIXME: #DB should be delayed by one instruction if 220*cdc829b3SPaolo Bonzini * INHIBIT_IRQ is set (STI cannot trigger a watchpoint). 221*cdc829b3SPaolo Bonzini * The delayed #DB should also fuse with one generated 222*cdc829b3SPaolo Bonzini * by ICEBP (aka INT1). 223*cdc829b3SPaolo Bonzini */ 2246d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 2256d8d1a03SClaudio Fontana } else { 2266d8d1a03SClaudio Fontana cpu_loop_exit_noexc(cs); 2276d8d1a03SClaudio Fontana } 2286d8d1a03SClaudio Fontana } 2296d8d1a03SClaudio Fontana } else { 23050b208b8SRichard Henderson if (cpu_breakpoint_test(cs, env->eip, BP_CPU)) { 2316d8d1a03SClaudio Fontana check_hw_breakpoints(env, true); 2326d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 2336d8d1a03SClaudio Fontana } 2346d8d1a03SClaudio Fontana } 2356d8d1a03SClaudio Fontana } 2366d8d1a03SClaudio Fontana 237533883fdSPaolo Bonzini target_ulong helper_get_dr(CPUX86State *env, int reg) 238533883fdSPaolo Bonzini { 239533883fdSPaolo Bonzini if (reg >= 4 && reg < 6) { 240533883fdSPaolo Bonzini if (env->cr[4] & CR4_DE_MASK) { 241533883fdSPaolo Bonzini raise_exception_ra(env, EXCP06_ILLOP, GETPC()); 242533883fdSPaolo Bonzini } else { 243533883fdSPaolo Bonzini reg += 2; 244533883fdSPaolo Bonzini } 245533883fdSPaolo Bonzini } 246533883fdSPaolo Bonzini 24757f8dbdbSPaolo Bonzini if (env->dr[7] & DR7_GD) { 24857f8dbdbSPaolo Bonzini env->dr[7] &= ~DR7_GD; 24957f8dbdbSPaolo Bonzini env->dr[6] |= DR6_BD; 25057f8dbdbSPaolo Bonzini raise_exception_ra(env, EXCP01_DB, GETPC()); 25157f8dbdbSPaolo Bonzini } 25257f8dbdbSPaolo Bonzini 253533883fdSPaolo Bonzini return env->dr[reg]; 254533883fdSPaolo Bonzini } 255533883fdSPaolo Bonzini 2566d8d1a03SClaudio Fontana void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) 2576d8d1a03SClaudio Fontana { 258533883fdSPaolo Bonzini if (reg >= 4 && reg < 6) { 259533883fdSPaolo Bonzini if (env->cr[4] & CR4_DE_MASK) { 260533883fdSPaolo Bonzini raise_exception_ra(env, EXCP06_ILLOP, GETPC()); 261533883fdSPaolo Bonzini } else { 262533883fdSPaolo Bonzini reg += 2; 263533883fdSPaolo Bonzini } 264533883fdSPaolo Bonzini } 265533883fdSPaolo Bonzini 26657f8dbdbSPaolo Bonzini if (env->dr[7] & DR7_GD) { 26757f8dbdbSPaolo Bonzini env->dr[7] &= ~DR7_GD; 26857f8dbdbSPaolo Bonzini env->dr[6] |= DR6_BD; 26957f8dbdbSPaolo Bonzini raise_exception_ra(env, EXCP01_DB, GETPC()); 27057f8dbdbSPaolo Bonzini } 27157f8dbdbSPaolo Bonzini 272533883fdSPaolo Bonzini if (reg < 4) { 2736d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(env->dr[7], reg) 2746d8d1a03SClaudio Fontana && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { 2756d8d1a03SClaudio Fontana hw_breakpoint_remove(env, reg); 2766d8d1a03SClaudio Fontana env->dr[reg] = t0; 2776d8d1a03SClaudio Fontana hw_breakpoint_insert(env, reg); 2786d8d1a03SClaudio Fontana } else { 2796d8d1a03SClaudio Fontana env->dr[reg] = t0; 2806d8d1a03SClaudio Fontana } 281533883fdSPaolo Bonzini } else { 282533883fdSPaolo Bonzini if (t0 & DR_RESERVED_MASK) { 283533883fdSPaolo Bonzini raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); 2846d8d1a03SClaudio Fontana } 285533883fdSPaolo Bonzini if (reg == 6) { 2866d8d1a03SClaudio Fontana env->dr[6] = t0 | DR6_FIXED_1; 287533883fdSPaolo Bonzini } else { 2886d8d1a03SClaudio Fontana cpu_x86_update_dr7(env, t0); 2896d8d1a03SClaudio Fontana } 290533883fdSPaolo Bonzini } 2916d8d1a03SClaudio Fontana } 2926d8d1a03SClaudio Fontana 2936d8d1a03SClaudio Fontana /* Check if Port I/O is trapped by a breakpoint. */ 2946d8d1a03SClaudio Fontana void helper_bpt_io(CPUX86State *env, uint32_t port, 2956d8d1a03SClaudio Fontana uint32_t size, target_ulong next_eip) 2966d8d1a03SClaudio Fontana { 2976d8d1a03SClaudio Fontana target_ulong dr7 = env->dr[7]; 2986d8d1a03SClaudio Fontana int i, hit = 0; 2996d8d1a03SClaudio Fontana 3006d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; ++i) { 3016d8d1a03SClaudio Fontana if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW 3026d8d1a03SClaudio Fontana && hw_breakpoint_enabled(dr7, i)) { 3036d8d1a03SClaudio Fontana int bpt_len = hw_breakpoint_len(dr7, i); 3046d8d1a03SClaudio Fontana if (port + size - 1 >= env->dr[i] 3056d8d1a03SClaudio Fontana && port <= env->dr[i] + bpt_len - 1) { 3066d8d1a03SClaudio Fontana hit |= 1 << i; 3076d8d1a03SClaudio Fontana } 3086d8d1a03SClaudio Fontana } 3096d8d1a03SClaudio Fontana } 3106d8d1a03SClaudio Fontana 3116d8d1a03SClaudio Fontana if (hit) { 3126d8d1a03SClaudio Fontana env->dr[6] = (env->dr[6] & ~0xf) | hit; 3136d8d1a03SClaudio Fontana env->eip = next_eip; 3146d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 3156d8d1a03SClaudio Fontana } 3166d8d1a03SClaudio Fontana } 317