Microkernel based hobby OS

more building

Changed files
+1226 -13
.claude
aethelos-source
heartwood
+6 -1
.claude/settings.local.json
··· 7 7 "Bash(cargo tree:*)", 8 8 "Bash(grep:*)", 9 9 "Bash(1)", 10 - "Bash(cargo clippy:*)" 10 + "Bash(cargo clippy:*)", 11 + "Bash(powershell -Command \"(Get-Content heartwood/src/attunement/idt.rs) -replace ''unsafe extern \"\"C\"\" fn'', ''extern \"\"C\"\" fn'' | Set-Content heartwood/src/attunement/idt.rs\")", 12 + "Bash(powershell -Command \"\n$content = Get-Content idt.rs -Raw\n# Replace asm! with naked_asm! inside naked functions, but keep regular asm! elsewhere\n$content = $content -replace ''(?s)(#\\[unsafe\\(naked\\)\\].*?extern \"\"C\"\" fn.*?\\{.*?unsafe \\{)\\s*asm!'', ''$1 core::arch::naked_asm!''\nSet-Content idt.rs $content\n\")", 13 + "Bash(powershell -Command \"(Get-Content idt_handlers.rs) -replace ''println!'', ''crate::println!'' | Set-Content idt_handlers.rs\")", 14 + "Bash(powershell -Command \"(Get-Content idt_handlers.rs) -replace ''\\!'', ''!'' | Set-Content idt_handlers.rs\")", 15 + "Bash(powershell -Command \"$content = (Get-Content idt_handlers.rs -Raw); $content = $content -replace ''\\\\!'', ''!''; Set-Content idt_handlers.rs $content\")" 11 16 ], 12 17 "deny": [], 13 18 "ask": []
+272
aethelos-source/heartwood/src/attunement/gdt.rs
··· 1 + //! # Global Descriptor Table 2 + //! 3 + //! The GDT defines the privilege boundaries of AethelOS. 4 + //! Unlike traditional systems that enforce rigid hierarchies, 5 + //! our rings exist in symbiosis - Ring 0 (kernel) and Ring 3 (user) 6 + //! dance together in mutual respect. 7 + //! 8 + //! ## Philosophy 9 + //! Privilege is not dominion, but responsibility. 10 + //! The kernel serves userspace as much as userspace relies on the kernel. 11 + 12 + use core::arch::asm; 13 + use core::mem::size_of; 14 + 15 + /// GDT Entry - A segment descriptor 16 + #[derive(Debug, Clone, Copy)] 17 + #[repr(C, packed)] 18 + pub struct GdtEntry { 19 + limit_low: u16, 20 + base_low: u16, 21 + base_middle: u8, 22 + access: u8, 23 + granularity: u8, 24 + base_high: u8, 25 + } 26 + 27 + impl GdtEntry { 28 + /// Create a null descriptor 29 + pub const fn null() -> Self { 30 + GdtEntry { 31 + limit_low: 0, 32 + base_low: 0, 33 + base_middle: 0, 34 + access: 0, 35 + granularity: 0, 36 + base_high: 0, 37 + } 38 + } 39 + 40 + /// Create a kernel code segment (Ring 0, executable, readable) 41 + pub const fn kernel_code() -> Self { 42 + GdtEntry { 43 + limit_low: 0xFFFF, 44 + base_low: 0, 45 + base_middle: 0, 46 + access: 0b10011010, // Present, Ring 0, Code, Execute/Read 47 + granularity: 0b10101111, // 4KB granularity, 64-bit, limit high = 0xF 48 + base_high: 0, 49 + } 50 + } 51 + 52 + /// Create a kernel data segment (Ring 0, writable) 53 + pub const fn kernel_data() -> Self { 54 + GdtEntry { 55 + limit_low: 0xFFFF, 56 + base_low: 0, 57 + base_middle: 0, 58 + access: 0b10010010, // Present, Ring 0, Data, Read/Write 59 + granularity: 0b11001111, // 4KB granularity, 32-bit, limit high = 0xF 60 + base_high: 0, 61 + } 62 + } 63 + 64 + /// Create a user code segment (Ring 3, executable, readable) 65 + pub const fn user_code() -> Self { 66 + GdtEntry { 67 + limit_low: 0xFFFF, 68 + base_low: 0, 69 + base_middle: 0, 70 + access: 0b11111010, // Present, Ring 3, Code, Execute/Read 71 + granularity: 0b10101111, // 4KB granularity, 64-bit, limit high = 0xF 72 + base_high: 0, 73 + } 74 + } 75 + 76 + /// Create a user data segment (Ring 3, writable) 77 + pub const fn user_data() -> Self { 78 + GdtEntry { 79 + limit_low: 0xFFFF, 80 + base_low: 0, 81 + base_middle: 0, 82 + access: 0b11110010, // Present, Ring 3, Data, Read/Write 83 + granularity: 0b11001111, // 4KB granularity, 32-bit, limit high = 0xF 84 + base_high: 0, 85 + } 86 + } 87 + 88 + /// Create a TSS descriptor from a TSS reference 89 + pub fn tss(tss: &'static TaskStateSegment) -> [GdtEntry; 2] { 90 + let ptr = tss as *const _ as u64; 91 + let limit = (size_of::<TaskStateSegment>() - 1) as u64; 92 + 93 + let low = GdtEntry { 94 + limit_low: (limit & 0xFFFF) as u16, 95 + base_low: (ptr & 0xFFFF) as u16, 96 + base_middle: ((ptr >> 16) & 0xFF) as u8, 97 + access: 0b10001001, // Present, Ring 0, TSS (available) 98 + granularity: ((limit >> 16) & 0x0F) as u8, 99 + base_high: ((ptr >> 24) & 0xFF) as u8, 100 + }; 101 + 102 + let high = GdtEntry { 103 + limit_low: ((ptr >> 32) & 0xFFFF) as u16, 104 + base_low: ((ptr >> 48) & 0xFFFF) as u16, 105 + base_middle: 0, 106 + access: 0, 107 + granularity: 0, 108 + base_high: 0, 109 + }; 110 + 111 + [low, high] 112 + } 113 + } 114 + 115 + /// Task State Segment - Holds stack pointers for privilege transitions 116 + #[derive(Debug, Clone, Copy)] 117 + #[repr(C, packed)] 118 + pub struct TaskStateSegment { 119 + reserved_1: u32, 120 + /// Privilege stack pointers (RSP for rings 0-2) 121 + pub rsp: [u64; 3], 122 + reserved_2: u64, 123 + /// Interrupt stack table (IST 1-7) 124 + pub ist: [u64; 7], 125 + reserved_3: u64, 126 + reserved_4: u16, 127 + /// I/O Map Base Address 128 + pub iomap_base: u16, 129 + } 130 + 131 + impl TaskStateSegment { 132 + /// Create a new TSS with default values 133 + pub const fn new() -> Self { 134 + TaskStateSegment { 135 + reserved_1: 0, 136 + rsp: [0; 3], 137 + reserved_2: 0, 138 + ist: [0; 7], 139 + reserved_3: 0, 140 + reserved_4: 0, 141 + iomap_base: size_of::<TaskStateSegment>() as u16, 142 + } 143 + } 144 + 145 + /// Set the kernel stack pointer (used when switching from Ring 3 to Ring 0) 146 + pub fn set_kernel_stack(&mut self, stack_ptr: u64) { 147 + self.rsp[0] = stack_ptr; 148 + } 149 + 150 + /// Set an interrupt stack (IST entry) 151 + pub fn set_interrupt_stack(&mut self, index: usize, stack_ptr: u64) { 152 + if index < 7 { 153 + self.ist[index] = stack_ptr; 154 + } 155 + } 156 + } 157 + 158 + /// GDT Pointer structure for LGDT instruction 159 + #[repr(C, packed)] 160 + struct GdtPointer { 161 + limit: u16, 162 + base: u64, 163 + } 164 + 165 + /// The Global Descriptor Table 166 + #[repr(align(16))] 167 + pub struct GlobalDescriptorTable { 168 + entries: [GdtEntry; 8], // Null, K_Code, K_Data, U_Code, U_Data, TSS_Low, TSS_High, Reserved 169 + len: usize, 170 + } 171 + 172 + impl GlobalDescriptorTable { 173 + /// Create a new GDT with default segments 174 + pub const fn new() -> Self { 175 + GlobalDescriptorTable { 176 + entries: [GdtEntry::null(); 8], 177 + len: 1, // Start with null descriptor 178 + } 179 + } 180 + 181 + /// Initialize the GDT with kernel and user segments 182 + pub fn initialize(&mut self, tss: &'static TaskStateSegment) { 183 + self.len = 1; // Reset to just null descriptor 184 + 185 + // Add kernel code segment (index 1, selector 0x08) 186 + self.entries[self.len] = GdtEntry::kernel_code(); 187 + self.len += 1; 188 + 189 + // Add kernel data segment (index 2, selector 0x10) 190 + self.entries[self.len] = GdtEntry::kernel_data(); 191 + self.len += 1; 192 + 193 + // Add user code segment (index 3, selector 0x18) 194 + self.entries[self.len] = GdtEntry::user_code(); 195 + self.len += 1; 196 + 197 + // Add user data segment (index 4, selector 0x20) 198 + self.entries[self.len] = GdtEntry::user_data(); 199 + self.len += 1; 200 + 201 + // Add TSS (takes 2 entries in 64-bit mode) 202 + let tss_entries = GdtEntry::tss(tss); 203 + self.entries[self.len] = tss_entries[0]; 204 + self.len += 1; 205 + self.entries[self.len] = tss_entries[1]; 206 + self.len += 1; 207 + } 208 + 209 + /// Load the GDT and reload segment registers 210 + pub fn load(&'static self) { 211 + let ptr = GdtPointer { 212 + limit: (self.len * size_of::<GdtEntry>() - 1) as u16, 213 + base: self.entries.as_ptr() as u64, 214 + }; 215 + 216 + unsafe { 217 + // Load GDT 218 + asm!("lgdt [{}]", in(reg) &ptr, options(readonly, nostack, preserves_flags)); 219 + 220 + // Reload segment registers 221 + // Code segment via far return 222 + asm!( 223 + "push 0x08", // Push kernel code selector 224 + "lea {tmp}, [rip + 2f]", 225 + "push {tmp}", 226 + "retfq", // Far return to reload CS 227 + "2:", 228 + tmp = lateout(reg) _, 229 + options(preserves_flags), 230 + ); 231 + 232 + // Data segments 233 + asm!( 234 + "mov ax, 0x10", // Kernel data selector 235 + "mov ds, ax", 236 + "mov es, ax", 237 + "mov fs, ax", 238 + "mov gs, ax", 239 + "mov ss, ax", 240 + out("ax") _, 241 + options(preserves_flags, nostack), 242 + ); 243 + } 244 + } 245 + 246 + /// Load the Task State Segment 247 + pub fn load_tss(&self) { 248 + unsafe { 249 + // TSS selector is at index 5, so selector = 5 * 8 = 0x28 250 + asm!("ltr ax", in("ax") 0x28u16, options(nostack, preserves_flags)); 251 + } 252 + } 253 + } 254 + 255 + /// Segment selectors 256 + #[allow(dead_code)] 257 + pub mod selectors { 258 + /// Kernel code segment selector (Ring 0) 259 + pub const KERNEL_CODE: u16 = 0x08; 260 + 261 + /// Kernel data segment selector (Ring 0) 262 + pub const KERNEL_DATA: u16 = 0x10; 263 + 264 + /// User code segment selector (Ring 3) 265 + pub const USER_CODE: u16 = 0x18 | 3; // RPL = 3 266 + 267 + /// User data segment selector (Ring 3) 268 + pub const USER_DATA: u16 = 0x20 | 3; // RPL = 3 269 + 270 + /// TSS selector 271 + pub const TSS: u16 = 0x28; 272 + }
+174
aethelos-source/heartwood/src/attunement/idt.rs
··· 1 + //! # The Interrupt Conduit 2 + //! 3 + //! Where hardware whispers to the kernel. 4 + //! Interrupts are invitations, not intrusions. 5 + //! Each interrupt is a message from hardware seeking harmony. 6 + 7 + use core::arch::asm; 8 + use core::mem::size_of; 9 + 10 + #[path = "idt_handlers.rs"] 11 + pub(super) mod idt_handlers; 12 + 13 + /// The Interrupt Descriptor Table - 256 entries 14 + pub struct InterruptDescriptorTable { 15 + entries: [IdtEntry; 256], 16 + } 17 + 18 + /// A single entry in the IDT 19 + #[derive(Clone, Copy)] 20 + #[repr(C, packed)] 21 + struct IdtEntry { 22 + offset_low: u16, // Lower 16 bits of handler address 23 + selector: u16, // Code segment selector 24 + ist: u8, // Interrupt Stack Table offset (0 = don't use) 25 + flags: u8, // Type and attributes 26 + offset_mid: u16, // Middle 16 bits of handler address 27 + offset_high: u32, // Upper 32 bits of handler address 28 + reserved: u32, // Must be zero 29 + } 30 + 31 + /// IDT Pointer structure for loading 32 + #[repr(C, packed)] 33 + struct IdtPointer { 34 + limit: u16, 35 + base: u64, 36 + } 37 + 38 + impl IdtEntry { 39 + /// Create a null entry 40 + const fn null() -> Self { 41 + Self { 42 + offset_low: 0, 43 + selector: 0, 44 + ist: 0, 45 + flags: 0, 46 + offset_mid: 0, 47 + offset_high: 0, 48 + reserved: 0, 49 + } 50 + } 51 + 52 + /// Create an interrupt gate entry 53 + fn new(handler: usize, selector: u16) -> Self { 54 + Self { 55 + offset_low: (handler & 0xFFFF) as u16, 56 + selector, 57 + ist: 0, 58 + flags: 0x8E, // Present, DPL=0, Type=Interrupt Gate (0x8E) 59 + offset_mid: ((handler >> 16) & 0xFFFF) as u16, 60 + offset_high: ((handler >> 32) & 0xFFFFFFFF) as u32, 61 + reserved: 0, 62 + } 63 + } 64 + 65 + /// Create a trap gate entry (for exceptions) 66 + fn new_trap(handler: usize, selector: u16) -> Self { 67 + Self { 68 + offset_low: (handler & 0xFFFF) as u16, 69 + selector, 70 + ist: 0, 71 + flags: 0x8F, // Present, DPL=0, Type=Trap Gate (0x8F) 72 + offset_mid: ((handler >> 16) & 0xFFFF) as u16, 73 + offset_high: ((handler >> 32) & 0xFFFFFFFF) as u32, 74 + reserved: 0, 75 + } 76 + } 77 + } 78 + 79 + impl InterruptDescriptorTable { 80 + /// Create a new IDT with all null entries 81 + pub const fn new() -> Self { 82 + Self { 83 + entries: [IdtEntry::null(); 256], 84 + } 85 + } 86 + 87 + /// Attune to hardware interrupts and exceptions 88 + pub fn initialize(&mut self) { 89 + use idt_handlers::*; 90 + 91 + // CPU Exceptions (0-31) - These are disharmony alerts 92 + self.set_trap_handler(0, divide_by_zero_handler); 93 + self.set_trap_handler(1, debug_handler); 94 + self.set_trap_handler(2, nmi_handler); 95 + self.set_trap_handler(3, breakpoint_handler); 96 + self.set_trap_handler(4, overflow_handler); 97 + self.set_trap_handler(5, bound_range_handler); 98 + self.set_trap_handler(6, invalid_opcode_handler); 99 + self.set_trap_handler(7, device_not_available_handler); 100 + self.set_trap_handler(8, double_fault_handler); 101 + // 9 is reserved (legacy) 102 + self.set_trap_handler(10, invalid_tss_handler); 103 + self.set_trap_handler(11, segment_not_present_handler); 104 + self.set_trap_handler(12, stack_segment_fault_handler); 105 + self.set_trap_handler(13, general_protection_fault_handler); 106 + self.set_trap_handler(14, page_fault_handler); 107 + // 15 is reserved 108 + self.set_trap_handler(16, x87_floating_point_handler); 109 + self.set_trap_handler(17, alignment_check_handler); 110 + self.set_trap_handler(18, machine_check_handler); 111 + self.set_trap_handler(19, simd_floating_point_handler); 112 + self.set_trap_handler(20, virtualization_handler); 113 + // 21-29 are reserved 114 + self.set_trap_handler(30, security_exception_handler); 115 + // 31 is reserved 116 + 117 + // Hardware Interrupts (32-47) - IRQs remapped from PIC 118 + // These are invitations from hardware 119 + self.set_interrupt_handler(32, timer_handler); // IRQ 0 - The Pulse of Time 120 + self.set_interrupt_handler(33, keyboard_handler); // IRQ 1 - The Voice of Intent 121 + self.set_interrupt_handler(34, cascade_handler); // IRQ 2 - PIC cascade 122 + self.set_interrupt_handler(35, com2_handler); // IRQ 3 - Serial port 2 123 + self.set_interrupt_handler(36, com1_handler); // IRQ 4 - Serial port 1 124 + self.set_interrupt_handler(37, lpt2_handler); // IRQ 5 - Parallel port 2 125 + self.set_interrupt_handler(38, floppy_handler); // IRQ 6 - Floppy disk 126 + self.set_interrupt_handler(39, lpt1_handler); // IRQ 7 - Parallel port 1 127 + self.set_interrupt_handler(40, rtc_handler); // IRQ 8 - Real-time clock 128 + self.set_interrupt_handler(41, acpi_handler); // IRQ 9 - ACPI 129 + self.set_interrupt_handler(42, available1_handler); // IRQ 10 - Available 130 + self.set_interrupt_handler(43, available2_handler); // IRQ 11 - Available 131 + self.set_interrupt_handler(44, mouse_handler); // IRQ 12 - PS/2 Mouse 132 + self.set_interrupt_handler(45, fpu_handler); // IRQ 13 - FPU 133 + self.set_interrupt_handler(46, ata1_handler); // IRQ 14 - Primary ATA 134 + self.set_interrupt_handler(47, ata2_handler); // IRQ 15 - Secondary ATA 135 + 136 + // Software interrupts (48-255) 137 + // Used for system calls and inter-process communication 138 + self.set_interrupt_handler(128, syscall_handler); // Int 0x80 - System call 139 + } 140 + 141 + /// Set a trap gate handler (for exceptions) 142 + fn set_trap_handler(&mut self, index: u8, handler: extern "C" fn()) { 143 + let handler_addr = handler as usize; 144 + self.entries[index as usize] = IdtEntry::new_trap(handler_addr, 0x08); // 0x08 = kernel code segment 145 + } 146 + 147 + /// Set an interrupt gate handler (for hardware interrupts) 148 + fn set_interrupt_handler(&mut self, index: u8, handler: extern "C" fn()) { 149 + let handler_addr = handler as usize; 150 + self.entries[index as usize] = IdtEntry::new(handler_addr, 0x08); // 0x08 = kernel code segment 151 + } 152 + 153 + /// Load this IDT into the CPU 154 + pub fn load(&self) { 155 + let ptr = IdtPointer { 156 + limit: (size_of::<Self>() - 1) as u16, 157 + base: self as *const _ as u64, 158 + }; 159 + 160 + unsafe { 161 + asm!( 162 + "lidt [{}]", 163 + in(reg) &ptr, 164 + options(readonly, nostack, preserves_flags) 165 + ); 166 + } 167 + } 168 + } 169 + 170 + impl Default for InterruptDescriptorTable { 171 + fn default() -> Self { 172 + Self::new() 173 + } 174 + }
+327
aethelos-source/heartwood/src/attunement/idt_handlers.rs
··· 1 + //! Interrupt handler implementations 2 + //! 3 + //! These handlers are called when hardware or CPU exceptions occur. 4 + 5 + use core::arch::{asm, naked_asm}; 6 + 7 + // 8 + // Exception Handlers (0-31) 9 + // These are disharmony alerts from the CPU 10 + // 11 + 12 + /// Division by zero - mathematical disharmony 13 + #[unsafe(naked)] 14 + pub extern "C" fn divide_by_zero_handler() { 15 + unsafe { 16 + naked_asm!( 17 + "push rax", 18 + "push rcx", 19 + "push rdx", 20 + "push rsi", 21 + "push rdi", 22 + "push r8", 23 + "push r9", 24 + "push r10", 25 + "push r11", 26 + "call {inner}", 27 + "pop r11", 28 + "pop r10", 29 + "pop r9", 30 + "pop r8", 31 + "pop rdi", 32 + "pop rsi", 33 + "pop rdx", 34 + "pop rcx", 35 + "pop rax", 36 + "iretq", 37 + inner = sym divide_by_zero_inner, 38 + ); 39 + } 40 + } 41 + 42 + extern "C" fn divide_by_zero_inner() { 43 + crate::println!("❖ Disharmony: Division by zero attempted"); 44 + crate::println!("❖ The void cannot be divided."); 45 + } 46 + 47 + /// General Protection Fault - privilege violation 48 + #[unsafe(naked)] 49 + pub extern "C" fn general_protection_fault_handler() { 50 + unsafe { 51 + naked_asm!( 52 + "push rax", 53 + "push rcx", 54 + "push rdx", 55 + "push rsi", 56 + "push rdi", 57 + "push r8", 58 + "push r9", 59 + "push r10", 60 + "push r11", 61 + "call {inner}", 62 + "pop r11", 63 + "pop r10", 64 + "pop r9", 65 + "pop r8", 66 + "pop rdi", 67 + "pop rsi", 68 + "pop rdx", 69 + "pop rcx", 70 + "pop rax", 71 + "add rsp, 8", 72 + "iretq", 73 + inner = sym general_protection_fault_inner, 74 + ); 75 + } 76 + } 77 + 78 + extern "C" fn general_protection_fault_inner() { 79 + crate::println!("❖ Disharmony: General protection fault"); 80 + crate::println!("❖ A boundary has been crossed without permission."); 81 + } 82 + 83 + /// Page Fault - memory seeking 84 + #[unsafe(naked)] 85 + pub extern "C" fn page_fault_handler() { 86 + unsafe { 87 + naked_asm!( 88 + "push rax", 89 + "push rcx", 90 + "push rdx", 91 + "push rsi", 92 + "push rdi", 93 + "push r8", 94 + "push r9", 95 + "push r10", 96 + "push r11", 97 + "call {inner}", 98 + "pop r11", 99 + "pop r10", 100 + "pop r9", 101 + "pop r8", 102 + "pop rdi", 103 + "pop rsi", 104 + "pop rdx", 105 + "pop rcx", 106 + "pop rax", 107 + "add rsp, 8", 108 + "iretq", 109 + inner = sym page_fault_inner, 110 + ); 111 + } 112 + } 113 + 114 + extern "C" fn page_fault_inner() { 115 + let faulting_address: u64; 116 + unsafe { 117 + asm!("mov {}, cr2", out(reg) faulting_address, options(nostack, preserves_flags)); 118 + } 119 + crate::println!("❖ Disharmony: Page fault at address {:#x}", faulting_address); 120 + crate::println!("❖ Memory seeks what has not been granted."); 121 + } 122 + 123 + /// Double Fault - cascade of disharmony 124 + #[unsafe(naked)] 125 + pub extern "C" fn double_fault_handler() { 126 + unsafe { 127 + naked_asm!( 128 + "cli", 129 + "call {inner}", 130 + inner = sym double_fault_inner, 131 + ); 132 + } 133 + } 134 + 135 + extern "C" fn double_fault_inner() -> ! { 136 + crate::println!("❖❖❖ CRITICAL DISHARMONY: Double Fault ❖❖❖"); 137 + crate::println!("❖ The system has lost its balance."); 138 + crate::println!("❖ The Heartwood must rest and be reborn."); 139 + loop { 140 + unsafe { asm!("hlt", options(nostack, nomem)) }; 141 + } 142 + } 143 + 144 + // Stub handlers for other exceptions 145 + macro_rules! stub_exception_handler { 146 + ($name:ident, $message:expr) => { 147 + #[unsafe(naked)] 148 + pub extern "C" fn $name() { 149 + unsafe { 150 + naked_asm!( 151 + "iretq" 152 + ); 153 + } 154 + } 155 + }; 156 + } 157 + 158 + stub_exception_handler!(debug_handler, "Debug exception"); 159 + stub_exception_handler!(nmi_handler, "Non-maskable interrupt"); 160 + stub_exception_handler!(breakpoint_handler, "Breakpoint"); 161 + stub_exception_handler!(overflow_handler, "Overflow"); 162 + stub_exception_handler!(bound_range_handler, "Bound range exceeded"); 163 + stub_exception_handler!(invalid_opcode_handler, "Invalid opcode"); 164 + stub_exception_handler!(device_not_available_handler, "Device not available"); 165 + stub_exception_handler!(invalid_tss_handler, "Invalid TSS"); 166 + stub_exception_handler!(segment_not_present_handler, "Segment not present"); 167 + stub_exception_handler!(stack_segment_fault_handler, "Stack segment fault"); 168 + stub_exception_handler!(x87_floating_point_handler, "x87 FPU error"); 169 + stub_exception_handler!(alignment_check_handler, "Alignment check"); 170 + stub_exception_handler!(machine_check_handler, "Machine check"); 171 + stub_exception_handler!(simd_floating_point_handler, "SIMD floating point"); 172 + stub_exception_handler!(virtualization_handler, "Virtualization exception"); 173 + stub_exception_handler!(security_exception_handler, "Security exception"); 174 + 175 + // 176 + // Hardware Interrupt Handlers (32-47) 177 + // These are invitations from hardware 178 + // 179 + 180 + /// Timer interrupt - The Pulse of Time 181 + #[unsafe(naked)] 182 + pub extern "C" fn timer_handler() { 183 + unsafe { 184 + naked_asm!( 185 + "push rax", 186 + "push rcx", 187 + "push rdx", 188 + "push rsi", 189 + "push rdi", 190 + "push r8", 191 + "push r9", 192 + "push r10", 193 + "push r11", 194 + "call {inner}", 195 + "mov al, 0x20", 196 + "out 0x20, al", 197 + "pop r11", 198 + "pop r10", 199 + "pop r9", 200 + "pop r8", 201 + "pop rdi", 202 + "pop rsi", 203 + "pop rdx", 204 + "pop rcx", 205 + "pop rax", 206 + "iretq", 207 + inner = sym timer_inner, 208 + ); 209 + } 210 + } 211 + 212 + extern "C" fn timer_inner() { 213 + // Will be connected to PIT driver 214 + crate::attunement::timer::on_tick(); 215 + } 216 + 217 + /// Keyboard interrupt - The Voice of Intent 218 + #[unsafe(naked)] 219 + pub extern "C" fn keyboard_handler() { 220 + unsafe { 221 + naked_asm!( 222 + "push rax", 223 + "push rcx", 224 + "push rdx", 225 + "push rsi", 226 + "push rdi", 227 + "push r8", 228 + "push r9", 229 + "push r10", 230 + "push r11", 231 + "call {inner}", 232 + "mov al, 0x20", 233 + "out 0x20, al", 234 + "pop r11", 235 + "pop r10", 236 + "pop r9", 237 + "pop r8", 238 + "pop rdi", 239 + "pop rsi", 240 + "pop rdx", 241 + "pop rcx", 242 + "pop rax", 243 + "iretq", 244 + inner = sym keyboard_inner, 245 + ); 246 + } 247 + } 248 + 249 + extern "C" fn keyboard_inner() { 250 + // Will be connected to keyboard driver 251 + // For now, just read and discard the scancode 252 + unsafe { 253 + let scancode: u8; 254 + asm!("in al, 0x60", out("al") scancode, options(nostack, preserves_flags)); 255 + // TODO: Send to keyboard driver via Nexus 256 + } 257 + } 258 + 259 + // Stub handlers for other hardware interrupts 260 + macro_rules! stub_irq_handler { 261 + ($name:ident, $irq:expr) => { 262 + #[unsafe(naked)] 263 + pub extern "C" fn $name() { 264 + unsafe { 265 + naked_asm!( 266 + "push rax", 267 + "mov al, 0x20", 268 + "out 0x20, al", 269 + concat!(".if ", stringify!($irq), " >= 8"), 270 + "out 0xA0, al", 271 + ".endif", 272 + "pop rax", 273 + "iretq", 274 + ); 275 + } 276 + } 277 + }; 278 + } 279 + 280 + stub_irq_handler!(cascade_handler, 2); 281 + stub_irq_handler!(com2_handler, 3); 282 + stub_irq_handler!(com1_handler, 4); 283 + stub_irq_handler!(lpt2_handler, 5); 284 + stub_irq_handler!(floppy_handler, 6); 285 + stub_irq_handler!(lpt1_handler, 7); 286 + stub_irq_handler!(rtc_handler, 8); 287 + stub_irq_handler!(acpi_handler, 9); 288 + stub_irq_handler!(available1_handler, 10); 289 + stub_irq_handler!(available2_handler, 11); 290 + stub_irq_handler!(mouse_handler, 12); 291 + stub_irq_handler!(fpu_handler, 13); 292 + stub_irq_handler!(ata1_handler, 14); 293 + stub_irq_handler!(ata2_handler, 15); 294 + 295 + /// System call handler (Int 0x80) 296 + #[unsafe(naked)] 297 + pub extern "C" fn syscall_handler() { 298 + unsafe { 299 + naked_asm!( 300 + "push rax", 301 + "push rcx", 302 + "push rdx", 303 + "push rsi", 304 + "push rdi", 305 + "push r8", 306 + "push r9", 307 + "push r10", 308 + "push r11", 309 + "call {inner}", 310 + "pop r11", 311 + "pop r10", 312 + "pop r9", 313 + "pop r8", 314 + "pop rdi", 315 + "pop rsi", 316 + "pop rdx", 317 + "pop rcx", 318 + "pop rax", 319 + "iretq", 320 + inner = sym syscall_inner, 321 + ); 322 + } 323 + } 324 + 325 + extern "C" fn syscall_inner() { 326 + crate::println!("❖ System call received (not yet implemented)"); 327 + }
+120 -12
aethelos-source/heartwood/src/attunement/mod.rs
··· 9 9 //! The Attunement Layer speaks the language of the hardware, translating 10 10 //! the Heartwood's intentions into signals the silicon can comprehend. 11 11 12 + pub mod gdt; 13 + pub mod idt; 14 + pub mod pic; 15 + pub mod pit; 16 + 17 + use lazy_static::lazy_static; 18 + use spin::Mutex; 19 + 20 + lazy_static! { 21 + /// The global Task State Segment 22 + static ref TSS: gdt::TaskStateSegment = gdt::TaskStateSegment::new(); 23 + 24 + /// The global Global Descriptor Table 25 + static ref GDT: gdt::GlobalDescriptorTable = { 26 + let mut gdt = gdt::GlobalDescriptorTable::new(); 27 + gdt.initialize(&TSS); 28 + gdt 29 + }; 30 + 31 + /// The global Interrupt Descriptor Table 32 + static ref IDT: Mutex<idt::InterruptDescriptorTable> = { 33 + let mut idt = idt::InterruptDescriptorTable::new(); 34 + idt.initialize(); 35 + Mutex::new(idt) 36 + }; 37 + 38 + /// The global PIC configuration 39 + static ref PIC: Mutex<pic::Pic> = Mutex::new(pic::Pic::new(32, 40)); 40 + 41 + /// The global PIT configuration 42 + static ref PIT: pit::Pit = pit::Pit::new(); 43 + } 44 + 12 45 /// Initialize the Attunement Layer 13 46 pub fn init() { 47 + crate::println!(" ◈ Attuning to hardware..."); 48 + 14 49 // Initialize CPU features 15 50 cpu::init(); 16 51 52 + // Load the Global Descriptor Table 53 + crate::println!(" ∴ Loading privilege rings (GDT)..."); 54 + GDT.load(); 55 + GDT.load_tss(); 56 + 57 + // Initialize Programmable Interrupt Controller (PIC) 58 + // Remap IRQs 0-15 to interrupts 32-47 59 + crate::println!(" ∴ Remapping interrupt controller (PIC)..."); 60 + unsafe { 61 + PIC.lock().initialize(); 62 + PIC.lock().disable_all(); // Start with all IRQs masked 63 + } 64 + 65 + // Load the Interrupt Descriptor Table 66 + crate::println!(" ∴ Preparing interrupt handlers (IDT)..."); 67 + IDT.lock().load(); 68 + 17 69 // Initialize interrupt handling 18 70 interrupts::init(); 19 71 20 - // Initialize system timer 72 + // Initialize system timer (PIT) 73 + crate::println!(" ∴ Starting system heartbeat (PIT @ {} Hz)...", PIT.frequency()); 21 74 timer::init(); 75 + 76 + crate::println!(" ◈ Hardware attunement complete"); 22 77 } 23 78 24 79 /// CPU information and features ··· 49 104 50 105 /// Interrupt handling 51 106 pub mod interrupts { 107 + use core::arch::asm; 108 + 52 109 /// Initialize interrupt handling 53 110 pub fn init() { 54 - // In a real implementation: 55 - // - Set up IDT (Interrupt Descriptor Table) 56 - // - Install interrupt handlers 57 - // - Enable interrupts 111 + // IDT is already loaded by main init() 112 + // Enable interrupts 113 + unsafe { 114 + enable(); 115 + } 58 116 } 59 117 60 118 /// Disable interrupts 61 - pub fn disable() { 62 - // x86_64::instructions::interrupts::disable(); 119 + pub unsafe fn disable() { 120 + asm!("cli", options(nostack, nomem)); 63 121 } 64 122 65 123 /// Enable interrupts 66 - pub fn enable() { 67 - // x86_64::instructions::interrupts::enable(); 124 + pub unsafe fn enable() { 125 + asm!("sti", options(nostack, nomem)); 126 + } 127 + 128 + /// Check if interrupts are enabled 129 + pub fn are_enabled() -> bool { 130 + let flags: u64; 131 + unsafe { 132 + asm!("pushf; pop {}", out(reg) flags, options(nomem, preserves_flags)); 133 + } 134 + (flags & 0x200) != 0 // IF flag is bit 9 68 135 } 69 136 } 70 137 ··· 75 142 76 143 lazy_static! { 77 144 static ref TICKS: Mutex<u64> = Mutex::new(0); 145 + static ref UPTIME_MS: Mutex<u64> = Mutex::new(0); 78 146 } 79 147 80 148 /// Initialize the system timer 81 149 pub fn init() { 82 - // In a real implementation: 83 - // - Configure PIT or APIC timer 84 - // - Set up timer interrupt handler 150 + // Enable timer IRQ (IRQ 0) 151 + unsafe { 152 + super::PIC.lock().enable_irq(0); 153 + } 154 + 155 + // Initialize the PIT to generate interrupts at 100 Hz 156 + unsafe { 157 + super::PIT.initialize(); 158 + } 85 159 } 86 160 87 161 /// Get the number of timer ticks since boot ··· 89 163 *TICKS.lock() 90 164 } 91 165 166 + /// Get uptime in milliseconds 167 + pub fn uptime_ms() -> u64 { 168 + *UPTIME_MS.lock() 169 + } 170 + 171 + /// Get uptime in seconds 172 + pub fn uptime_seconds() -> u64 { 173 + uptime_ms() / 1000 174 + } 175 + 92 176 /// Timer interrupt handler (called by interrupt) 93 177 pub fn on_tick() { 94 178 let mut ticks = TICKS.lock(); 95 179 *ticks += 1; 180 + 181 + // Update uptime (100 Hz = 10ms per tick) 182 + let mut uptime = UPTIME_MS.lock(); 183 + *uptime += super::pit::ms_per_tick(&super::PIT) as u64; 184 + } 185 + 186 + /// Sleep for approximately the given number of milliseconds 187 + /// 188 + /// # Note 189 + /// This is a busy-wait implementation. In a real system, this would 190 + /// yield to the scheduler. 191 + pub fn sleep_ms(ms: u64) { 192 + let start = uptime_ms(); 193 + while uptime_ms() - start < ms { 194 + core::hint::spin_loop(); 195 + } 196 + } 197 + 198 + /// Sleep for approximately the given number of ticks 199 + pub fn sleep_ticks(ticks_to_wait: u64) { 200 + let start = ticks(); 201 + while ticks() - start < ticks_to_wait { 202 + core::hint::spin_loop(); 203 + } 96 204 } 97 205 }
+144
aethelos-source/heartwood/src/attunement/pic.rs
··· 1 + //! # Programmable Interrupt Controller (PIC) 2 + //! 3 + //! The 8259 PIC manages hardware interrupts. 4 + //! We remap IRQs 0-15 to interrupts 32-47 to avoid conflicts with CPU exceptions. 5 + 6 + use core::arch::asm; 7 + 8 + /// I/O ports for the PICs 9 + const PIC1_COMMAND: u16 = 0x20; 10 + const PIC1_DATA: u16 = 0x21; 11 + const PIC2_COMMAND: u16 = 0xA0; 12 + const PIC2_DATA: u16 = 0xA1; 13 + 14 + /// PIC initialization command words 15 + const ICW1_INIT: u8 = 0x10; 16 + const ICW1_ICW4: u8 = 0x01; 17 + const ICW4_8086: u8 = 0x01; 18 + 19 + /// End of Interrupt command 20 + const EOI: u8 = 0x20; 21 + 22 + /// The dual 8259 PIC configuration 23 + pub struct Pic { 24 + offset1: u8, // Offset for PIC1 (usually 32) 25 + offset2: u8, // Offset for PIC2 (usually 40) 26 + } 27 + 28 + impl Pic { 29 + /// Create a new PIC configuration 30 + pub const fn new(offset1: u8, offset2: u8) -> Self { 31 + Self { offset1, offset2 } 32 + } 33 + 34 + /// Initialize and remap the PICs 35 + /// 36 + /// # Safety 37 + /// 38 + /// This must be called exactly once during system initialization. 39 + /// Incorrect PIC configuration can cause system instability. 40 + pub unsafe fn initialize(&self) { 41 + // Save masks 42 + let mask1 = inb(PIC1_DATA); 43 + let mask2 = inb(PIC2_DATA); 44 + 45 + // Start initialization sequence 46 + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); 47 + io_wait(); 48 + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); 49 + io_wait(); 50 + 51 + // Set vector offsets 52 + outb(PIC1_DATA, self.offset1); 53 + io_wait(); 54 + outb(PIC2_DATA, self.offset2); 55 + io_wait(); 56 + 57 + // Tell PIC1 about PIC2 (cascade) 58 + outb(PIC1_DATA, 4); // PIC2 is at IRQ2 59 + io_wait(); 60 + outb(PIC2_DATA, 2); // Cascade identity 61 + io_wait(); 62 + 63 + // Set 8086 mode 64 + outb(PIC1_DATA, ICW4_8086); 65 + io_wait(); 66 + outb(PIC2_DATA, ICW4_8086); 67 + io_wait(); 68 + 69 + // Restore masks 70 + outb(PIC1_DATA, mask1); 71 + outb(PIC2_DATA, mask2); 72 + } 73 + 74 + /// Disable all interrupts (mask all IRQs) 75 + pub unsafe fn disable_all(&self) { 76 + outb(PIC1_DATA, 0xFF); 77 + outb(PIC2_DATA, 0xFF); 78 + } 79 + 80 + /// Enable specific IRQ 81 + pub unsafe fn enable_irq(&self, irq: u8) { 82 + let port = if irq < 8 { 83 + PIC1_DATA 84 + } else { 85 + PIC2_DATA 86 + }; 87 + 88 + let value = inb(port); 89 + let mask = !(1 << (irq % 8)); 90 + outb(port, value & mask); 91 + } 92 + 93 + /// Disable specific IRQ 94 + pub unsafe fn disable_irq(&self, irq: u8) { 95 + let port = if irq < 8 { 96 + PIC1_DATA 97 + } else { 98 + PIC2_DATA 99 + }; 100 + 101 + let value = inb(port); 102 + let mask = 1 << (irq % 8); 103 + outb(port, value | mask); 104 + } 105 + 106 + /// Send End of Interrupt signal 107 + pub unsafe fn send_eoi(&self, irq: u8) { 108 + if irq >= 8 { 109 + outb(PIC2_COMMAND, EOI); 110 + } 111 + outb(PIC1_COMMAND, EOI); 112 + } 113 + } 114 + 115 + /// Read a byte from an I/O port 116 + #[inline] 117 + unsafe fn inb(port: u16) -> u8 { 118 + let value: u8; 119 + asm!( 120 + "in al, dx", 121 + out("al") value, 122 + in("dx") port, 123 + options(nostack, preserves_flags) 124 + ); 125 + value 126 + } 127 + 128 + /// Write a byte to an I/O port 129 + #[inline] 130 + unsafe fn outb(port: u16, value: u8) { 131 + asm!( 132 + "out dx, al", 133 + in("dx") port, 134 + in("al") value, 135 + options(nostack, preserves_flags) 136 + ); 137 + } 138 + 139 + /// Wait for an I/O operation to complete 140 + /// Uses port 0x80 (POST diagnostic port) for a brief delay 141 + #[inline] 142 + unsafe fn io_wait() { 143 + outb(0x80, 0); 144 + }
+183
aethelos-source/heartwood/src/attunement/pit.rs
··· 1 + //! # Programmable Interval Timer (PIT) 2 + //! 3 + //! The heartbeat of AethelOS - the system's sense of time's passage. 4 + //! The PIT provides rhythmic interrupts that drive scheduling, timeouts, 5 + //! and our awareness of temporal flow. 6 + //! 7 + //! ## Philosophy 8 + //! Time in AethelOS is not a tyrant demanding rigid adherence, 9 + //! but a gentle rhythm that guides cooperation. 10 + //! The PIT ticks not to command, but to remind us that moments pass. 11 + //! 12 + //! ## Technical Details 13 + //! The Intel 8253/8254 PIT has three channels: 14 + //! - Channel 0: System timer (IRQ 0) 15 + //! - Channel 1: DRAM refresh (legacy, unused) 16 + //! - Channel 2: PC speaker 17 + //! 18 + //! We use Channel 0 in mode 3 (square wave generator) for periodic interrupts. 19 + 20 + use core::arch::asm; 21 + 22 + /// PIT I/O ports 23 + const PIT_CHANNEL0: u16 = 0x40; // Channel 0 data port (system timer) 24 + const PIT_CHANNEL1: u16 = 0x41; // Channel 1 data port (unused) 25 + const PIT_CHANNEL2: u16 = 0x42; // Channel 2 data port (PC speaker) 26 + const PIT_COMMAND: u16 = 0x43; // Mode/Command register 27 + 28 + /// PIT base frequency (Hz) 29 + const PIT_BASE_FREQ: u32 = 1193182; 30 + 31 + /// Default timer frequency (100 Hz = 10ms per tick) 32 + /// This provides a good balance between precision and overhead 33 + const DEFAULT_FREQ: u32 = 100; 34 + 35 + /// PIT command byte components 36 + /// Format: [Channel (2 bits)][Access mode (2 bits)][Operating mode (3 bits)][BCD (1 bit)] 37 + const CMD_CHANNEL0: u8 = 0b00_000000; // Select channel 0 38 + const CMD_ACCESS_LOHI: u8 = 0b00_11_0000; // Access mode: lobyte/hibyte 39 + const CMD_MODE3: u8 = 0b00_00_011_0; // Operating mode 3: square wave 40 + const CMD_BINARY: u8 = 0b00_00_000_0; // Binary mode (not BCD) 41 + 42 + /// Helper function to output a byte to an I/O port 43 + #[inline] 44 + unsafe fn outb(port: u16, value: u8) { 45 + asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags)); 46 + } 47 + 48 + /// Helper function to input a byte from an I/O port 49 + #[inline] 50 + unsafe fn inb(port: u16) -> u8 { 51 + let value: u8; 52 + asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags)); 53 + value 54 + } 55 + 56 + /// The Programmable Interval Timer 57 + pub struct Pit { 58 + frequency: u32, 59 + divisor: u16, 60 + ticks_per_second: u32, 61 + } 62 + 63 + impl Pit { 64 + /// Create a new PIT configuration with default frequency 65 + pub const fn new() -> Self { 66 + let divisor = (PIT_BASE_FREQ / DEFAULT_FREQ) as u16; 67 + Pit { 68 + frequency: DEFAULT_FREQ, 69 + divisor, 70 + ticks_per_second: DEFAULT_FREQ, 71 + } 72 + } 73 + 74 + /// Create a PIT with custom frequency 75 + /// 76 + /// # Arguments 77 + /// * `freq` - Desired frequency in Hz (18.2 Hz to 1.19 MHz) 78 + /// 79 + /// # Note 80 + /// The actual frequency may differ slightly due to integer division. 81 + pub fn with_frequency(freq: u32) -> Self { 82 + let freq = freq.clamp(18, PIT_BASE_FREQ); 83 + let divisor = (PIT_BASE_FREQ / freq) as u16; 84 + let actual_freq = PIT_BASE_FREQ / divisor as u32; 85 + 86 + Pit { 87 + frequency: actual_freq, 88 + divisor, 89 + ticks_per_second: actual_freq, 90 + } 91 + } 92 + 93 + /// Initialize the PIT and start generating interrupts 94 + /// 95 + /// # Safety 96 + /// This function writes to I/O ports and should only be called once during boot. 97 + pub unsafe fn initialize(&self) { 98 + // Disable interrupts during PIT programming 99 + asm!("cli", options(nomem, nostack)); 100 + 101 + // Send command byte: Channel 0, lobyte/hibyte, mode 3, binary 102 + let command = CMD_CHANNEL0 | CMD_ACCESS_LOHI | CMD_MODE3 | CMD_BINARY; 103 + outb(PIT_COMMAND, command); 104 + 105 + // Send divisor (low byte, then high byte) 106 + outb(PIT_CHANNEL0, (self.divisor & 0xFF) as u8); 107 + outb(PIT_CHANNEL0, ((self.divisor >> 8) & 0xFF) as u8); 108 + 109 + // Re-enable interrupts 110 + asm!("sti", options(nomem, nostack)); 111 + } 112 + 113 + /// Get the configured frequency in Hz 114 + pub fn frequency(&self) -> u32 { 115 + self.frequency 116 + } 117 + 118 + /// Get the divisor value 119 + pub fn divisor(&self) -> u16 { 120 + self.divisor 121 + } 122 + 123 + /// Get ticks per second (same as frequency) 124 + pub fn ticks_per_second(&self) -> u32 { 125 + self.ticks_per_second 126 + } 127 + 128 + /// Read the current counter value 129 + /// 130 + /// # Safety 131 + /// Reads from I/O ports. The value may be unreliable if called during a count update. 132 + pub unsafe fn read_count(&self) -> u16 { 133 + // Send latch command 134 + outb(PIT_COMMAND, 0b00_00_0000); 135 + 136 + // Read count (low byte, then high byte) 137 + let low = inb(PIT_CHANNEL0) as u16; 138 + let high = inb(PIT_CHANNEL0) as u16; 139 + 140 + (high << 8) | low 141 + } 142 + } 143 + 144 + /// Calculate milliseconds per tick for the current PIT frequency 145 + pub fn ms_per_tick(pit: &Pit) -> u32 { 146 + 1000 / pit.frequency() 147 + } 148 + 149 + /// Calculate microseconds per tick for the current PIT frequency 150 + pub fn us_per_tick(pit: &Pit) -> u32 { 151 + 1_000_000 / pit.frequency() 152 + } 153 + 154 + #[cfg(test)] 155 + mod tests { 156 + use super::*; 157 + 158 + #[test] 159 + fn test_divisor_calculation() { 160 + let pit = Pit::new(); 161 + // For 100 Hz: 1193182 / 100 = 11931.82, should round to 11931 162 + assert_eq!(pit.divisor(), 11931); 163 + } 164 + 165 + #[test] 166 + fn test_frequency() { 167 + let pit = Pit::new(); 168 + assert!(pit.frequency() >= 99 && pit.frequency() <= 101); 169 + } 170 + 171 + #[test] 172 + fn test_custom_frequency() { 173 + let pit = Pit::with_frequency(1000); 174 + assert!(pit.frequency() >= 999 && pit.frequency() <= 1001); 175 + } 176 + 177 + #[test] 178 + fn test_ms_per_tick() { 179 + let pit = Pit::new(); 180 + let ms = ms_per_tick(&pit); 181 + assert!(ms >= 9 && ms <= 11); // Should be ~10ms 182 + } 183 + }