16d8d1a03SClaudio Fontana /* 232cad1ffSPhilippe Mathieu-Daudé * i386 breakpoint helpers - system 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" 24*3e57baa2SRichard Henderson #include "exec/watchpoint.h" 256d8d1a03SClaudio Fontana #include "tcg/helper-tcg.h" 266d8d1a03SClaudio Fontana 276d8d1a03SClaudio Fontana 286d8d1a03SClaudio Fontana static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) 296d8d1a03SClaudio Fontana { 306d8d1a03SClaudio Fontana return (dr7 >> (index * 2)) & 1; 316d8d1a03SClaudio Fontana } 326d8d1a03SClaudio Fontana 336d8d1a03SClaudio Fontana static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) 346d8d1a03SClaudio Fontana { 356d8d1a03SClaudio Fontana return (dr7 >> (index * 2)) & 2; 366d8d1a03SClaudio Fontana 376d8d1a03SClaudio Fontana } 386d8d1a03SClaudio Fontana static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) 396d8d1a03SClaudio Fontana { 406d8d1a03SClaudio Fontana return hw_global_breakpoint_enabled(dr7, index) || 416d8d1a03SClaudio Fontana hw_local_breakpoint_enabled(dr7, index); 426d8d1a03SClaudio Fontana } 436d8d1a03SClaudio Fontana 446d8d1a03SClaudio Fontana static inline int hw_breakpoint_type(unsigned long dr7, int index) 456d8d1a03SClaudio Fontana { 466d8d1a03SClaudio Fontana return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; 476d8d1a03SClaudio Fontana } 486d8d1a03SClaudio Fontana 496d8d1a03SClaudio Fontana static inline int hw_breakpoint_len(unsigned long dr7, int index) 506d8d1a03SClaudio Fontana { 516d8d1a03SClaudio Fontana int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); 526d8d1a03SClaudio Fontana return (len == 2) ? 8 : len + 1; 536d8d1a03SClaudio Fontana } 546d8d1a03SClaudio Fontana 556d8d1a03SClaudio Fontana static int hw_breakpoint_insert(CPUX86State *env, int index) 566d8d1a03SClaudio Fontana { 576d8d1a03SClaudio Fontana CPUState *cs = env_cpu(env); 586d8d1a03SClaudio Fontana target_ulong dr7 = env->dr[7]; 596d8d1a03SClaudio Fontana target_ulong drN = env->dr[index]; 606d8d1a03SClaudio Fontana int err = 0; 616d8d1a03SClaudio Fontana 626d8d1a03SClaudio Fontana switch (hw_breakpoint_type(dr7, index)) { 636d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 646d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 656d8d1a03SClaudio Fontana err = cpu_breakpoint_insert(cs, drN, BP_CPU, 666d8d1a03SClaudio Fontana &env->cpu_breakpoint[index]); 676d8d1a03SClaudio Fontana } 686d8d1a03SClaudio Fontana break; 696d8d1a03SClaudio Fontana 706d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 716d8d1a03SClaudio Fontana /* Notice when we should enable calls to bpt_io. */ 726d8d1a03SClaudio Fontana return hw_breakpoint_enabled(env->dr[7], index) 736d8d1a03SClaudio Fontana ? HF_IOBPT_MASK : 0; 746d8d1a03SClaudio Fontana 756d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 766d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 776d8d1a03SClaudio Fontana err = cpu_watchpoint_insert(cs, drN, 786d8d1a03SClaudio Fontana hw_breakpoint_len(dr7, index), 796d8d1a03SClaudio Fontana BP_CPU | BP_MEM_WRITE, 806d8d1a03SClaudio Fontana &env->cpu_watchpoint[index]); 816d8d1a03SClaudio Fontana } 826d8d1a03SClaudio Fontana break; 836d8d1a03SClaudio Fontana 846d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 856d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(dr7, index)) { 866d8d1a03SClaudio Fontana err = cpu_watchpoint_insert(cs, drN, 876d8d1a03SClaudio Fontana hw_breakpoint_len(dr7, index), 886d8d1a03SClaudio Fontana BP_CPU | BP_MEM_ACCESS, 896d8d1a03SClaudio Fontana &env->cpu_watchpoint[index]); 906d8d1a03SClaudio Fontana } 916d8d1a03SClaudio Fontana break; 926d8d1a03SClaudio Fontana } 936d8d1a03SClaudio Fontana if (err) { 946d8d1a03SClaudio Fontana env->cpu_breakpoint[index] = NULL; 956d8d1a03SClaudio Fontana } 966d8d1a03SClaudio Fontana return 0; 976d8d1a03SClaudio Fontana } 986d8d1a03SClaudio Fontana 996d8d1a03SClaudio Fontana static void hw_breakpoint_remove(CPUX86State *env, int index) 1006d8d1a03SClaudio Fontana { 1016d8d1a03SClaudio Fontana CPUState *cs = env_cpu(env); 1026d8d1a03SClaudio Fontana 1036d8d1a03SClaudio Fontana switch (hw_breakpoint_type(env->dr[7], index)) { 1046d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 1056d8d1a03SClaudio Fontana if (env->cpu_breakpoint[index]) { 1066d8d1a03SClaudio Fontana cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); 1076d8d1a03SClaudio Fontana env->cpu_breakpoint[index] = NULL; 1086d8d1a03SClaudio Fontana } 1096d8d1a03SClaudio Fontana break; 1106d8d1a03SClaudio Fontana 1116d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 1126d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 113080ac335SDmitry Voronetskiy if (env->cpu_watchpoint[index]) { 1146d8d1a03SClaudio Fontana cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); 115080ac335SDmitry Voronetskiy env->cpu_watchpoint[index] = NULL; 1166d8d1a03SClaudio Fontana } 1176d8d1a03SClaudio Fontana break; 1186d8d1a03SClaudio Fontana 1196d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 1206d8d1a03SClaudio Fontana /* HF_IOBPT_MASK cleared elsewhere. */ 1216d8d1a03SClaudio Fontana break; 1226d8d1a03SClaudio Fontana } 1236d8d1a03SClaudio Fontana } 1246d8d1a03SClaudio Fontana 1256d8d1a03SClaudio Fontana void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) 1266d8d1a03SClaudio Fontana { 1276d8d1a03SClaudio Fontana target_ulong old_dr7 = env->dr[7]; 1286d8d1a03SClaudio Fontana int iobpt = 0; 1296d8d1a03SClaudio Fontana int i; 1306d8d1a03SClaudio Fontana 1316d8d1a03SClaudio Fontana new_dr7 |= DR7_FIXED_1; 1326d8d1a03SClaudio Fontana 1336d8d1a03SClaudio Fontana /* If nothing is changing except the global/local enable bits, 1346d8d1a03SClaudio Fontana then we can make the change more efficient. */ 1356d8d1a03SClaudio Fontana if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { 1366d8d1a03SClaudio Fontana /* Fold the global and local enable bits together into the 1376d8d1a03SClaudio Fontana global fields, then xor to show which registers have 1386d8d1a03SClaudio Fontana changed collective enable state. */ 1396d8d1a03SClaudio Fontana int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; 1406d8d1a03SClaudio Fontana 1416d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1426d8d1a03SClaudio Fontana if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { 1436d8d1a03SClaudio Fontana hw_breakpoint_remove(env, i); 1446d8d1a03SClaudio Fontana } 1456d8d1a03SClaudio Fontana } 1466d8d1a03SClaudio Fontana env->dr[7] = new_dr7; 1476d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1486d8d1a03SClaudio Fontana if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { 1496d8d1a03SClaudio Fontana iobpt |= hw_breakpoint_insert(env, i); 1506d8d1a03SClaudio Fontana } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW 1516d8d1a03SClaudio Fontana && hw_breakpoint_enabled(new_dr7, i)) { 1526d8d1a03SClaudio Fontana iobpt |= HF_IOBPT_MASK; 1536d8d1a03SClaudio Fontana } 1546d8d1a03SClaudio Fontana } 1556d8d1a03SClaudio Fontana } else { 1566d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1576d8d1a03SClaudio Fontana hw_breakpoint_remove(env, i); 1586d8d1a03SClaudio Fontana } 1596d8d1a03SClaudio Fontana env->dr[7] = new_dr7; 1606d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; i++) { 1616d8d1a03SClaudio Fontana iobpt |= hw_breakpoint_insert(env, i); 1626d8d1a03SClaudio Fontana } 1636d8d1a03SClaudio Fontana } 1646d8d1a03SClaudio Fontana 1656d8d1a03SClaudio Fontana env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; 1666d8d1a03SClaudio Fontana } 1676d8d1a03SClaudio Fontana 1686d8d1a03SClaudio Fontana bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) 1696d8d1a03SClaudio Fontana { 1706d8d1a03SClaudio Fontana target_ulong dr6; 1716d8d1a03SClaudio Fontana int reg; 1726d8d1a03SClaudio Fontana bool hit_enabled = false; 1736d8d1a03SClaudio Fontana 1746d8d1a03SClaudio Fontana dr6 = env->dr[6] & ~0xf; 1756d8d1a03SClaudio Fontana for (reg = 0; reg < DR7_MAX_BP; reg++) { 1766d8d1a03SClaudio Fontana bool bp_match = false; 1776d8d1a03SClaudio Fontana bool wp_match = false; 1786d8d1a03SClaudio Fontana 1796d8d1a03SClaudio Fontana switch (hw_breakpoint_type(env->dr[7], reg)) { 1806d8d1a03SClaudio Fontana case DR7_TYPE_BP_INST: 1816d8d1a03SClaudio Fontana if (env->dr[reg] == env->eip) { 1826d8d1a03SClaudio Fontana bp_match = true; 1836d8d1a03SClaudio Fontana } 1846d8d1a03SClaudio Fontana break; 1856d8d1a03SClaudio Fontana case DR7_TYPE_DATA_WR: 1866d8d1a03SClaudio Fontana case DR7_TYPE_DATA_RW: 1876d8d1a03SClaudio Fontana if (env->cpu_watchpoint[reg] && 1886d8d1a03SClaudio Fontana env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { 1896d8d1a03SClaudio Fontana wp_match = true; 1906d8d1a03SClaudio Fontana } 1916d8d1a03SClaudio Fontana break; 1926d8d1a03SClaudio Fontana case DR7_TYPE_IO_RW: 1936d8d1a03SClaudio Fontana break; 1946d8d1a03SClaudio Fontana } 1956d8d1a03SClaudio Fontana if (bp_match || wp_match) { 1966d8d1a03SClaudio Fontana dr6 |= 1 << reg; 1976d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(env->dr[7], reg)) { 1986d8d1a03SClaudio Fontana hit_enabled = true; 1996d8d1a03SClaudio Fontana } 2006d8d1a03SClaudio Fontana } 2016d8d1a03SClaudio Fontana } 2026d8d1a03SClaudio Fontana 2036d8d1a03SClaudio Fontana if (hit_enabled || force_dr6_update) { 2046d8d1a03SClaudio Fontana env->dr[6] = dr6; 2056d8d1a03SClaudio Fontana } 2066d8d1a03SClaudio Fontana 2076d8d1a03SClaudio Fontana return hit_enabled; 2086d8d1a03SClaudio Fontana } 2096d8d1a03SClaudio Fontana 2106d8d1a03SClaudio Fontana void breakpoint_handler(CPUState *cs) 2116d8d1a03SClaudio Fontana { 2126d8d1a03SClaudio Fontana X86CPU *cpu = X86_CPU(cs); 2136d8d1a03SClaudio Fontana CPUX86State *env = &cpu->env; 2146d8d1a03SClaudio Fontana 2156d8d1a03SClaudio Fontana if (cs->watchpoint_hit) { 2166d8d1a03SClaudio Fontana if (cs->watchpoint_hit->flags & BP_CPU) { 2176d8d1a03SClaudio Fontana cs->watchpoint_hit = NULL; 2186d8d1a03SClaudio Fontana if (check_hw_breakpoints(env, false)) { 219cdc829b3SPaolo Bonzini /* 220cdc829b3SPaolo Bonzini * FIXME: #DB should be delayed by one instruction if 221cdc829b3SPaolo Bonzini * INHIBIT_IRQ is set (STI cannot trigger a watchpoint). 222cdc829b3SPaolo Bonzini * The delayed #DB should also fuse with one generated 223cdc829b3SPaolo Bonzini * by ICEBP (aka INT1). 224cdc829b3SPaolo Bonzini */ 2256d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 2266d8d1a03SClaudio Fontana } else { 2276d8d1a03SClaudio Fontana cpu_loop_exit_noexc(cs); 2286d8d1a03SClaudio Fontana } 2296d8d1a03SClaudio Fontana } 2306d8d1a03SClaudio Fontana } else { 23150b208b8SRichard Henderson if (cpu_breakpoint_test(cs, env->eip, BP_CPU)) { 2326d8d1a03SClaudio Fontana check_hw_breakpoints(env, true); 2336d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 2346d8d1a03SClaudio Fontana } 2356d8d1a03SClaudio Fontana } 2366d8d1a03SClaudio Fontana } 2376d8d1a03SClaudio Fontana 238533883fdSPaolo Bonzini target_ulong helper_get_dr(CPUX86State *env, int reg) 239533883fdSPaolo Bonzini { 240533883fdSPaolo Bonzini if (reg >= 4 && reg < 6) { 241533883fdSPaolo Bonzini if (env->cr[4] & CR4_DE_MASK) { 242533883fdSPaolo Bonzini raise_exception_ra(env, EXCP06_ILLOP, GETPC()); 243533883fdSPaolo Bonzini } else { 244533883fdSPaolo Bonzini reg += 2; 245533883fdSPaolo Bonzini } 246533883fdSPaolo Bonzini } 247533883fdSPaolo Bonzini 24857f8dbdbSPaolo Bonzini if (env->dr[7] & DR7_GD) { 24957f8dbdbSPaolo Bonzini env->dr[7] &= ~DR7_GD; 25057f8dbdbSPaolo Bonzini env->dr[6] |= DR6_BD; 25157f8dbdbSPaolo Bonzini raise_exception_ra(env, EXCP01_DB, GETPC()); 25257f8dbdbSPaolo Bonzini } 25357f8dbdbSPaolo Bonzini 254533883fdSPaolo Bonzini return env->dr[reg]; 255533883fdSPaolo Bonzini } 256533883fdSPaolo Bonzini 2576d8d1a03SClaudio Fontana void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) 2586d8d1a03SClaudio Fontana { 259533883fdSPaolo Bonzini if (reg >= 4 && reg < 6) { 260533883fdSPaolo Bonzini if (env->cr[4] & CR4_DE_MASK) { 261533883fdSPaolo Bonzini raise_exception_ra(env, EXCP06_ILLOP, GETPC()); 262533883fdSPaolo Bonzini } else { 263533883fdSPaolo Bonzini reg += 2; 264533883fdSPaolo Bonzini } 265533883fdSPaolo Bonzini } 266533883fdSPaolo Bonzini 26757f8dbdbSPaolo Bonzini if (env->dr[7] & DR7_GD) { 26857f8dbdbSPaolo Bonzini env->dr[7] &= ~DR7_GD; 26957f8dbdbSPaolo Bonzini env->dr[6] |= DR6_BD; 27057f8dbdbSPaolo Bonzini raise_exception_ra(env, EXCP01_DB, GETPC()); 27157f8dbdbSPaolo Bonzini } 27257f8dbdbSPaolo Bonzini 273533883fdSPaolo Bonzini if (reg < 4) { 2746d8d1a03SClaudio Fontana if (hw_breakpoint_enabled(env->dr[7], reg) 2756d8d1a03SClaudio Fontana && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { 2766d8d1a03SClaudio Fontana hw_breakpoint_remove(env, reg); 2776d8d1a03SClaudio Fontana env->dr[reg] = t0; 2786d8d1a03SClaudio Fontana hw_breakpoint_insert(env, reg); 2796d8d1a03SClaudio Fontana } else { 2806d8d1a03SClaudio Fontana env->dr[reg] = t0; 2816d8d1a03SClaudio Fontana } 282533883fdSPaolo Bonzini } else { 283533883fdSPaolo Bonzini if (t0 & DR_RESERVED_MASK) { 284533883fdSPaolo Bonzini raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); 2856d8d1a03SClaudio Fontana } 286533883fdSPaolo Bonzini if (reg == 6) { 2876d8d1a03SClaudio Fontana env->dr[6] = t0 | DR6_FIXED_1; 288533883fdSPaolo Bonzini } else { 2896d8d1a03SClaudio Fontana cpu_x86_update_dr7(env, t0); 2906d8d1a03SClaudio Fontana } 291533883fdSPaolo Bonzini } 2926d8d1a03SClaudio Fontana } 2936d8d1a03SClaudio Fontana 2946d8d1a03SClaudio Fontana /* Check if Port I/O is trapped by a breakpoint. */ 2956d8d1a03SClaudio Fontana void helper_bpt_io(CPUX86State *env, uint32_t port, 2966d8d1a03SClaudio Fontana uint32_t size, target_ulong next_eip) 2976d8d1a03SClaudio Fontana { 2986d8d1a03SClaudio Fontana target_ulong dr7 = env->dr[7]; 2996d8d1a03SClaudio Fontana int i, hit = 0; 3006d8d1a03SClaudio Fontana 3016d8d1a03SClaudio Fontana for (i = 0; i < DR7_MAX_BP; ++i) { 3026d8d1a03SClaudio Fontana if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW 3036d8d1a03SClaudio Fontana && hw_breakpoint_enabled(dr7, i)) { 3046d8d1a03SClaudio Fontana int bpt_len = hw_breakpoint_len(dr7, i); 3056d8d1a03SClaudio Fontana if (port + size - 1 >= env->dr[i] 3066d8d1a03SClaudio Fontana && port <= env->dr[i] + bpt_len - 1) { 3076d8d1a03SClaudio Fontana hit |= 1 << i; 3086d8d1a03SClaudio Fontana } 3096d8d1a03SClaudio Fontana } 3106d8d1a03SClaudio Fontana } 3116d8d1a03SClaudio Fontana 3126d8d1a03SClaudio Fontana if (hit) { 3136d8d1a03SClaudio Fontana env->dr[6] = (env->dr[6] & ~0xf) | hit; 3146d8d1a03SClaudio Fontana env->eip = next_eip; 3156d8d1a03SClaudio Fontana raise_exception(env, EXCP01_DB); 3166d8d1a03SClaudio Fontana } 3176d8d1a03SClaudio Fontana } 318