From a15d461d480cfff2548141059fd849a1d1889c06 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:13:24 +0530 Subject: [PATCH 01/12] feat(kernel): add x86_64 exception support --- kernel/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 6414a56..7c62fb1 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -8,3 +8,4 @@ license = "MIT OR Apache-2.0" bootloader_api = "0.11.17" uart_16550 = "0.3.2" spin = "0.9" +x86_64 = "0.15.5" From 679473bb91a0516ec4e0cb222e08796c633eb3e5 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:13:29 +0530 Subject: [PATCH 02/12] feat(kernel): enable x86 interrupt ABI --- kernel/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 7d2fb3b..4e98151 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,3 +1,5 @@ +#![feature(abi_x86_interrupt)] + #![no_std] #![no_main] From 88d752912bbb96599e86a084a33dd1a403437224 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:13:33 +0530 Subject: [PATCH 03/12] feat(kernel): add IDT subsystem --- kernel/src/arch/x86_64/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 28700d3..d65a5cd 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,6 +1,7 @@ //! x86_64-specific kernel facilities. pub mod interrupts; +pub mod idt; /// Initialize CPU-specific facilities that are safe to establish at boot. pub fn init() { From c14d5e0e5439de6ace3c5025fa5e3ffdcf8acc4c Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:13:42 +0530 Subject: [PATCH 04/12] feat(kernel): implement breakpoint and invalid-opcode handlers --- kernel/src/arch/x86_64/idt.rs | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 kernel/src/arch/x86_64/idt.rs diff --git a/kernel/src/arch/x86_64/idt.rs b/kernel/src/arch/x86_64/idt.rs new file mode 100644 index 0000000..c56ff5f --- /dev/null +++ b/kernel/src/arch/x86_64/idt.rs @@ -0,0 +1,61 @@ +//! Interrupt Descriptor Table setup and CPU exception handlers. + +use spin::Once; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; + +static IDT: Once = Once::new(); + +/// Install the Rustalis exception handlers and load the IDT. +/// +/// The IDT is initialized exactly once and stored in a static so its address +/// remains stable for as long as the CPU can dispatch interrupts. +pub fn init() { + let idt = IDT.call_once(|| { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.invalid_opcode.set_handler_fn(invalid_opcode_handler); + idt + }); + + // SAFETY: IDT lives in a static Once and is therefore never moved or + // destroyed after initialization. Interrupts remain disabled until the + // table has been loaded. + idt.load(); +} + +/// Handle INT3/#BP without modifying the saved execution state. +extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) { + crate::logger::info("BREAKPOINT exception (#BP)"); + crate::logger::info_u64("Breakpoint RIP", frame.instruction_pointer.as_u64()); + crate::logger::info_u64("Breakpoint CS", frame.code_segment.bits() as u64); + crate::logger::info_u64("Breakpoint RFLAGS", frame.cpu_flags.bits()); + crate::logger::info_u64("Breakpoint RSP", frame.stack_pointer.as_u64()); + crate::logger::info_u64("Breakpoint SS", frame.stack_segment.bits() as u64); +} + +/// Handle an invalid opcode as a controlled kernel failure. +/// +/// Returning would retry the faulting instruction because #UD reports the +/// instruction that caused the exception. Entering the kernel panic path +/// avoids silently looping on the same invalid instruction. +extern "x86-interrupt" fn invalid_opcode_handler(frame: InterruptStackFrame) -> ! { + crate::logger::error("INVALID OPCODE exception (#UD)"); + crate::logger::info_u64("Invalid opcode RIP", frame.instruction_pointer.as_u64()); + crate::logger::info_u64("Invalid opcode CS", frame.code_segment.bits() as u64); + panic!("invalid opcode exception"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn idt_registers_exception_handlers() { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.invalid_opcode.set_handler_fn(invalid_opcode_handler); + + assert_ne!(idt.breakpoint.handler_addr().as_u64(), 0); + assert_ne!(idt.invalid_opcode.handler_addr().as_u64(), 0); + } +} From 1b12545d77c57dcf83b11f6d74598b9372d8637f Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:13:47 +0530 Subject: [PATCH 05/12] feat(kernel): load exception IDT during boot --- kernel/src/arch/x86_64/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index d65a5cd..96a3e16 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -6,4 +6,5 @@ pub mod idt; /// Initialize CPU-specific facilities that are safe to establish at boot. pub fn init() { interrupts::init(); + idt::init(); } From 0429629c78fa803b0a721931de858ad536ff156b Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:02 +0530 Subject: [PATCH 06/12] test: add exception self-test feature --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a198f84..df93081 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,9 @@ resolver = "3" members = ["kernel"] +[features] +exception-self-test = ["kernel/exception-self-test"] + [package] name = "rustalis" version = "0.1.0" From 75a2cb136a8c077710748d09aa7981cdea5f614f Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:04 +0530 Subject: [PATCH 07/12] test(kernel): add exception self-test feature --- kernel/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 7c62fb1..871c5b9 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" +[features] +exception-self-test = [] + [dependencies] bootloader_api = "0.11.17" uart_16550 = "0.3.2" From 4c4682ed72b526df4a6548170b0c1e02c5b5391c Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:06 +0530 Subject: [PATCH 08/12] test(kernel): add breakpoint integration trigger --- kernel/src/arch/x86_64/idt.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/arch/x86_64/idt.rs b/kernel/src/arch/x86_64/idt.rs index c56ff5f..8e9d94d 100644 --- a/kernel/src/arch/x86_64/idt.rs +++ b/kernel/src/arch/x86_64/idt.rs @@ -23,6 +23,12 @@ pub fn init() { idt.load(); } +/// Trigger INT3 for the optional boot-time exception self-test. +#[cfg(feature = "exception-self-test")] +pub fn self_test() { + x86_64::instructions::interrupts::int3(); +} + /// Handle INT3/#BP without modifying the saved execution state. extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) { crate::logger::info("BREAKPOINT exception (#BP)"); From e0a5a83413e00864a1e8b66e1216147a28979899 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:08 +0530 Subject: [PATCH 09/12] test(kernel): run optional breakpoint self-test --- kernel/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 4e98151..7703ec0 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -19,6 +19,12 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { logger::info("Boot information received"); arch::x86_64::init(); logger::info("x86_64 architecture layer initialized"); + #[cfg(feature = "exception-self-test")] + { + logger::info("Running breakpoint exception self-test"); + arch::x86_64::idt::self_test(); + logger::info("Breakpoint exception self-test passed"); + } let summary = memory::summarize(&boot_info.memory_regions); logger::info_u64("Memory regions", summary.region_count as u64); logger::info_u64("Usable memory bytes", summary.usable_bytes); From d127bc5b8d626a896ccda92e0080560c1704d9b6 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:19 +0530 Subject: [PATCH 10/12] docs: document implemented exception handlers --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ec3c6a9..316b57f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The project is deliberately incremental. A subsystem is only marked complete aft - [x] x86_64 architecture boundary - [x] Interrupt subsystem boundary - [ ] GDT / TSS -- [ ] IDT +- [x] IDT (breakpoint and invalid-opcode entries) - [ ] Exception handlers - [ ] Double-fault stack @@ -130,6 +130,12 @@ cargo test --workspace ```bash cargo build + +Optional breakpoint exception smoke test: + +```bash +cargo build --features exception-self-test +``` ``` ### Run in QEMU From 48ab862fd6cf5c16b82af8118626ae116348642e Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:21 +0530 Subject: [PATCH 11/12] ci: validate exception self-test build --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 307acca..40c4455 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@nightly with: components: rustfmt, clippy - name: Format @@ -39,4 +39,4 @@ jobs: - name: Install QEMU run: sudo apt-get update && sudo apt-get install -y qemu-system-x86 - name: Build boot image - run: cargo build --verbose + run: cargo build --features exception-self-test --verbose From 1d58fc92d8a18ef132b3cf260717346350368235 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 6 Oct 2026 14:14:24 +0530 Subject: [PATCH 12/12] docs: describe exception handling architecture --- docs/architecture.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/architecture.md b/docs/architecture.md index 45393e7..53394be 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -28,6 +28,15 @@ arch -> interrupt -> scheduler -> process -> syscall -> user space These are architectural targets, not claims that those subsystems are implemented yet. +## Exception handling + +The x86_64 interrupt layer now installs a static IDT with handlers for: + +- Vector 3: breakpoint (`#BP` / `INT3`), which logs the saved interrupt frame and resumes execution. +- Vector 6: invalid opcode (`#UD`), which logs the faulting instruction pointer and enters the kernel panic path. + +The `exception-self-test` Cargo feature triggers `INT3` during boot so the breakpoint path can be exercised without making the normal boot path intentionally fault. + ## Boot invariant Every milestone must leave a bootable kernel or a clearly documented reason why the milestone temporarily cannot boot. No placeholder subsystem should report successful initialization.