diff options
Diffstat (limited to 'accel')
| -rw-r--r-- | accel/tcg/cpu-exec.c | 14 | ||||
| -rw-r--r-- | accel/tcg/tcg-accel-ops-rr.c | 2 | ||||
| -rw-r--r-- | accel/tcg/translate-all.c | 59 | ||||
| -rw-r--r-- | accel/tcg/translator.c | 39 | ||||
| -rw-r--r-- | accel/tcg/user-exec.c | 48 |
5 files changed, 123 insertions, 39 deletions
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index e5c0ccd1a2..75dbc1e4e3 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -651,8 +651,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) loop */ #if defined(TARGET_I386) CPUClass *cc = CPU_GET_CLASS(cpu); - cc->tcg_ops->do_interrupt(cpu); -#endif + cc->tcg_ops->fake_user_interrupt(cpu); +#endif /* TARGET_I386 */ *ret = cpu->exception_index; cpu->exception_index = -1; return true; @@ -685,6 +685,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } +#ifndef CONFIG_USER_ONLY /* * CPU_INTERRUPT_POLL is a virtual event which gets converted into a * "real" interrupt event later. It does not need to be recorded for @@ -698,12 +699,11 @@ static inline bool need_replay_interrupt(int interrupt_request) return true; #endif } +#endif /* !CONFIG_USER_ONLY */ static inline bool cpu_handle_interrupt(CPUState *cpu, TranslationBlock **last_tb) { - CPUClass *cc = CPU_GET_CLASS(cpu); - /* Clear the interrupt flag now since we're processing * cpu->interrupt_request and cpu->exit_request. * Ensure zeroing happens before reading cpu->exit_request or @@ -725,6 +725,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, qemu_mutex_unlock_iothread(); return true; } +#if !defined(CONFIG_USER_ONLY) if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ } else if (interrupt_request & CPU_INTERRUPT_HALT) { @@ -753,12 +754,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, qemu_mutex_unlock_iothread(); return true; } -#endif +#endif /* !TARGET_I386 */ /* The target hook has 3 exit conditions: False when the interrupt isn't processed, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { + CPUClass *cc = CPU_GET_CLASS(cpu); + if (cc->tcg_ops->cpu_exec_interrupt && cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { if (need_replay_interrupt(interrupt_request)) { @@ -777,6 +780,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * reload the 'interrupt_request' value */ interrupt_request = cpu->interrupt_request; } +#endif /* !CONFIG_USER_ONLY */ if (interrupt_request & CPU_INTERRUPT_EXITTB) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index c02c061ecb..a5fd26190e 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -60,8 +60,6 @@ void rr_kick_vcpu_thread(CPUState *unused) static QEMUTimer *rr_kick_vcpu_timer; static CPUState *rr_current_cpu; -#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) - static inline int64_t rr_next_kick_time(void) { return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD; diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index bbfcfb698c..fb9ebfad9e 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1297,31 +1297,8 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, invalidate_page_bitmap(p); #if defined(CONFIG_USER_ONLY) - if (p->flags & PAGE_WRITE) { - target_ulong addr; - PageDesc *p2; - int prot; - - /* force the host page as non writable (writes will have a - page fault + mprotect overhead) */ - page_addr &= qemu_host_page_mask; - prot = 0; - for (addr = page_addr; addr < page_addr + qemu_host_page_size; - addr += TARGET_PAGE_SIZE) { - - p2 = page_find(addr >> TARGET_PAGE_BITS); - if (!p2) { - continue; - } - prot |= p2->flags; - p2->flags &= ~PAGE_WRITE; - } - mprotect(g2h_untagged(page_addr), qemu_host_page_size, - (prot & PAGE_BITS) & ~PAGE_WRITE); - if (DEBUG_TB_INVALIDATE_GATE) { - printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr); - } - } + /* translator_loop() must have made all TB pages non-writable */ + assert(!(p->flags & PAGE_WRITE)); #else /* if some code is already present, then the pages are already protected. So we handle the case where only the first TB is @@ -2394,6 +2371,38 @@ int page_check_range(target_ulong start, target_ulong len, int flags) return 0; } +void page_protect(tb_page_addr_t page_addr) +{ + target_ulong addr; + PageDesc *p; + int prot; + + p = page_find(page_addr >> TARGET_PAGE_BITS); + if (p && (p->flags & PAGE_WRITE)) { + /* + * Force the host page as non writable (writes will have a page fault + + * mprotect overhead). + */ + page_addr &= qemu_host_page_mask; + prot = 0; + for (addr = page_addr; addr < page_addr + qemu_host_page_size; + addr += TARGET_PAGE_SIZE) { + + p = page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + continue; + } + prot |= p->flags; + p->flags &= ~PAGE_WRITE; + } + mprotect(g2h_untagged(page_addr), qemu_host_page_size, + (prot & PAGE_BITS) & ~PAGE_WRITE); + if (DEBUG_TB_INVALIDATE_GATE) { + printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr); + } + } +} + /* called from signal handler: invalidate the code and unprotect the * page. Return 0 if the fault was not handled, 1 if it was handled, * and 2 if it was handled but the caller must cause the TB to be diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index c53a7f8e44..390bd9db0a 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -42,6 +42,15 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; } +static inline void translator_page_protect(DisasContextBase *dcbase, + target_ulong pc) +{ +#ifdef CONFIG_USER_ONLY + dcbase->page_protect_end = pc | ~TARGET_PAGE_MASK; + page_protect(pc); +#endif +} + void translator_loop(const TranslatorOps *ops, DisasContextBase *db, CPUState *cpu, TranslationBlock *tb, int max_insns) { @@ -56,6 +65,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, db->num_insns = 0; db->max_insns = max_insns; db->singlestep_enabled = cflags & CF_SINGLE_STEP; + translator_page_protect(db, db->pc_next); ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ @@ -137,3 +147,32 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, } #endif } + +static inline void translator_maybe_page_protect(DisasContextBase *dcbase, + target_ulong pc, size_t len) +{ +#ifdef CONFIG_USER_ONLY + target_ulong end = pc + len - 1; + + if (end > dcbase->page_protect_end) { + translator_page_protect(dcbase, end); + } +#endif +} + +#define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn) \ + type fullname ## _swap(CPUArchState *env, DisasContextBase *dcbase, \ + abi_ptr pc, bool do_swap) \ + { \ + translator_maybe_page_protect(dcbase, pc, sizeof(type)); \ + type ret = load_fn(env, pc); \ + if (do_swap) { \ + ret = swap_fn(ret); \ + } \ + plugin_insn_append(&ret, sizeof(ret)); \ + return ret; \ + } + +FOR_EACH_TRANSLATOR_LD(GEN_TRANSLATOR_LD) + +#undef GEN_TRANSLATOR_LD diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 90d1a2d327..8fed542622 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -680,18 +680,26 @@ int cpu_signal_handler(int host_signum, void *pinfo, pc = uc->uc_mcontext.psw.addr; - /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead - of the normal 2 arguments. The 3rd argument contains the "int_code" - from the hardware which does in fact contain the is_write value. - The rt signal handler, as far as I can tell, does not give this value - at all. Not that we could get to it from here even if it were. */ - /* ??? This is not even close to complete, since it ignores all - of the read-modify-write instructions. */ + /* + * ??? On linux, the non-rt signal handler has 4 (!) arguments instead + * of the normal 2 arguments. The 4th argument contains the "Translation- + * Exception Identification for DAT Exceptions" from the hardware (aka + * "int_parm_long"), which does in fact contain the is_write value. + * The rt signal handler, as far as I can tell, does not give this value + * at all. Not that we could get to it from here even if it were. + * So fall back to parsing instructions. Treat read-modify-write ones as + * writes, which is not fully correct, but for tracking self-modifying code + * this is better than treating them as reads. Checking si_addr page flags + * might be a viable improvement, albeit a racy one. + */ + /* ??? This is not even close to complete. */ pinsn = (uint16_t *)pc; switch (pinsn[0] >> 8) { case 0x50: /* ST */ case 0x42: /* STC */ case 0x40: /* STH */ + case 0xba: /* CS */ + case 0xbb: /* CDS */ is_write = 1; break; case 0xc4: /* RIL format insns */ @@ -702,6 +710,12 @@ int cpu_signal_handler(int host_signum, void *pinfo, is_write = 1; } break; + case 0xc8: /* SSF format insns */ + switch (pinsn[0] & 0xf) { + case 0x2: /* CSST */ + is_write = 1; + } + break; case 0xe3: /* RXY format insns */ switch (pinsn[2] & 0xff) { case 0x50: /* STY */ @@ -715,7 +729,27 @@ int cpu_signal_handler(int host_signum, void *pinfo, is_write = 1; } break; + case 0xeb: /* RSY format insns */ + switch (pinsn[2] & 0xff) { + case 0x14: /* CSY */ + case 0x30: /* CSG */ + case 0x31: /* CDSY */ + case 0x3e: /* CDSG */ + case 0xe4: /* LANG */ + case 0xe6: /* LAOG */ + case 0xe7: /* LAXG */ + case 0xe8: /* LAAG */ + case 0xea: /* LAALG */ + case 0xf4: /* LAN */ + case 0xf6: /* LAO */ + case 0xf7: /* LAX */ + case 0xfa: /* LAAL */ + case 0xf8: /* LAA */ + is_write = 1; + } + break; } + return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } |