//! Trap frames: the user's registers, saved on the kernel stack. //! //! Every way into the kernel from ring 3 -- a `syscall`, a page fault or //! other exception, the timer interrupt -- now saves *all* of the user's //! registers in one layout, `TrapFrame`, at the top of the thread's kernel //! stack, and every way back goes through one exit path (`trap_return`) //! that reloads them and `iretq`s. Linux calls this `struct pt_regs`. //! //! Why bother (stage 13 used `sysretq` and the ready-made //! `extern "x86-interrupt"` handlers)? Because the new process features //! need to *read and change* the user's registers: //! * fork copies them for the child (with rax = 0, "you're the child"), //! * execve replaces them (new rip, new rsp, everything else zero), //! * a signal saves them on the user stack and points rip at the handler; //! rt_sigreturn puts them back, //! * and signals must be checked on *every* return to user mode, also //! after a timer interrupt (that's how Ctrl+C stops a program that //! never makes a syscall, like `spin`). //! //! Layout on the kernel stack (lowest address first; `rsp` points at r15 //! while the Rust handler runs): //! //! r15 r14 r13 r12 r11 r10 r9 r8 rbp rdi rsi rdx rcx rbx rax (we push) //! vector error (we push) //! rip cs rflags rsp ss (the CPU pushes, or we do) //! //! For a syscall `vector` is TRAP_SYSCALL and `error` holds the syscall //! number (Linux's "orig_rax": rax gets the result, but a restarted call //! needs the number again). use core::arch::global_asm; /// `vector` value of a frame built by the syscall entry. pub const TRAP_SYSCALL: u64 = 0x100; #[repr(C)] #[derive(Clone, Copy, Debug, Default)] pub struct TrapFrame { pub r15: u64, pub r14: u64, pub r13: u64, pub r12: u64, pub r11: u64, pub r10: u64, pub r9: u64, pub r8: u64, pub rbp: u64, pub rdi: u64, pub rsi: u64, pub rdx: u64, pub rcx: u64, pub rbx: u64, pub rax: u64, pub vector: u64, pub error: u64, pub rip: u64, pub cs: u64, pub rflags: u64, pub rsp: u64, pub ss: u64, } impl TrapFrame { /// Registers for starting a program: everything zero except the entry /// point, the stack, and RFLAGS = interrupts on (0x200, plus the /// always-one bit 1). pub fn new_user(rip: u64, rsp: u64) -> TrapFrame { let s = crate::gdt::selectors(); TrapFrame { rip, rsp, rflags: 0x202, cs: s.user_code.0 as u64, ss: s.user_data.0 as u64, ..Default::default() } } /// Did this trap come from ring 3? (The low two bits of CS are the /// privilege level the CPU was running at.) pub fn from_user(&self) -> bool { self.cs & 3 == 3 } pub fn is_syscall(&self) -> bool { self.vector == TRAP_SYSCALL } } unsafe extern "C" { /// Pops a TrapFrame (rsp must point at it) and `iretq`s. pub fn trap_return(); } /// Leave the kernel for good: load `frame` into the registers and jump to /// user mode. Used by a new process's first instructions (spawned or /// forked). The frame is copied onto our own stack first; the stack below /// it doesn't matter any more. pub fn return_to_user(frame: TrapFrame) -> ! { unsafe { core::arch::asm!( "mov rsp, {frame}", "jmp {ret}", frame = in(reg) &frame as *const TrapFrame, ret = sym trap_return, options(noreturn) ) } } // The common exit. Interrupts off: from here on we're restoring user // registers and must not be interrupted half-way (the CPU turns them back // on itself, because the saved RFLAGS has IF set). global_asm!( ".global trap_return", "trap_return:", " cli", " pop r15", " pop r14", " pop r13", " pop r12", " pop r11", " pop r10", " pop r9", " pop r8", " pop rbp", " pop rdi", " pop rsi", " pop rdx", " pop rcx", " pop rbx", " pop rax", " add rsp, 16", // skip vector and error " iretq", ); /// An interrupt/exception entry stub. The CPU has pushed ss, rsp, rflags, /// cs, rip (and for some exceptions an error code). We push a fake error /// code if needed, the vector number and all general registers, then call /// `interrupts::trap_handler(&mut TrapFrame)` and leave through /// `trap_return`. 22 slots of 8 bytes: the CPU aligned rsp to 16 before /// its pushes, so it's still 16-aligned for the call. macro_rules! trap_stub { ($name:literal, $vector:literal, $push_error:literal) => { global_asm!( concat!(".global ", $name), concat!($name, ":"), $push_error, concat!(" push ", $vector), " push rax", " push rbx", " push rcx", " push rdx", " push rsi", " push rdi", " push rbp", " push r8", " push r9", " push r10", " push r11", " push r12", " push r13", " push r14", " push r15", " mov rdi, rsp", " cld", // Rust expects the direction flag clear " call {handler}", " jmp {ret}", handler = sym crate::interrupts::trap_handler, ret = sym trap_return, ); }; } // Exceptions that a user program can cause (and get a signal for), plus // the local APIC timer (so a running program can be stopped by a signal). trap_stub!("trap_divide_error", "0", "push 0"); // SIGFPE trap_stub!("trap_invalid_opcode", "6", "push 0"); // SIGILL trap_stub!("trap_general_protection", "13", ""); // SIGSEGV (CPU pushes an error code) trap_stub!("trap_page_fault", "14", ""); // SIGSEGV or demand paging trap_stub!("trap_apic_timer", "0x40", "push 0"); unsafe extern "C" { pub fn trap_divide_error(); pub fn trap_invalid_opcode(); pub fn trap_general_protection(); pub fn trap_page_fault(); pub fn trap_apic_timer(); }