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 = env_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 = env_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_rechecking_single_step(CPUX86State *env)
 248{
 249    if ((env->eflags & TF_MASK) != 0) {
 250        helper_single_step(env);
 251    }
 252}
 253
 254void helper_set_dr(CPUX86State *env, int reg, target_ulong t0)
 255{
 256#ifndef CONFIG_USER_ONLY
 257    switch (reg) {
 258    case 0: case 1: case 2: case 3:
 259        if (hw_breakpoint_enabled(env->dr[7], reg)
 260            && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) {
 261            hw_breakpoint_remove(env, reg);
 262            env->dr[reg] = t0;
 263            hw_breakpoint_insert(env, reg);
 264        } else {
 265            env->dr[reg] = t0;
 266        }
 267        return;
 268    case 4:
 269        if (env->cr[4] & CR4_DE_MASK) {
 270            break;
 271        }
 272        /* fallthru */
 273    case 6:
 274        env->dr[6] = t0 | DR6_FIXED_1;
 275        return;
 276    case 5:
 277        if (env->cr[4] & CR4_DE_MASK) {
 278            break;
 279        }
 280        /* fallthru */
 281    case 7:
 282        cpu_x86_update_dr7(env, t0);
 283        return;
 284    }
 285    raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
 286#endif
 287}
 288
 289target_ulong helper_get_dr(CPUX86State *env, int reg)
 290{
 291    switch (reg) {
 292    case 0: case 1: case 2: case 3: case 6: case 7:
 293        return env->dr[reg];
 294    case 4:
 295        if (env->cr[4] & CR4_DE_MASK) {
 296            break;
 297        } else {
 298            return env->dr[6];
 299        }
 300    case 5:
 301        if (env->cr[4] & CR4_DE_MASK) {
 302            break;
 303        } else {
 304            return env->dr[7];
 305        }
 306    }
 307    raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
 308}
 309
 310/* Check if Port I/O is trapped by a breakpoint.  */
 311void helper_bpt_io(CPUX86State *env, uint32_t port,
 312                   uint32_t size, target_ulong next_eip)
 313{
 314#ifndef CONFIG_USER_ONLY
 315    target_ulong dr7 = env->dr[7];
 316    int i, hit = 0;
 317
 318    for (i = 0; i < DR7_MAX_BP; ++i) {
 319        if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW
 320            && hw_breakpoint_enabled(dr7, i)) {
 321            int bpt_len = hw_breakpoint_len(dr7, i);
 322            if (port + size - 1 >= env->dr[i]
 323                && port <= env->dr[i] + bpt_len - 1) {
 324                hit |= 1 << i;
 325            }
 326        }
 327    }
 328
 329    if (hit) {
 330        env->dr[6] = (env->dr[6] & ~0xf) | hit;
 331        env->eip = next_eip;
 332        raise_exception(env, EXCP01_DB);
 333    }
 334#endif
 335}
 336