11/**
22 * @file apic.h
3- * @author Craig Edwards (craigedwards@brainbox.cc)
3+ * @author Craig Edwards
44 * @copyright Copyright (c) 2012-2025
55 * @brief Local APIC (Advanced Programmable Interrupt Controller) interface
66 *
77 * This header provides low-level routines and register definitions to interface with
88 * the Local APIC (LAPIC) on x86_64 systems. It allows for reading and writing LAPIC registers,
9- * detecting the current CPU's LAPIC ID, and querying the LAPIC base address.
9+ * detecting the current CPU's LAPIC ID, sending inter-processor interrupts (IPIs),
10+ * and querying the LAPIC base address.
1011 *
11- * This interface is used during system initialisation to configure per-core interrupt handling,
12- * timer setup, and inter-processor signalling on SMP systems.
12+ * The LAPIC is a per-core interrupt controller responsible for:
13+ * - Handling local interrupts (e.g., timer, thermal, performance counters).
14+ * - Providing per-core programmable timer functionality.
15+ * - Supporting Inter-Processor Interrupts (IPIs) for SMP coordination.
16+ * - Forwarding external interrupts via the I/O APIC.
17+ *
18+ * Both xAPIC (MMIO) and x2APIC (MSR-based) modes are supported by this interface.
1319 */
1420
1521#pragma once
1622
17- /** Default virtual memory mapping address for the Local APIC (used by the kernel) */
18- #define APIC_ADDRESS 0x4000
23+ /** @brief Default virtual memory mapping address for the Local APIC MMIO page. */
24+ #define APIC_ADDRESS 0x4000
25+
26+ /** @brief Model Specific Register (MSR) index for LAPIC base address (IA32_APIC_BASE). */
27+ #define APIC_BASE_MSR 0x1B
28+
29+ /** @brief Bitmask in IA32_APIC_BASE MSR to enable the Local APIC. */
30+ #define APIC_BASE_MSR_ENABLE 0x800
31+
32+ /* -------------------------------------------------------------------------- */
33+ /* LAPIC Register Offsets (xAPIC mode, relative to MMIO base) */
34+ /* -------------------------------------------------------------------------- */
35+
36+ /** @brief Local APIC ID Register (read-only). */
37+ #define APIC_ID 0x0020
38+
39+ /** @brief Local APIC Version Register (read-only). */
40+ #define APIC_VERSION 0x0030
41+
42+ /** @brief Task Priority Register (TPR). Controls interrupt priority threshold. */
43+ #define APIC_TPR 0x0080
44+
45+ /** @brief Interrupt Command Register (ICR), low dword (vector + control flags). */
46+ #define APIC_ICR_LOW 0x0300
47+
48+ /** @brief Interrupt Command Register (ICR), high dword (destination LAPIC ID). */
49+ #define APIC_ICR_HIGH 0x0310
50+
51+ /* -------------------------------------------------------------------------- */
52+ /* Delivery Modes for Inter-Processor Interrupts (IPI) */
53+ /* -------------------------------------------------------------------------- */
54+
55+ /** @brief Fixed delivery mode: deliver interrupt using vector field. */
56+ #define APIC_DM_FIXED 0x000
57+
58+ /** @brief Non-Maskable Interrupt (NMI) delivery mode. */
59+ #define APIC_DM_NMI 0x400
1960
20- /** Model Specific Register for the LAPIC base address */
21- #define APIC_BASE_MSR 0x1B
61+ /** @brief INIT delivery mode: reset target CPU to INIT state. */
62+ #define APIC_DM_INIT 0x500
2263
23- /** Bitmask to enable the LAPIC via the APIC_BASE_MSR */
24- #define APIC_BASE_MSR_ENABLE 0x800
64+ /** @brief Startup IPI delivery mode: start target CPU at specified vector. */
65+ #define APIC_DM_STARTUP 0x600
2566
26- /* LAPIC register offsets (relative to the LAPIC base address) */
27- #define APIC_ID 0x0020 /**< LAPIC ID Register (read-only) */
28- #define APIC_VERSION 0x0030 /**< LAPIC Version Register (read-only) */
67+ /* -------------------------------------------------------------------------- */
68+ /* x2APIC Registers (MSR-based access only) */
69+ /* -------------------------------------------------------------------------- */
70+
71+ /** @brief MSR index for the x2APIC Interrupt Command Register (ICR). */
72+ #define IA32_X2APIC_ICR 0x830
73+
74+ /* -------------------------------------------------------------------------- */
75+ /* Miscellaneous constants */
76+ /* -------------------------------------------------------------------------- */
77+
78+ /** @brief Vector number used for custom AP wake-up IPIs. */
79+ #define APIC_WAKE_IPI 240
80+
81+ /** @brief Destination shorthand: no shorthand, use explicit LAPIC ID. */
82+ #define APIC_DEST_NO_SHORTHAND (0 << 18)
83+
84+ /** @brief Destination mode: physical addressing. */
85+ #define APIC_DEST_PHYSICAL (0 << 11)
86+
87+ /** @brief Assert level for IPI (used to trigger delivery). */
88+ #define APIC_LEVEL_ASSERT (1 << 14)
89+
90+ /** @brief Edge-triggered delivery mode for IPI. */
91+ #define APIC_TRIGGER_EDGE (0 << 15)
92+
93+ /* -------------------------------------------------------------------------- */
94+ /* Function prototypes */
95+ /* -------------------------------------------------------------------------- */
2996
3097/**
3198 * @brief Read a Local APIC register.
34101 * - In xAPIC mode, the register is memory‑mapped at the LAPIC base address.
35102 * - In x2APIC mode, registers are accessed via MSRs at 0x800 + (reg >> 4).
36103 *
37- * @param reg Register offset (e.g. APIC_EOI = 0xB0 ).
104+ * @param reg Register offset (e.g. APIC_TPR = 0x80 ).
38105 * @return uint32_t Value read from the register.
39106 */
40107uint32_t apic_read (uint64_t reg );
@@ -46,8 +113,8 @@ uint32_t apic_read(uint64_t reg);
46113 * - In xAPIC mode, the register is memory‑mapped at the LAPIC base address.
47114 * - In x2APIC mode, registers are written via MSRs at 0x800 + (reg >> 4).
48115 *
49- * @param reg Register offset (e.g. APIC_EOI = 0xB0 ).
50- * @param val Value to write.
116+ * @param reg Register offset (e.g. APIC_TPR = 0x80 ).
117+ * @param value Value to write.
51118 */
52119void apic_write (uint64_t reg , uint32_t value );
53120
@@ -118,4 +185,68 @@ uint32_t get_lapic_id_from_cpu_id(uint8_t cpu_id);
118185 */
119186uint8_t get_cpu_id_from_lapic_id (uint32_t lapic_id );
120187
121- void boot_aps ();
188+ /**
189+ * @brief Initialise and bring Application Processors (APs) online via Limine SMP.
190+ *
191+ * This function uses the Limine SMP response to discover all CPUs in the system,
192+ * map their LAPIC IDs to logical CPU IDs, and assign each AP an entry point
193+ * (`kmain_ap`). The Bootstrap Processor (BSP) is detected and skipped, as are
194+ * CPUs with processor IDs >= 255 (reserved for broadcast or outside kernel limits).
195+ *
196+ * Once all eligible APs are assigned their startup entry point, this function
197+ * spins until each AP reports online by incrementing @ref aps_online.
198+ *
199+ * @note This does not directly issue INIT/SIPI IPIs; Limine handles the actual
200+ * startup of APs. The kernel only assigns the entry function and waits.
201+ * @warning Systems with more than 254 CPUs are truncated to 254 usable cores.
202+ *
203+ * @see kmain_ap, aps_online, set_lapic_id_for_cpu_id
204+ */
205+ void boot_aps ();
206+
207+
208+ /**
209+ * @brief Send an Inter-Processor Interrupt (IPI) to a specific LAPIC ID.
210+ *
211+ * Supports both xAPIC (ICR registers) and x2APIC (IA32_X2APIC_ICR MSR).
212+ *
213+ * @param lapic_id Destination LAPIC ID.
214+ * @param vector Interrupt vector to deliver.
215+ */
216+ void apic_send_ipi (uint32_t lapic_id , uint8_t vector );
217+
218+ /**
219+ * @brief Send a wake-up IPI to a target logical CPU.
220+ *
221+ * This function is used to wake an Application Processor (AP) from a halted or
222+ * waiting state. It resolves the kernel-assigned logical CPU ID into the
223+ * hardware LAPIC ID and then sends a fixed-mode IPI using the predefined
224+ * wake-up vector (APIC_WAKE_IPI).
225+ *
226+ * @param logical_cpu_id Logical CPU ID (zero-based, contiguous index assigned
227+ * by the kernel).
228+ *
229+ * @note The Local APIC on the target CPU must already be enabled for the wake-up
230+ * IPI to be accepted. This function assumes that LAPIC initialisation has
231+ * been performed during CPU bring-up.
232+ */
233+ void wake_cpu (uint8_t logical_cpu_id );
234+
235+ /**
236+ * @brief Initialise the Local APIC on an Application Processor (AP).
237+ *
238+ * This function enables the Local APIC by setting the spurious interrupt
239+ * vector register's enable bit, and lowers the Task Priority Register (TPR)
240+ * to zero so that the AP can receive all interrupts.
241+ *
242+ * It is typically invoked during AP startup after IDT installation, allowing
243+ * the processor to handle interrupts and inter-processor IPIs.
244+ *
245+ * @note This does not configure LAPIC timers or IPI handlers; it only ensures
246+ * that the APIC is enabled and accepting interrupts.
247+ *
248+ * @see apic_write
249+ * @see APIC_SVR
250+ * @see APIC_TPR
251+ */
252+ void apic_setup_ap ();
0 commit comments