qemu/target/i386/tcg/sysemu/bpt_helper.c
<<
>>
Prefs
   1/*
   2 *  i386 breakpoint helpers - sysemu code
   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 "tcg/helper-tcg.h"
  25
  26
  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_watchpoint[index]) {
 113            cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]);
 114            env->cpu_watchpoint[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
 167bool 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
 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_loop_exit_noexc(cs);
 221            }
 222        }
 223    } else {
 224        if (cpu_breakpoint_test(cs, env->eip, BP_CPU)) {
 225            check_hw_breakpoints(env, true);
 226            raise_exception(env, EXCP01_DB);
 227        }
 228    }
 229}
 230
 231target_ulong helper_get_dr(CPUX86State *env, int reg)
 232{
 233    if (reg >= 4 && reg < 6) {
 234        if (env->cr[4] & CR4_DE_MASK) {
 235            raise_exception_ra(env, EXCP06_ILLOP, GETPC());
 236        } else {
 237            reg += 2;
 238        }
 239    }
 240
 241    return env->dr[reg];
 242}
 243
 244void helper_set_dr(CPUX86State *env, int reg, target_ulong t0)
 245{
 246    if (reg >= 4 && reg < 6) {
 247        if (env->cr[4] & CR4_DE_MASK) {
 248            raise_exception_ra(env, EXCP06_ILLOP, GETPC());
 249        } else {
 250            reg += 2;
 251        }
 252    }
 253
 254    if (reg < 4) {
 255        if (hw_breakpoint_enabled(env->dr[7], reg)
 256            && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) {
 257            hw_breakpoint_remove(env, reg);
 258            env->dr[reg] = t0;
 259            hw_breakpoint_insert(env, reg);
 260        } else {
 261            env->dr[reg] = t0;
 262        }
 263    } else {
 264        if (t0 & DR_RESERVED_MASK) {
 265            raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
 266        }
 267        if (reg == 6) {
 268            env->dr[6] = t0 | DR6_FIXED_1;
 269        } else {
 270            cpu_x86_update_dr7(env, t0);
 271        }
 272    }
 273}
 274
 275/* Check if Port I/O is trapped by a breakpoint.  */
 276void helper_bpt_io(CPUX86State *env, uint32_t port,
 277                   uint32_t size, target_ulong next_eip)
 278{
 279    target_ulong dr7 = env->dr[7];
 280    int i, hit = 0;
 281
 282    for (i = 0; i < DR7_MAX_BP; ++i) {
 283        if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW
 284            && hw_breakpoint_enabled(dr7, i)) {
 285            int bpt_len = hw_breakpoint_len(dr7, i);
 286            if (port + size - 1 >= env->dr[i]
 287                && port <= env->dr[i] + bpt_len - 1) {
 288                hit |= 1 << i;
 289            }
 290        }
 291    }
 292
 293    if (hit) {
 294        env->dr[6] = (env->dr[6] & ~0xf) | hit;
 295        env->eip = next_eip;
 296        raise_exception(env, EXCP01_DB);
 297    }
 298}
 299