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