mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-11 03:24:58 -06:00
accel/tcg: Split out handle_sigsegv_accerr_write
This is the major portion of handle_cpu_signal which is specific to tcg, handling the page protections for the translations. Most of the rest will migrate to linux-user/ shortly. Reviewed-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- v2: Pass guest address to handle_sigsegv_accerr_write.
This commit is contained in:
parent
f920ffdd8e
commit
5e38ba7dde
2 changed files with 72 additions and 41 deletions
|
@ -114,6 +114,52 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write)
|
||||||
return is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
return is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle_sigsegv_accerr_write:
|
||||||
|
* @cpu: the cpu context
|
||||||
|
* @old_set: the sigset_t from the signal ucontext_t
|
||||||
|
* @host_pc: the host pc, adjusted for the signal
|
||||||
|
* @guest_addr: the guest address of the fault
|
||||||
|
*
|
||||||
|
* Return true if the write fault has been handled, and should be re-tried.
|
||||||
|
*
|
||||||
|
* Note that it is important that we don't call page_unprotect() unless
|
||||||
|
* this is really a "write to nonwriteable page" fault, because
|
||||||
|
* page_unprotect() assumes that if it is called for an access to
|
||||||
|
* a page that's writeable this means we had two threads racing and
|
||||||
|
* another thread got there first and already made the page writeable;
|
||||||
|
* so we will retry the access. If we were to call page_unprotect()
|
||||||
|
* for some other kind of fault that should really be passed to the
|
||||||
|
* guest, we'd end up in an infinite loop of retrying the faulting access.
|
||||||
|
*/
|
||||||
|
bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
|
||||||
|
uintptr_t host_pc, abi_ptr guest_addr)
|
||||||
|
{
|
||||||
|
switch (page_unprotect(guest_addr, host_pc)) {
|
||||||
|
case 0:
|
||||||
|
/*
|
||||||
|
* Fault not caused by a page marked unwritable to protect
|
||||||
|
* cached translations, must be the guest binary's problem.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
case 1:
|
||||||
|
/*
|
||||||
|
* Fault caused by protection of cached translation; TBs
|
||||||
|
* invalidated, so resume execution.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
case 2:
|
||||||
|
/*
|
||||||
|
* Fault caused by protection of cached translation, and the
|
||||||
|
* currently executing TB was modified and must be exited immediately.
|
||||||
|
*/
|
||||||
|
cpu_exit_tb_from_sighandler(cpu, old_set);
|
||||||
|
/* NORETURN */
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 'pc' is the host PC at which the exception was raised.
|
* 'pc' is the host PC at which the exception was raised.
|
||||||
* 'address' is the effective address of the memory exception.
|
* 'address' is the effective address of the memory exception.
|
||||||
|
@ -125,8 +171,9 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||||
{
|
{
|
||||||
CPUState *cpu = current_cpu;
|
CPUState *cpu = current_cpu;
|
||||||
CPUClass *cc;
|
CPUClass *cc;
|
||||||
unsigned long address = (unsigned long)info->si_addr;
|
unsigned long host_addr = (unsigned long)info->si_addr;
|
||||||
MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
|
MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
|
||||||
|
abi_ptr guest_addr;
|
||||||
|
|
||||||
/* For synchronous signals we expect to be coming from the vCPU
|
/* For synchronous signals we expect to be coming from the vCPU
|
||||||
* thread (so current_cpu should be valid) and either from running
|
* thread (so current_cpu should be valid) and either from running
|
||||||
|
@ -143,49 +190,21 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||||
|
|
||||||
#if defined(DEBUG_SIGNAL)
|
#if defined(DEBUG_SIGNAL)
|
||||||
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
|
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
|
||||||
pc, address, is_write, *(unsigned long *)old_set);
|
pc, host_addr, is_write, *(unsigned long *)old_set);
|
||||||
#endif
|
#endif
|
||||||
/* XXX: locking issue */
|
|
||||||
/* Note that it is important that we don't call page_unprotect() unless
|
|
||||||
* this is really a "write to nonwriteable page" fault, because
|
|
||||||
* page_unprotect() assumes that if it is called for an access to
|
|
||||||
* a page that's writeable this means we had two threads racing and
|
|
||||||
* another thread got there first and already made the page writeable;
|
|
||||||
* so we will retry the access. If we were to call page_unprotect()
|
|
||||||
* for some other kind of fault that should really be passed to the
|
|
||||||
* guest, we'd end up in an infinite loop of retrying the faulting
|
|
||||||
* access.
|
|
||||||
*/
|
|
||||||
if (is_write && info->si_signo == SIGSEGV && info->si_code == SEGV_ACCERR &&
|
|
||||||
h2g_valid(address)) {
|
|
||||||
switch (page_unprotect(h2g(address), pc)) {
|
|
||||||
case 0:
|
|
||||||
/* Fault not caused by a page marked unwritable to protect
|
|
||||||
* cached translations, must be the guest binary's problem.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* Fault caused by protection of cached translation; TBs
|
|
||||||
* invalidated, so resume execution. Retain helper_retaddr
|
|
||||||
* for a possible second fault.
|
|
||||||
*/
|
|
||||||
return 1;
|
|
||||||
case 2:
|
|
||||||
/* Fault caused by protection of cached translation, and the
|
|
||||||
* currently executing TB was modified and must be exited
|
|
||||||
* immediately. Clear helper_retaddr for next execution.
|
|
||||||
*/
|
|
||||||
cpu_exit_tb_from_sighandler(cpu, old_set);
|
|
||||||
/* NORETURN */
|
|
||||||
|
|
||||||
default:
|
|
||||||
g_assert_not_reached();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert forcefully to guest address space, invalid addresses
|
/* Convert forcefully to guest address space, invalid addresses
|
||||||
are still valid segv ones */
|
are still valid segv ones */
|
||||||
address = h2g_nocheck(address);
|
guest_addr = h2g_nocheck(host_addr);
|
||||||
|
|
||||||
|
/* XXX: locking issue */
|
||||||
|
if (is_write &&
|
||||||
|
info->si_signo == SIGSEGV &&
|
||||||
|
info->si_code == SEGV_ACCERR &&
|
||||||
|
h2g_valid(host_addr) &&
|
||||||
|
handle_sigsegv_accerr_write(cpu, old_set, pc, guest_addr)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is no way the target can handle this other than raising
|
* There is no way the target can handle this other than raising
|
||||||
|
@ -194,7 +213,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||||
|
|
||||||
cc = CPU_GET_CLASS(cpu);
|
cc = CPU_GET_CLASS(cpu);
|
||||||
cc->tcg_ops->tlb_fill(cpu, address, 0, access_type,
|
cc->tcg_ops->tlb_fill(cpu, guest_addr, 0, access_type,
|
||||||
MMU_USER_IDX, false, pc);
|
MMU_USER_IDX, false, pc);
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -673,6 +673,18 @@ static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env,
|
||||||
*/
|
*/
|
||||||
MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write);
|
MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle_sigsegv_accerr_write:
|
||||||
|
* @cpu: the cpu context
|
||||||
|
* @old_set: the sigset_t from the signal ucontext_t
|
||||||
|
* @host_pc: the host pc, adjusted for the signal
|
||||||
|
* @host_addr: the host address of the fault
|
||||||
|
*
|
||||||
|
* Return true if the write fault has been handled, and should be re-tried.
|
||||||
|
*/
|
||||||
|
bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
|
||||||
|
uintptr_t host_pc, abi_ptr guest_addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpu_signal_handler
|
* cpu_signal_handler
|
||||||
* @signum: host signal number
|
* @signum: host signal number
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue