qemu/target-i386/bpt_helper.c
<<
>>
Prefs
   1/*
   2 *  i386 breakpoint helpers
   3 *
   4 *  Copyright (c) 2003 Fabrice Bellard
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "cpu.h"
  22#include "exec/helper-proto.h"
  23
  24
  25#ifndef CONFIG_USER_ONLY
  26static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index)
  27{
  28    return (dr7 >> (index * 2)) & 1;
  29}
  30
  31static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index)
  32{
  33    return (dr7 >> (index * 2)) & 2;
  34
  35}
  36static inline bool hw_breakpoint_enabled(unsigned long dr7, int index)
  37{
  38    return hw_global_breakpoint_enabled(dr7, index) ||
  39           hw_local_breakpoint_enabled(dr7, index);
  40}
  41
  42static inline int hw_breakpoint_type(unsigned long dr7, int index)
  43{
  44    return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3;
  45}
  46
  47static inline int hw_breakpoint_len(unsigned long dr7, int index)
  48{
  49    int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3);
  50    return (len == 2) ? 8 : len + 1;
  51}
  52
  53static int hw_breakpoint_insert(CPUX86State *env, int index)
  54{
  55    CPUState *cs = CPU(x86_env_get_cpu(env));
  56    target_ulong dr7 = env->dr[7];
  57    target_ulong drN = env->dr[index];
  58    int err = 0;
  59
  60    switch (hw_breakpoint_type(dr7, index)) {
  61    case DR7_TYPE_BP_INST:
  62        if (hw_breakpoint_enabled(dr7, index)) {
  63            err = cpu_breakpoint_insert(cs, drN, BP_CPU,
  64                                        &env->cpu_breakpoint[index]);
  65        }
  66        break;
  67
  68    case DR7_TYPE_IO_RW:
  69        /* Notice when we should enable calls to bpt_io.  */
  70        return hw_breakpoint_enabled(env->dr[7], index)
  71               ? HF_IOBPT_MASK : 0;
  72
  73    case DR7_TYPE_DATA_WR:
  74        if (hw_breakpoint_enabled(dr7, index)) {
  75            err = cpu_watchpoint_insert(cs, drN,
  76                                        hw_breakpoint_len(dr7, index),
  77                                        BP_CPU | BP_MEM_WRITE,
  78                                        &env->cpu_watchpoint[index]);
  79        }
  80        break;
  81
  82    case DR7_TYPE_DATA_RW:
  83        if (hw_breakpoint_enabled(dr7, index)) {
  84            err = cpu_watchpoint_insert(cs, drN,
  85                                        hw_breakpoint_len(dr7, index),
  86                                        BP_CPU | BP_MEM_ACCESS,
  87                                        &env->cpu_watchpoint[index]);
  88        }
  89        break;
  90    }
  91    if (err) {
  92        env->cpu_breakpoint[index] = NULL;
  93    }
  94    return 0;
  95}
  96
  97static void hw_breakpoint_remove(CPUX86State *env, int index)
  98{
  99    CPUState *cs = CPU(x86_env_get_cpu(env));
 100
 101    switch (hw_breakpoint_type(env->dr[7], index)) {
 102    case DR7_TYPE_BP_INST:
 103        if (env->cpu_breakpoint[index]) {
 104            cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]);
 105            env->cpu_breakpoint[index] = NULL;
 106        }
 107        break;
 108
 109    case DR7_TYPE_DATA_WR:
 110    case DR7_TYPE_DATA_RW:
 111        if (env->cpu_breakpoint[index]) {
 112            cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]);
 113            env->cpu_breakpoint[index] = NULL;
 114        }
 115        break;
 116
 117    case DR7_TYPE_IO_RW:
 118        /* HF_IOBPT_MASK cleared elsewhere.  */
 119        break;
 120    }
 121}
 122
 123void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7)
 124{
 125    target_ulong old_dr7 = env->dr[7];
 126    int iobpt = 0;
 127    int i;
 128
 129    new_dr7 |= DR7_FIXED_1;
 130
 131    /* If nothing is changing except the global/local enable bits,
 132       then we can make the change more efficient.  */
 133    if (((old_dr7 ^ new_dr7) & ~0xff) == 0) {
 134        /* Fold the global and local enable bits together into the
 135           global fields, then xor to show which registers have
 136           changed collective enable state.  */
 137        int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff;
 138
 139        for (i = 0; i < DR7_MAX_BP; i++) {
 140            if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) {
 141                hw_breakpoint_remove(env, i);
 142            }
 143        }
 144        env->dr[7] = new_dr7;
 145        for (i = 0; i < DR7_MAX_BP; i++) {
 146            if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) {
 147                iobpt |= hw_breakpoint_insert(env, i);
 148            } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW
 149                       && hw_breakpoint_enabled(new_dr7, i)) {
 150                iobpt |= HF_IOBPT_MASK;
 151            }
 152        }
 153    } else {
 154        for (i = 0; i < DR7_MAX_BP; i++) {
 155            hw_breakpoint_remove(env, i);
 156        }
 157        env->dr[7] = new_dr7;
 158        for (i = 0; i < DR7_MAX_BP; i++) {
 159            iobpt |= hw_breakpoint_insert(env, i);
 160        }
 161    }
 162
 163    env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt;
 164}
 165
 166static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update)
 167{
 168    target_ulong dr6;
 169    int reg;
 170    bool hit_enabled = false;
 171
 172    dr6 = env->dr[6] & ~0xf;
 173    for (reg = 0; reg < DR7_MAX_BP; reg++) {
 174        bool bp_match = false;
 175        bool wp_match = false;
 176
 177        switch (hw_breakpoint_type(env->dr[7], reg)) {
 178        case DR7_TYPE_BP_INST:
 179            if (env->dr[reg] == env->eip) {
 180                bp_match = true;
 181            }
 182            break;
 183        case DR7_TYPE_DATA_WR:
 184        case DR7_TYPE_DATA_RW:
 185            if (env->cpu_watchpoint[reg] &&
 186                env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) {
 187                wp_match = true;
 188            }
 189            break;
 190        case DR7_TYPE_IO_RW:
 191            break;
 192        }
 193        if (bp_match || wp_match) {
 194            dr6 |= 1 << reg;
 195            if (hw_breakpoint_enabled(env->dr[7], reg)) {
 196                hit_enabled = true;
 197            }
 198        }
 199    }
 200
 201    if (hit_enabled || force_dr6_update) {
 202        env->dr[6] = dr6;
 203    }
 204
 205    return hit_enabled;
 206}
 207
 208void breakpoint_handler(CPUState *cs)
 209{
 210    X86CPU *cpu = X86_CPU(cs);
 211    CPUX86State *env = &cpu->env;
 212    CPUBreakpoint *bp;
 213
 214    if (cs->watchpoint_hit) {
 215        if (cs->watchpoint_hit->flags & BP_CPU) {
 216            cs->watchpoint_hit = NULL;
 217            if (check_hw_breakpoints(env, false)) {
 218                raise_exception(env, EXCP01_DB);
 219            } else {
 220                cpu_resume_from_signal(cs, NULL);
 221            }
 222        }
 223    } else {
 224        QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
 225            if (bp->pc == env->eip) {
 226                if (bp->flags & BP_CPU) {
 227                    check_hw_breakpoints(env, true);
 228                    raise_exception(env, EXCP01_DB);
 229                }
 230                break;
 231            }
 232        }
 233    }
 234}
 235#endif
 236
 237void helper_single_step(CPUX86State *env)
 238{
 239#ifndef CONFIG_USER_ONLY
 240    check_hw_breakpoints(env, true);
 241    env->dr[6] |= DR6_BS;
 242#endif
 243    raise_exception(env, EXCP01_DB);
 244}
 245
 246void helper_set_dr(CPUX86State *env, int reg, target_ulong t0)
 247{
 248#ifndef CONFIG_USER_ONLY
 249    switch (reg) {
 250    case 0: case 1: case 2: case 3:
 251        if (hw_breakpoint_enabled(env->dr[7], reg)
 252            && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) {
 253            hw_breakpoint_remove(env, reg);
 254            env->dr[reg] = t0;
 255            hw_breakpoint_insert(env, reg);
 256        } else {
 257            env->dr[reg] = t0;
 258        }
 259        return;
 260    case 4:
 261        if (env->cr[4] & CR4_DE_MASK) {
 262            break;
 263        }
 264        /* fallthru */
 265    case 6:
 266        env->dr[6] = t0 | DR6_FIXED_1;
 267        return;
 268    case 5:
 269        if (env->cr[4] & CR4_DE_MASK) {
 270            break;
 271        }
 272        /* fallthru */
 273    case 7:
 274        cpu_x86_update_dr7(env, t0);
 275        return;
 276    }
 277    raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
 278#endif
 279}
 280
 281target_ulong helper_get_dr(CPUX86State *env, int reg)
 282{
 283    switch (reg) {
 284    case 0: case 1: case 2: case 3: case 6: case 7:
 285        return env->dr[reg];
 286    case 4:
 287        if (env->cr[4] & CR4_DE_MASK) {
 288            break;
 289        } else {
 290            return env->dr[6];
 291        }
 292    case 5:
 293        if (env->cr[4] & CR4_DE_MASK) {
 294            break;
 295        } else {
 296            return env->dr[7];
 297        }
 298    }
 299    raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
 300}
 301
 302/* Check if Port I/O is trapped by a breakpoint.  */
 303void helper_bpt_io(CPUX86State *env, uint32_t port,
 304                   uint32_t size, target_ulong next_eip)
 305{
 306#ifndef CONFIG_USER_ONLY
 307    target_ulong dr7 = env->dr[7];
 308    int i, hit = 0;
 309
 310    for (i = 0; i < DR7_MAX_BP; ++i) {
 311        if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW
 312            && hw_breakpoint_enabled(dr7, i)) {
 313            int bpt_len = hw_breakpoint_len(dr7, i);
 314            if (port + size - 1 >= env->dr[i]
 315                && port <= env->dr[i] + bpt_len - 1) {
 316                hit |= 1 << i;
 317            }
 318        }
 319    }
 320
 321    if (hit) {
 322        env->dr[6] = (env->dr[6] & ~0xf) | hit;
 323        env->eip = next_eip;
 324        raise_exception(env, EXCP01_DB);
 325    }
 326#endif
 327}
 328