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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | //! The FPU, SSE and AVX registers of user programs (stage 15.1).
//!
//! Besides the 16 general-purpose registers, an x86_64 CPU has a second set
//! for floating point and vector maths: the old x87 FPU stack, the 16 SSE
//! registers xmm0-xmm15 (128 bits each) plus their control register MXCSR,
//! and, on CPUs with AVX, the upper halves that make them 256-bit ymm0-ymm15.
//! Every C compiler uses xmm registers for `double` arithmetic and for fast
//! `memcpy`, so to the kernel they are as much "the program's registers" as
//! rax is: if two threads share a CPU, each must find its own values there
//! when it runs again.
//!
//! The kernel itself is built *soft-float* (see x86_64-rust_kernel.json): it
//! never touches these registers. That makes the job simple: whatever is in
//! them while the kernel runs belongs to the user thread the CPU last ran.
//! So we only
//!
//! * save them into the thread's `FpuState` when the scheduler switches
//! away from a user thread, and load the next user thread's (thread.rs,
//! "eager" switching -- simple, and cheap enough at ~1 KiB);
//! * copy them into a fork's child / a new thread (they start with the
//! parent's values, like Linux);
//! * reset them on execve (a new program starts clean);
//! * put them in signal frames, so a handler may use floats and the
//! interrupted code gets its own values back (signal.rs).
//!
//! The CPU has instructions that save/load all of it at once:
//!
//! * FXSAVE / FXRSTOR (every x86_64 CPU): x87 + SSE, a 512-byte area.
//! * XSAVE / XRSTOR (newer CPUs): a 512-byte legacy part in the same
//! format, a 64-byte header (which parts are saved), then one part per
//! extra feature -- AVX's upper ymm halves are 256 bytes more. Which
//! features are switched on is the OS's choice, in register XCR0.
//!
//! Before user code may use any of this, the OS must say it will save the
//! state (otherwise the CPU raises "invalid opcode"): control register bits
//! CR4.OSFXSR (SSE allowed), CR4.OSXMMEXCPT (SSE maths errors raise #XM, not
//! #UD) and CR4.OSXSAVE (XSAVE + XCR0 allowed), on every CPU.
use asm;
use ;
use ;
/// Room for the biggest state we enable: x87 + SSE + AVX = 832 bytes with
/// XSAVE (we don't switch on AVX-512, which would need ~2.7 KiB).
pub const AREA_SIZE: usize = 1024;
/// One thread's saved FPU/SSE/AVX registers, in the FXSAVE/XSAVE memory
/// format. XSAVE needs 64-byte alignment.
;
/// XSAVE is used (else FXSAVE).
static USE_XSAVE: AtomicBool = new;
/// The features in XCR0 (bit 0 x87, bit 1 SSE, bit 2 AVX).
static XCR0: AtomicU64 = new;
/// Bytes of state: 512 (FXSAVE) or what CPUID says for our XCR0.
static SIZE: AtomicU32 = new;
/// MXCSR bits the CPU supports; loading any other bit faults (#GP).
static MXCSR_MASK: AtomicU32 = new;
/// MXCSR at reset: all six SSE exceptions masked, round to nearest.
pub const MXCSR_DEFAULT: u32 = 0x1f80;
/// x87 control word at reset (FNINIT): all exceptions masked, 64-bit precision.
const FCW_DEFAULT: u16 = 0x037f;
pub const XFEATURE_X87: u64 = 1;
pub const XFEATURE_SSE: u64 = 2;
pub const XFEATURE_AVX: u64 = 4;
/// Switch the FPU/SSE (and AVX) on for this CPU. Every CPU calls it once at
/// boot: CR0/CR4/XCR0 are per CPU.
/// One line for the boot log, e.g. "XSAVE: x87 SSE AVX, 832 bytes per thread".
/// Bytes of saved state (512 with FXSAVE).
/// The state a new program starts with: everything zero, the x87 control
/// word and MXCSR at their reset values. The XSAVE header (bytes 512..576)
/// is all zero, which tells XRSTOR "every extra feature is in its initial
/// state" (zero).
/// Save this CPU's registers into `s`.
/// The registers right now (for fork, clone and signal frames).
/// Load `s` into this CPU's registers. `s` must be valid (see `sanitize`):
/// a bad MXCSR or XSAVE header would fault in the kernel.
/// execve and signal handlers start from clean registers.
/// Make state that came from user memory (a signal frame the program may
/// have edited) safe to load: false if MXCSR has bits the CPU doesn't
/// support (XRSTOR would fault). Fixes the XSAVE header: only features we
/// enabled, standard (not compacted) format, reserved bytes zero.
/// The SSE status flags of the last SIMD exception (#XM), for SIGFPE's
/// si_code: MXCSR bits 0-5 = invalid, denormal, divide by zero, overflow,
/// underflow, precision.
|