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