diff options
Diffstat (limited to 'accel')
| -rw-r--r-- | accel/accel-system.c | 2 | ||||
| -rw-r--r-- | accel/accel-system.h | 2 | ||||
| -rw-r--r-- | accel/accel-target.c | 2 | ||||
| -rw-r--r-- | accel/dummy-cpus.c | 1 | ||||
| -rw-r--r-- | accel/hvf/hvf-accel-ops.c | 1 | ||||
| -rw-r--r-- | accel/kvm/kvm-accel-ops.c | 1 | ||||
| -rw-r--r-- | accel/tcg/icount-common.c | 36 | ||||
| -rw-r--r-- | accel/tcg/meson.build | 1 | ||||
| -rw-r--r-- | accel/tcg/tb-maint.c | 24 | ||||
| -rw-r--r-- | accel/tcg/watchpoint.c | 143 |
10 files changed, 167 insertions, 46 deletions
diff --git a/accel/accel-system.c b/accel/accel-system.c index fa8f43757c..f6c947dd82 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -62,7 +62,7 @@ void accel_setup_post(MachineState *ms) } /* initialize the arch-independent accel operation interfaces */ -void accel_init_ops_interfaces(AccelClass *ac) +void accel_system_init_ops_interfaces(AccelClass *ac) { const char *ac_name; char *ops_name; diff --git a/accel/accel-system.h b/accel/accel-system.h index d41c62f21b..2d37c73c97 100644 --- a/accel/accel-system.h +++ b/accel/accel-system.h @@ -10,6 +10,6 @@ #ifndef ACCEL_SYSTEM_H #define ACCEL_SYSTEM_H -void accel_init_ops_interfaces(AccelClass *ac); +void accel_system_init_ops_interfaces(AccelClass *ac); #endif /* ACCEL_SYSTEM_H */ diff --git a/accel/accel-target.c b/accel/accel-target.c index 7e3cbde5df..08626c00c2 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -104,7 +104,7 @@ static void accel_init_cpu_interfaces(AccelClass *ac) void accel_init_interfaces(AccelClass *ac) { #ifndef CONFIG_USER_ONLY - accel_init_ops_interfaces(ac); + accel_system_init_ops_interfaces(ac); #endif /* !CONFIG_USER_ONLY */ accel_init_cpu_interfaces(ac); diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index f4b0ec5890..20519f1ea4 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -27,7 +27,6 @@ static void *dummy_cpu_thread_fn(void *arg) bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->neg.can_do_io = true; current_cpu = cpu; #ifndef _WIN32 diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 8eabb696fa..d94d41ab6d 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -428,7 +428,6 @@ static void *hvf_cpu_thread_fn(void *arg) qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->neg.can_do_io = true; current_cpu = cpu; hvf_init_vcpu(cpu); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 45ff06e953..b3c946dc4b 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -36,7 +36,6 @@ static void *kvm_vcpu_thread_fn(void *arg) bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->neg.can_do_io = true; current_cpu = cpu; r = kvm_init_vcpu(cpu, &error_fatal); diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c index ec57192be8..a4a747d1dc 100644 --- a/accel/tcg/icount-common.c +++ b/accel/tcg/icount-common.c @@ -49,21 +49,19 @@ static bool icount_sleep = true; /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ #define MAX_ICOUNT_SHIFT 10 -/* - * 0 = Do not count executed instructions. - * 1 = Fixed conversion of insn to ns via "shift" option - * 2 = Runtime adaptive algorithm to compute shift - */ -int use_icount; +/* Do not count executed instructions */ +ICountMode use_icount = ICOUNT_DISABLED; static void icount_enable_precise(void) { - use_icount = 1; + /* Fixed conversion of insn to ns via "shift" option */ + use_icount = ICOUNT_PRECISE; } static void icount_enable_adaptive(void) { - use_icount = 2; + /* Runtime adaptive algorithm to compute shift */ + use_icount = ICOUNT_ADAPTATIVE; } /* @@ -256,7 +254,7 @@ static void icount_warp_rt(void) int64_t warp_delta; warp_delta = clock - timers_state.vm_clock_warp_start; - if (icount_enabled() == 2) { + if (icount_enabled() == ICOUNT_ADAPTATIVE) { /* * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too far * ahead of real time (it might already be ahead so careful not @@ -419,7 +417,7 @@ void icount_account_warp_timer(void) icount_warp_rt(); } -void icount_configure(QemuOpts *opts, Error **errp) +bool icount_configure(QemuOpts *opts, Error **errp) { const char *option = qemu_opt_get(opts, "shift"); bool sleep = qemu_opt_get_bool(opts, "sleep", true); @@ -429,27 +427,28 @@ void icount_configure(QemuOpts *opts, Error **errp) if (!option) { if (qemu_opt_get(opts, "align") != NULL) { error_setg(errp, "Please specify shift option when using align"); + return false; } - return; + return true; } if (align && !sleep) { error_setg(errp, "align=on and sleep=off are incompatible"); - return; + return false; } if (strcmp(option, "auto") != 0) { if (qemu_strtol(option, NULL, 0, &time_shift) < 0 || time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) { error_setg(errp, "icount: Invalid shift value"); - return; + return false; } } else if (icount_align_option) { error_setg(errp, "shift=auto and align=on are incompatible"); - return; + return false; } else if (!icount_sleep) { error_setg(errp, "shift=auto and sleep=off are incompatible"); - return; + return false; } icount_sleep = sleep; @@ -463,7 +462,7 @@ void icount_configure(QemuOpts *opts, Error **errp) if (time_shift >= 0) { timers_state.icount_time_shift = time_shift; icount_enable_precise(); - return; + return true; } icount_enable_adaptive(); @@ -491,11 +490,14 @@ void icount_configure(QemuOpts *opts, Error **errp) timer_mod(timers_state.icount_vm_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10); + return true; } void icount_notify_exit(void) { - if (icount_enabled() && current_cpu) { + assert(icount_enabled()); + + if (current_cpu) { qemu_cpu_kick(current_cpu); qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index d25638d6c1..c15ac9ac8f 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -24,6 +24,7 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', + 'watchpoint.c', )) system_ss.add(when: ['CONFIG_TCG'], if_true: files( diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 3d2a896220..da39a43bd8 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1021,7 +1021,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) * Called with mmap_lock held for user-mode emulation * NOTE: this function must not be called while a TB is running. */ -void tb_invalidate_phys_page(tb_page_addr_t addr) +static void tb_invalidate_phys_page(tb_page_addr_t addr) { tb_page_addr_t start, last; @@ -1161,28 +1161,6 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, } /* - * Invalidate all TBs which intersect with the target physical - * address page @addr. - */ -void tb_invalidate_phys_page(tb_page_addr_t addr) -{ - struct page_collection *pages; - tb_page_addr_t start, last; - PageDesc *p; - - p = page_find(addr >> TARGET_PAGE_BITS); - if (p == NULL) { - return; - } - - start = addr & TARGET_PAGE_MASK; - last = addr | ~TARGET_PAGE_MASK; - pages = page_collection_lock(start, last); - tb_invalidate_phys_page_range__locked(pages, p, start, last, 0); - page_collection_unlock(pages); -} - -/* * Invalidate all TBs which intersect with the target physical address range * [start;last]. NOTE: start and end may refer to *different* physical pages. * 'is_cpu_write_access' should be true if called from a real cpu write diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c new file mode 100644 index 0000000000..d3aab11458 --- /dev/null +++ b/accel/tcg/watchpoint.c @@ -0,0 +1,143 @@ +/* + * CPU watchpoints + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "exec/exec-all.h" +#include "exec/translate-all.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "hw/core/tcg-cpu-ops.h" +#include "hw/core/cpu.h" + +/* + * Return true if this watchpoint address matches the specified + * access (ie the address range covered by the watchpoint overlaps + * partially or completely with the address range covered by the + * access). + */ +static inline bool watchpoint_address_matches(CPUWatchpoint *wp, + vaddr addr, vaddr len) +{ + /* + * We know the lengths are non-zero, but a little caution is + * required to avoid errors in the case where the range ends + * exactly at the top of the address space and so addr + len + * wraps round to zero. + */ + vaddr wpend = wp->vaddr + wp->len - 1; + vaddr addrend = addr + len - 1; + + return !(addr > wpend || wp->vaddr > addrend); +} + +/* Return flags for watchpoints that match addr + prot. */ +int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) +{ + CPUWatchpoint *wp; + int ret = 0; + + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (watchpoint_address_matches(wp, addr, len)) { + ret |= wp->flags; + } + } + return ret; +} + +/* Generate a debug exception if a watchpoint has been hit. */ +void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, + MemTxAttrs attrs, int flags, uintptr_t ra) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUWatchpoint *wp; + + assert(tcg_enabled()); + if (cpu->watchpoint_hit) { + /* + * We re-entered the check after replacing the TB. + * Now raise the debug interrupt so that it will + * trigger after the current instruction. + */ + bql_lock(); + cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); + bql_unlock(); + return; + } + + if (cc->tcg_ops->adjust_watchpoint_address) { + /* this is currently used only by ARM BE32 */ + addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); + } + + assert((flags & ~BP_MEM_ACCESS) == 0); + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + int hit_flags = wp->flags & flags; + + if (hit_flags && watchpoint_address_matches(wp, addr, len)) { + if (replay_running_debug()) { + /* + * replay_breakpoint reads icount. + * Force recompile to succeed, because icount may + * be read only at the end of the block. + */ + if (!cpu->neg.can_do_io) { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); + cpu_loop_exit_restore(cpu, ra); + } + /* + * Don't process the watchpoints when we are + * in a reverse debugging operation. + */ + replay_breakpoint(); + return; + } + + wp->flags |= hit_flags << BP_HIT_SHIFT; + wp->hitaddr = MAX(addr, wp->vaddr); + wp->hitattrs = attrs; + + if (wp->flags & BP_CPU + && cc->tcg_ops->debug_check_watchpoint + && !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } + cpu->watchpoint_hit = wp; + + mmap_lock(); + /* This call also restores vCPU state */ + tb_check_watchpoint(cpu, ra); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); + cpu_loop_exit(cpu); + } else { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); + mmap_unlock(); + cpu_loop_exit_noexc(cpu); + } + } else { + wp->flags &= ~BP_WATCHPOINT_HIT; + } + } +} |