Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion dev_tests/src/ratchet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ fn ratchet_maybe_uninit() -> Result<()> {
("dev_tests/", 1),
("litebox/", 1),
("litebox_platform_linux_userland/", 3),
("litebox_platform_lvbs/", 5),
("litebox_shim_linux/", 5),
("litebox_shim_optee/", 1),
],
Expand Down
18 changes: 18 additions & 0 deletions litebox/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,27 @@ pub trait EnterShim {
}

/// The operation to perform after returning from a shim handler
///
/// - `ResumeGuest` and `ExitThread` cover the cases where the platform enters the shim
/// in response to events that occur during guest execution (e.g., a syscall).
/// - `ResumeKernelPlatform` and `ExceptionFixup` cover the cases where the **kernel platform**
/// enters the shim in response to events that occur during platform execution
/// (e.g., a user-space page fault triggered by a syscall handler).
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ContinueOperation {
/// Resume execution of the guest.
ResumeGuest,
/// Exit the current thread.
ExitThread,
/// The shim successfully handled an exception which was triggered by
/// the kernel platform (e.g., a syscall handler's copy_from_user against
/// demand-pageable user memory); Resume the kernel platform's execution.
ResumeKernelPlatform,
/// The shim failed to handle the exception (e.g., invalid memory access).
/// The kernel platform will apply a fixup via
/// [`search_exception_tables`](crate::mm::exception_table::search_exception_tables)
/// if one exists.
ExceptionFixup,
}

/// Information about a hardware exception.
Expand All @@ -109,6 +124,9 @@ pub struct ExceptionInfo {
/// The value of the CR2 register at the time of the exception, if
/// applicable (e.g., for page faults).
pub cr2: usize,
/// Whether the exception occurred in kernel mode (e.g., a demand page
/// fault during a kernel-mode access to a user-space address).
pub kernel_mode: bool,
}

/// An x86 exception type.
Expand Down
2 changes: 2 additions & 0 deletions litebox_common_linux/src/vmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,6 @@ pub enum PhysPointerError {
UnsupportedOperation,
#[error("Unsupported permissions: {0:#x}")]
UnsupportedPermissions(u8),
#[error("Memory copy failed")]
CopyFailed,
}
12 changes: 12 additions & 0 deletions litebox_platform_linux_kernel/src/host/snp/snp_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ pub fn init_thread(
match tls.shim.get().unwrap().init(pt_regs) {
litebox::shim::ContinueOperation::ResumeGuest => {}
litebox::shim::ContinueOperation::ExitThread => exit_thread(),
litebox::shim::ContinueOperation::ResumeKernelPlatform => {
panic!("ResumeKernelPlatform not expected in SNP init")
}
litebox::shim::ContinueOperation::ExceptionFixup => {
panic!("ExceptionFixup not expected in SNP init")
}
}
}

Expand All @@ -238,6 +244,12 @@ pub fn handle_syscall(pt_regs: &mut litebox_common_linux::PtRegs) {
match tls.shim.get().unwrap().syscall(pt_regs) {
litebox::shim::ContinueOperation::ResumeGuest => {}
litebox::shim::ContinueOperation::ExitThread => exit_thread(),
litebox::shim::ContinueOperation::ResumeKernelPlatform => {
panic!("ResumeKernelPlatform not expected in SNP syscall")
}
litebox::shim::ContinueOperation::ExceptionFixup => {
panic!("ExceptionFixup not expected in SNP syscall")
}
}
}

Expand Down
26 changes: 26 additions & 0 deletions litebox_platform_linux_userland/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,7 @@ extern "C-unwind" fn exception_handler(
exception: litebox::shim::Exception(trapno.try_into().unwrap()),
error_code: error.try_into().unwrap(),
cr2,
kernel_mode: false,
};
thread_ctx.call_shim(|shim, ctx| shim.exception(ctx, &info));
}
Expand Down Expand Up @@ -1632,6 +1633,12 @@ impl ThreadContext<'_> {
match op {
ContinueOperation::ResumeGuest => unsafe { switch_to_guest(self.ctx) },
ContinueOperation::ExitThread => {}
ContinueOperation::ResumeKernelPlatform => {
panic!("ResumeKernelPlatform not expected in linux_userland")
}
ContinueOperation::ExceptionFixup => {
panic!("ExceptionFixup not expected in linux_userland")
}
}
}
}
Expand Down Expand Up @@ -2228,6 +2235,25 @@ impl litebox::platform::CrngProvider for LinuxUserland {
/// testing, or use a kernel module to provide this functionality (if needed).
impl<const ALIGN: usize> VmapManager<ALIGN> for LinuxUserland {}

/// Dummy `VmemPageFaultHandler`.
///
/// Page faults are handled transparently by the host Linux kernel.
/// Provided to satisfy trait bounds for `PageManager::handle_page_fault`.
impl litebox::mm::linux::VmemPageFaultHandler for LinuxUserland {
unsafe fn handle_page_fault(
&self,
_fault_addr: usize,
_flags: litebox::mm::linux::VmFlags,
_error_code: u64,
) -> Result<(), litebox::mm::linux::PageFaultError> {
unreachable!("host kernel handles page faults for Linux userland")
}

fn access_error(_error_code: u64, _flags: litebox::mm::linux::VmFlags) -> bool {
unreachable!("host kernel handles page faults for Linux userland")
}
}

#[cfg(test)]
mod tests {
use core::sync::atomic::AtomicU32;
Expand Down
2 changes: 1 addition & 1 deletion litebox_platform_lvbs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object = { version = "0.36.7", default-features = false, features = ["pe"] }
digest = { version = "0.10.7", default-features = false }
aligned-vec = { version = "0.6.4", default-features = false }
raw-cpuid = "11.6.0"
zerocopy = { version = "0.8", default-features = false }
zerocopy = { version = "0.8", default-features = false, features = ["derive"] }

[target.'cfg(target_arch = "x86_64")'.dependencies]
x86_64 = { version = "0.15.2", default-features = false, features = ["instructions"] }
Expand Down
11 changes: 9 additions & 2 deletions litebox_platform_lvbs/src/arch/x86/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,17 @@ impl Default for GdtWrapper {
}

fn setup_gdt_tss() {
let stack_top = with_per_cpu_variables_asm(PerCpuVariablesAsm::get_interrupt_stack_ptr);
let double_fault_stack_top =
with_per_cpu_variables_asm(PerCpuVariablesAsm::get_double_fault_stack_ptr);
let exception_stack_top =
with_per_cpu_variables_asm(PerCpuVariablesAsm::get_exception_stack_ptr);

let mut tss = Box::new(AlignedTss(TaskStateSegment::new()));
tss.0.interrupt_stack_table[0] = VirtAddr::new(stack_top as u64);
// TSS.IST1: dedicated stack for double faults
tss.0.interrupt_stack_table[0] = VirtAddr::new(double_fault_stack_top as u64);
// TSS.RSP0: stack loaded by the CPU on Ring 3 -> Ring 0 transition when the IDT
// entry's IST index is 0. In our setup, all exceptions except for double faults.
tss.0.privilege_stack_table[0] = VirtAddr::new(exception_stack_top as u64);
// `tss_segment()` requires `&'static TaskStateSegment`. Leaking `tss` is fine because
// it will be used until the LVBS kernel resets.
let tss = Box::leak(tss);
Expand Down
95 changes: 66 additions & 29 deletions litebox_platform_lvbs/src/arch/x86/interrupts.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,29 @@
/*
* Interrupt Service Routine (ISR) stubs for x86_64
*
* This file provides assembly stubs for interrupt handlers that:
* 1. Save all general-purpose registers in PtRegs layout
* 2. Call the appropriate Rust handler
* 3. Restore registers and return via iretq
* Each stub checks the saved CS RPL bits to determine whether the exception
* came from user mode (ring 3) or kernel mode (ring 0):
*
* - User-mode exceptions: push the vector number and jump to
* exception_callback (run_thread_arch), which swaps GS, saves the full
* CPU context, and routes to the shim's exception handler.
*
* - Kernel-mode exceptions: standard push_regs/call/pop_regs/iretq flow
* into a per-vector Rust handler.
*
* Stacks (Reference: Intel SDM Vol. 3A, §6.12.1):
*
* Unless an IST entry is configured for the vector (i.e., #DF in our case),
* the CPU selects the stack based on the privilege transition:
*
* - User-mode (CPL change): the CPU loads RSP from TSS.RSP0. We set a
* dedicated per-CPU stack for this (gdt.rs). Since RSP0 is always
* reloaded from the TSS, we do not wipe stale data from old exceptions.
*
* - Kernel-mode (no CPL change): the CPU continues on the current stack.
* The ISR stub pushes registers onto it. Kernel code must ensure enough
* stack space before performing operations that might fault; otherwise
* the fault handler may overwrite live data or trigger a double fault.
*
* The x86_64 interrupt frame pushed by CPU:
* [rsp+40] SS
Expand Down Expand Up @@ -72,15 +91,14 @@
pop rdi
.endm

/*
* ISR stub for interrupts WITHOUT an error code.
* The CPU does not push an error code, so we push a dummy 0.
*/
.macro isr_no_err_code name:req handler:req
/* ISR stub for interrupts WITHOUT an error code. */
.macro isr_no_err_code name:req handler:req vector:req
.global \name
\name:
cld
push 0 /* Push dummy error code */
test qword ptr [rsp + 16], 0x3 /* Check CS RPL bits */
jnz .Luser_\name
push_regs
mov rbp, rsp /* Save stack pointer */
and rsp, -16 /* Align stack to 16 bytes for call */
Expand All @@ -90,16 +108,18 @@
pop_regs
add rsp, 8 /* Skip error code */
iretq
.Luser_\name:
push \vector /* Pass vector number to exception_callback */
jmp exception_callback
.endm

/*
* ISR stub for interrupts WITH an error code.
* The CPU pushes the error code automatically.
*/
.macro isr_with_err_code name:req handler:req
/* ISR stub for interrupts WITH an error code. */
.macro isr_with_err_code name:req handler:req vector:req
.global \name
\name:
cld
test qword ptr [rsp + 16], 0x3 /* Check CS RPL bits */
jnz .Luser_\name
push_regs
mov rbp, rsp /* Save stack pointer */
and rsp, -16 /* Align stack to 16 bytes for call */
Expand All @@ -109,51 +129,68 @@
pop_regs
add rsp, 8 /* Skip error code */
iretq
.Luser_\name:
push \vector /* Pass vector number to exception_callback */
jmp exception_callback
.endm

/* Exception handlers (vectors 0-31) */

/* Vector 0: Divide Error (#DE) - No error code */
isr_no_err_code isr_divide_error divide_error_handler_impl
isr_no_err_code isr_divide_error divide_error_handler_impl 0

/* Vector 1: Debug (#DB) - No error code */
isr_no_err_code isr_debug debug_handler_impl
isr_no_err_code isr_debug debug_handler_impl 1

/* Vector 3: Breakpoint (#BP) - No error code */
isr_no_err_code isr_breakpoint breakpoint_handler_impl
isr_no_err_code isr_breakpoint breakpoint_handler_impl 3

/* Vector 4: Overflow (#OF) - No error code */
isr_no_err_code isr_overflow overflow_handler_impl
isr_no_err_code isr_overflow overflow_handler_impl 4

/* Vector 5: Bound Range Exceeded (#BR) - No error code */
isr_no_err_code isr_bound_range_exceeded bound_range_exceeded_handler_impl
isr_no_err_code isr_bound_range_exceeded bound_range_exceeded_handler_impl 5

/* Vector 6: Invalid Opcode (#UD) - No error code */
isr_no_err_code isr_invalid_opcode invalid_opcode_handler_impl
isr_no_err_code isr_invalid_opcode invalid_opcode_handler_impl 6

/* Vector 7: Device Not Available (#NM) - No error code */
isr_no_err_code isr_device_not_available device_not_available_handler_impl
isr_no_err_code isr_device_not_available device_not_available_handler_impl 7

/* Vector 8: Double Fault (#DF) - Error code (always 0) */
isr_with_err_code isr_double_fault double_fault_handler_impl
isr_with_err_code isr_double_fault double_fault_handler_impl 8

/* Vector 12: Stack-Segment Fault (#SS) - Error code */
isr_with_err_code isr_stack_segment_fault stack_segment_fault_handler_impl
isr_with_err_code isr_stack_segment_fault stack_segment_fault_handler_impl 12

/* Vector 13: General Protection Fault (#GP) - Error code */
isr_with_err_code isr_general_protection_fault general_protection_fault_handler_impl
isr_with_err_code isr_general_protection_fault general_protection_fault_handler_impl 13

/* Vector 14: Page Fault (#PF) - Error code */
isr_with_err_code isr_page_fault page_fault_handler_impl
/* Vector 14: Page Fault (#PF) - Error code
*
* Both kernel-mode and user-mode page faults are routed through the shim's
* exception_handler, which handles demand paging, exception table fixup,
* and panic in order. The ISR stub just pushes the vector number and jumps
* to the appropriate callback.
*/
.global isr_page_fault
isr_page_fault:
cld
test qword ptr [rsp + 16], 0x3 /* Check CS RPL bits */
push 14 /* Pass vector number (push does not affect flags) */
jnz .Luser_isr_page_fault
jmp kernel_exception_callback
.Luser_isr_page_fault:
jmp exception_callback

/* Vector 16: x87 Floating-Point Exception (#MF) - No error code */
isr_no_err_code isr_x87_floating_point x87_floating_point_handler_impl
isr_no_err_code isr_x87_floating_point x87_floating_point_handler_impl 16

/* Vector 17: Alignment Check (#AC) - Error code */
isr_with_err_code isr_alignment_check alignment_check_handler_impl
isr_with_err_code isr_alignment_check alignment_check_handler_impl 17

/* Vector 19: SIMD Floating-Point Exception (#XM) - No error code */
isr_no_err_code isr_simd_floating_point simd_floating_point_handler_impl
isr_no_err_code isr_simd_floating_point simd_floating_point_handler_impl 19

/*
* Hypervisor synthetic interrupt handler (vector 0xf3)
Expand Down
Loading
Loading