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:
Andrew Cooper committed 2026-03-10 11:57:53 +00:00
1 parent 9ea7d7f939
commit 87cfcbe9f0
3 files changed
+93 -4

No files matched your search

+78 -2
View File
@@ -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);
} }
+12 -1
View File
@@ -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
+3 -1
View File
@@ -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
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.