rust-kernel
Rustbelt: a hobby x86_64 kernel written in Rust
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | //! 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 global_asm;
/// `vector` value of a frame built by the syscall entry.
pub const TRAP_SYSCALL: u64 = 0x100;
unsafe extern "C"
/// 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.
!
// 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!;
/// 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.
// 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!; // SIGFPE
trap_stub!; // SIGILL
trap_stub!; // SIGSEGV (CPU pushes an error code)
trap_stub!; // SIGSEGV or demand paging
trap_stub!; // SIGFPE (an unmasked x87 exception)
trap_stub!; // SIGFPE (an unmasked SSE exception)
trap_stub!;
unsafe extern "C"
|