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 | //! The local APIC: every CPU's own interrupt controller.
//!
//! Each x86 CPU core has a "local APIC" (Advanced Programmable Interrupt
//! Controller). We use three of its features:
//!
//! * **Inter-processor interrupts (IPIs).** One CPU can interrupt another
//! by writing the target's APIC ID and a vector into its ICR register.
//! That's how CPU 0 wakes the others up at boot (INIT + "startup" IPIs,
//! see smp.rs) and how a CPU pokes an idle one when new work arrives.
//! * **The APIC timer.** A per-CPU countdown timer. We program it to fire
//! every ~10 ms on *every* CPU, which drives preemption (time slices).
//! Its speed isn't known in advance, so we measure it against the PIT.
//! * **Its ID**, which tells a CPU which CPU it is (see `smp::cpu_id`).
//!
//! The registers are memory-mapped at physical 0xFEE0_0000 (each CPU sees
//! its *own* APIC at that same address). We reach them through the
//! physical-memory map. (On real hardware that range is uncached thanks to
//! the firmware's MTRR settings; QEMU doesn't care either way.)
//!
//! The old 8259 PIC stays in charge of device interrupts (PIT timer,
//! keyboard) and delivers them to CPU 0 only, through CPU 0's LINT0 pin
//! ("virtual wire mode"). Routing device interrupts to any CPU would need
//! the I/O APIC; we don't need that yet.
use crate;
use cratememory;
use ;
use PhysAddr;
// Register offsets (bytes from the APIC base).
const ID: usize = 0x20;
const TPR: usize = 0x80; // task priority: 0 = accept everything
const EOI: usize = 0xB0;
const SVR: usize = 0xF0; // spurious vector + "APIC enabled" bit 8
const ICR_LOW: usize = 0x300;
const ICR_HIGH: usize = 0x310;
const LVT_TIMER: usize = 0x320;
const LVT_LINT0: usize = 0x350;
const LVT_LINT1: usize = 0x360;
const TIMER_INITIAL: usize = 0x380;
const TIMER_CURRENT: usize = 0x390;
const TIMER_DIVIDE: usize = 0x3E0;
const MASKED: u32 = 1 << 16;
const TIMER_PERIODIC: u32 = 1 << 17;
const DIVIDE_BY_16: u32 = 0b0011;
/// Virtual address of the APIC registers (0 = not set up yet).
static BASE: AtomicU64 = new;
/// Timer count for one 10 ms slice (measured once, on CPU 0).
static TIMER_COUNT: AtomicU32 = new;
/// Is CPU 0's APIC timer running? (Until then the PIT drives preemption.)
static TIMER_ON: AtomicBool = new;
/// Is the APIC usable yet?
/// This CPU's APIC ID (0 before `init_bsp`).
/// "End of interrupt": tell the APIC we handled its interrupt (needed for
/// the APIC timer and IPIs; PIC interrupts get their EOI at the PIC).
/// Is the APIC timer driving preemption?
/// CPU 0: find and enable the APIC, measure its timer, start it.
/// Needs interrupts on (it waits for PIT ticks).
/// Another CPU: enable its APIC and start its timer. Its LINT0 stays
/// masked (the PIC talks to CPU 0 only).
/// Measure how far the APIC timer counts in 2 PIT ticks (~110 ms), and
/// work out the count for 10 ms.
/// Write the interrupt command register: send `command` to APIC `target`,
/// then wait until the APIC has delivered it.
/// Interrupt CPU `target` with `vector` (a normal, "fixed" interrupt).
/// INIT IPI: reset CPU `target` into its "wait for startup IPI" state.
/// Startup IPI: CPU `target` starts in 16-bit real mode at physical
/// address `page * 4096`.
|