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 | //! Kernel stacks with guard pages.
//!
//! Every kernel thread (and every CPU's double-fault handler) needs its own
//! stack. Stack overflows are nasty: the stack grows *down* into whatever
//! lies below it and silently corrupts it. So each stack gets a **guard
//! page** underneath: a page that is deliberately never mapped. Overflowing
//! the stack touches the guard page, the CPU raises a page fault (and,
//! because pushing the fault frame onto the broken stack fails too, a double
//! fault on a separate emergency stack), and interrupts.rs prints a clear
//! "kernel stack overflow" message and kills the thread.
//!
//! All stacks live in one 512 GiB region (L4 slot 200), cut into slots:
//!
//! ```text
//! slot n: [ guard page (unmapped) ][ 16 pages = 64 KiB of stack ]
//! ^ STACKS_START + n * SLOT_SIZE ^ top
//! ```
//!
//! **Reusing a slot safely on several CPUs.** When a stack is freed we unmap
//! its pages and give the frames back. Another CPU may still have the old
//! translations cached in its TLB (translation lookaside buffer). If the
//! slot got a new stack and a thread ran on that other CPU, it could use the
//! stale entries and write into frames that now belong to someone else.
//! Real kernels send a "TLB shootdown" interrupt to every CPU. We use a
//! simpler trick: the scheduler remembers, per thread, which CPUs have
//! flushed that thread's stack from their TLB, and flushes it (`invlpg`,
//! 16 instructions) the first time the thread runs on a CPU. Nothing else
//! ever touches another thread's stack, so that's enough.
use cratememory;
use Vec;
use ;
use Mutex;
use without_interrupts;
use ;
/// Usable size of every kernel stack.
pub const STACK_SIZE: u64 = 64 * 1024;
const PAGE: u64 = 4096;
/// One guard page plus the stack.
const SLOT_SIZE: u64 = PAGE + STACK_SIZE;
/// Start of the stack region (L4 slot 200 = 200 * 512 GiB).
pub const STACKS_START: u64 = << 39;
const STACKS_END: u64 = STACKS_START + ;
/// Next never-used slot number.
static NEXT_SLOT: AtomicU64 = new;
/// Slots whose stacks were freed, ready for reuse (taken with interrupts off).
static FREE_SLOTS: = new;
/// How many stacks exist right now (for statistics and tests).
static LIVE: AtomicU64 = new;
/// An owned, mapped kernel stack. Dropping it unmaps the pages and frees
/// the frames.
/// Is `addr` inside the guard page of some kernel stack?
/// How many kernel stacks are allocated right now.
|