mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
x86/pv: Guest exception handling in FRED mode
Under FRED, entry_from_pv() handles everything. To start with, implement exception handling in the same manner as entry_from_xen(), although we can unconditionally enable interrupts after the async/fatal events. After entry_from_pv() returns, test_all_events() needs to run to perform exception and interrupt injection. Split entry_FRED_R3() into two and introduce eretu_exit_to_guest() as the latter half, coming unilaterally from restore_all_guest(). For all of this, there is a slightly complicated relationship with CONFIG_PV. entry_FRED_R3() must exist irrespective of CONFIG_PV, because it's the entrypoint registered with hardware. For simplicity, entry_from_pv() is always called, but it collapses into fatal_trap() in the !PV case. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
9ea7d7f939
commit
87cfcbe9f0
3 files changed
+93
-4
No files matched your search
+78
-2
@@ -2267,9 +2267,85 @@ void asmlinkage check_ist_exit(const struct cpu_user_regs *regs, bool ist_exit)
|
|||||||
|
|
||||||
void asmlinkage entry_from_pv(struct cpu_user_regs *regs)
|
void asmlinkage entry_from_pv(struct cpu_user_regs *regs)
|
||||||
{
|
{
|
||||||
/* Copy fred_ss.vector into entry_vector as IDT delivery would have done. */
|
struct fred_info *fi = cpu_regs_fred_info(regs);
|
||||||
regs->entry_vector = regs->fred_ss.vector;
|
uint8_t type = regs->fred_ss.type;
|
||||||
|
uint8_t vec = regs->fred_ss.vector;
|
||||||
|
|
||||||
|
/* Copy fred_ss.vector into entry_vector as IDT delivery would have done. */
|
||||||
|
regs->entry_vector = vec;
|
||||||
|
|
||||||
|
if ( !IS_ENABLED(CONFIG_PV) )
|
||||||
|
goto fatal;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, handle the asynchronous or fatal events. These are either
|
||||||
|
* unrelated to the interrupted context, or may not have valid context
|
||||||
|
* recorded, and all have special rules on how/whether to re-enable IRQs.
|
||||||
|
*/
|
||||||
|
if ( regs->fred_ss.nested )
|
||||||
|
goto fatal;
|
||||||
|
|
||||||
|
switch ( type )
|
||||||
|
{
|
||||||
|
case X86_ET_EXT_INTR:
|
||||||
|
return do_IRQ(regs);
|
||||||
|
|
||||||
|
case X86_ET_NMI:
|
||||||
|
return do_nmi(regs);
|
||||||
|
|
||||||
|
case X86_ET_HW_EXC:
|
||||||
|
switch ( vec )
|
||||||
|
{
|
||||||
|
case X86_EXC_DF: return do_double_fault(regs);
|
||||||
|
case X86_EXC_MC: return do_machine_check(regs);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* With the asynchronous events handled, what remains are the synchronous
|
||||||
|
* ones. PV guest context always had interrupts enabled.
|
||||||
|
*/
|
||||||
|
local_irq_enable();
|
||||||
|
|
||||||
|
switch ( type )
|
||||||
|
{
|
||||||
|
case X86_ET_HW_EXC:
|
||||||
|
case X86_ET_PRIV_SW_EXC:
|
||||||
|
case X86_ET_SW_EXC:
|
||||||
|
switch ( vec )
|
||||||
|
{
|
||||||
|
case X86_EXC_PF: handle_PF(regs, fi->edata); break;
|
||||||
|
case X86_EXC_GP: do_general_protection(regs); break;
|
||||||
|
case X86_EXC_UD: do_invalid_op(regs); break;
|
||||||
|
case X86_EXC_NM: do_device_not_available(regs); break;
|
||||||
|
case X86_EXC_BP: do_int3(regs); break;
|
||||||
|
case X86_EXC_DB: handle_DB(regs, fi->edata); break;
|
||||||
|
case X86_EXC_CP: do_entry_CP(regs); break;
|
||||||
|
|
||||||
|
case X86_EXC_DE:
|
||||||
|
case X86_EXC_OF:
|
||||||
|
case X86_EXC_BR:
|
||||||
|
case X86_EXC_NP:
|
||||||
|
case X86_EXC_SS:
|
||||||
|
case X86_EXC_MF:
|
||||||
|
case X86_EXC_AC:
|
||||||
|
case X86_EXC_XM:
|
||||||
|
do_trap(regs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fatal:
|
||||||
fatal_trap(regs, false);
|
fatal_trap(regs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,20 @@ FUNC(entry_FRED_R3, 4096)
|
|||||||
mov %rsp, %rdi
|
mov %rsp, %rdi
|
||||||
call entry_from_pv
|
call entry_from_pv
|
||||||
|
|
||||||
|
#ifdef CONFIG_PV
|
||||||
|
GET_STACK_END(14)
|
||||||
|
movq STACK_CPUINFO_FIELD(current_vcpu)(%r14), %rbx
|
||||||
|
|
||||||
|
jmp test_all_events
|
||||||
|
#else
|
||||||
|
BUG /* Not Reached */
|
||||||
|
#endif
|
||||||
|
END(entry_FRED_R3)
|
||||||
|
|
||||||
|
FUNC(eretu_exit_to_guest)
|
||||||
POP_GPRS
|
POP_GPRS
|
||||||
eretu
|
eretu
|
||||||
END(entry_FRED_R3)
|
END(eretu_exit_to_guest)
|
||||||
|
|
||||||
/* The Ring0 entrypoint is at Ring3 + 0x100. */
|
/* The Ring0 entrypoint is at Ring3 + 0x100. */
|
||||||
.org entry_FRED_R3 + 0x100, 0xcc
|
.org entry_FRED_R3 + 0x100, 0xcc
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ UNLIKELY_END(syscall_no_callback)
|
|||||||
/* Conditionally clear DF */
|
/* Conditionally clear DF */
|
||||||
and %esi, UREGS_eflags(%rsp)
|
and %esi, UREGS_eflags(%rsp)
|
||||||
/* %rbx: struct vcpu */
|
/* %rbx: struct vcpu */
|
||||||
test_all_events:
|
LABEL(test_all_events, 0)
|
||||||
ASSERT_NOT_IN_ATOMIC
|
ASSERT_NOT_IN_ATOMIC
|
||||||
cli # tests must not race interrupts
|
cli # tests must not race interrupts
|
||||||
/*test_softirqs:*/
|
/*test_softirqs:*/
|
||||||
@@ -152,6 +152,8 @@ END(switch_to_kernel)
|
|||||||
FUNC_LOCAL(restore_all_guest)
|
FUNC_LOCAL(restore_all_guest)
|
||||||
ASSERT_INTERRUPTS_DISABLED
|
ASSERT_INTERRUPTS_DISABLED
|
||||||
|
|
||||||
|
ALTERNATIVE "", "jmp eretu_exit_to_guest", X86_FEATURE_XEN_FRED
|
||||||
|
|
||||||
/* Stash guest SPEC_CTRL value while we can read struct vcpu. */
|
/* Stash guest SPEC_CTRL value while we can read struct vcpu. */
|
||||||
mov VCPU_arch_msrs(%rbx), %rdx
|
mov VCPU_arch_msrs(%rbx), %rdx
|
||||||
mov VCPUMSR_spec_ctrl_raw(%rdx), %r15d
|
mov VCPUMSR_spec_ctrl_raw(%rdx), %r15d
|
||||||
|
|||||||