diff --git a/.gitignore b/.gitignore index eccd7b4a..0ec4e612 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target/ **/*.rs.bk +.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 34decae3..e4042d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,35 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "acpi" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654f48ab3178632ea535be1765073b990895cb62f70a7e5671975d7150c26d15" +dependencies = [ + "bit_field", + "log", + "rsdp", +] + [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "apic_example" +version = "0.11.7" +dependencies = [ + "acpi", + "bootloader_api", + "lazy_static", + "log", + "spin", + "x86_64 0.15.1", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -224,7 +247,7 @@ dependencies = [ "rsdp", "serde-json-core", "usize_conversions", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -242,7 +265,7 @@ dependencies = [ "spinning_top", "uart_16550", "usize_conversions", - "x86_64", + "x86_64 0.14.10", "xmas-elf", ] @@ -256,7 +279,7 @@ dependencies = [ "log", "serde-json-core", "uefi", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -592,6 +615,12 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.147" @@ -1059,7 +1088,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1068,7 +1097,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1077,7 +1106,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1086,7 +1115,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1095,7 +1124,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1104,7 +1133,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1113,7 +1142,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1122,7 +1151,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1131,7 +1160,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1140,7 +1169,7 @@ version = "0.1.0" dependencies = [ "bootloader_api", "uart_16550", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1171,7 +1200,7 @@ checksum = "b074eb9300ad949edd74c529c0e8d451625af71bb948e6b65fe69f72dc1363d9" dependencies = [ "bitflags 1.3.2", "rustversion", - "x86_64", + "x86_64 0.14.10", ] [[package]] @@ -1382,6 +1411,18 @@ dependencies = [ "volatile", ] +[[package]] +name = "x86_64" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc79523af8abf92fb1a970c3e086c5a343f6bcc1a0eb890f575cbb3b45743df" +dependencies = [ + "bit_field", + "bitflags 2.3.3", + "rustversion", + "volatile", +] + [[package]] name = "xmas-elf" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 4ff329fe..c235d430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "tests/test_kernels/min_stack", "tests/test_kernels/lower_memory_free", "tests/test_kernels/write_usable_memory", + "docs/apic_example", ] exclude = ["examples/basic", "examples/test_framework"] diff --git a/docs/apic_example/Cargo.toml b/docs/apic_example/Cargo.toml new file mode 100644 index 00000000..2dc72802 --- /dev/null +++ b/docs/apic_example/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "apic_example" +edition = "2021" +version.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +bootloader_api = { workspace = true } +x86_64 = "=0.15.1" +lazy_static = "=1.5.0" +spin = "=0.9.8" +acpi = "=4.1.1" +log = "=0.4.19" diff --git a/docs/apic_example/apic.md b/docs/apic_example/apic.md new file mode 100644 index 00000000..46c98997 --- /dev/null +++ b/docs/apic_example/apic.md @@ -0,0 +1,30 @@ +# How to use the APIC + +## src/main.rs + +In the example provided we will be using a dynamic memory mapping. This is for exemplification only and can be changed +with your own implementation of the memory mapper. + +## src/apic.rs + +Here we create an `AcpiHandlerImpl` that implements the `AcpiHandler` trait from the `apic` crate. +We have also added an enum with all the APIC registers from +the [OS Dev Wiki](https://wiki.osdev.org/APIC) +The main functions of the file are: + +- Init the Local APIC +- Init the IO APIC +- Map the APIC + +## src/frame_allocator.rs + +This implements a basic frame allocator based on the [blog post](https://os.phil-opp.com/heap-allocation/) + +## src/gdt.rs + +Implements a basic Global Descriptor Table based on +the [blog post](https://os.phil-opp.com/double-fault-exceptions/#the-global-descriptor-table/) as well as ensures that +all segment registers are written, including `ss` and `ds`. + +## src/idt.rs +Implements a basic Interrupt Descriptor Table based on the [blog post](https://os.phil-opp.com/hardware-interrupts/) \ No newline at end of file diff --git a/docs/apic_example/src/apic.rs b/docs/apic_example/src/apic.rs new file mode 100644 index 00000000..6785ca09 --- /dev/null +++ b/docs/apic_example/src/apic.rs @@ -0,0 +1,264 @@ +use crate::idt::{InterruptIndex, IDT}; +use acpi::{AcpiHandler, AcpiTables, PhysicalMapping}; +use core::ptr::NonNull; +use lazy_static::lazy_static; +use spin::Mutex; +use x86_64::structures::paging::{FrameAllocator, Mapper, PhysFrame, Size4KiB}; +use x86_64::{PhysAddr, VirtAddr}; + +lazy_static! { + pub static ref LAPIC_ADDR: Mutex = Mutex::new(LAPICAddress::new()); // Needs to be initialized +} + +// https://wiki.osdev.org/APIC +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, Copy)] +#[repr(isize)] +#[allow(dead_code)] +pub enum APICOffset { + R0x00 = 0x0, // RESERVED = 0x00 + R0x10 = 0x10, // RESERVED = 0x10 + Ir = 0x20, // ID Register + Vr = 0x30, // Version Register + R0x40 = 0x40, // RESERVED = 0x40 + R0x50 = 0x50, // RESERVED = 0x50 + R0x60 = 0x60, // RESERVED = 0x60 + R0x70 = 0x70, // RESERVED = 0x70 + Tpr = 0x80, // Text Priority Register + Apr = 0x90, // Arbitration Priority Register + Ppr = 0xA0, // Processor Priority Register + Eoi = 0xB0, // End of Interrupt + Rrd = 0xC0, // Remote Read Register + Ldr = 0xD0, // Logical Destination Register + Dfr = 0xE0, // DFR + Svr = 0xF0, // Spurious (Interrupt) Vector Register + Isr1 = 0x100, // In-Service Register 1 + Isr2 = 0x110, // In-Service Register 2 + Isr3 = 0x120, // In-Service Register 3 + Isr4 = 0x130, // In-Service Register 4 + Isr5 = 0x140, // In-Service Register 5 + Isr6 = 0x150, // In-Service Register 6 + Isr7 = 0x160, // In-Service Register 7 + Isr8 = 0x170, // In-Service Register 8 + Tmr1 = 0x180, // Trigger Mode Register 1 + Tmr2 = 0x190, // Trigger Mode Register 2 + Tmr3 = 0x1A0, // Trigger Mode Register 3 + Tmr4 = 0x1B0, // Trigger Mode Register 4 + Tmr5 = 0x1C0, // Trigger Mode Register 5 + Tmr6 = 0x1D0, // Trigger Mode Register 6 + Tmr7 = 0x1E0, // Trigger Mode Register 7 + Tmr8 = 0x1F0, // Trigger Mode Register 8 + Irr1 = 0x200, // Interrupt Request Register 1 + Irr2 = 0x210, // Interrupt Request Register 2 + Irr3 = 0x220, // Interrupt Request Register 3 + Irr4 = 0x230, // Interrupt Request Register 4 + Irr5 = 0x240, // Interrupt Request Register 5 + Irr6 = 0x250, // Interrupt Request Register 6 + Irr7 = 0x260, // Interrupt Request Register 7 + Irr8 = 0x270, // Interrupt Request Register 8 + Esr = 0x280, // Error Status Register + R0x290 = 0x290, // RESERVED = 0x290 + R0x2A0 = 0x2A0, // RESERVED = 0x2A0 + R0x2B0 = 0x2B0, // RESERVED = 0x2B0 + R0x2C0 = 0x2C0, // RESERVED = 0x2C0 + R0x2D0 = 0x2D0, // RESERVED = 0x2D0 + R0x2E0 = 0x2E0, // RESERVED = 0x2E0 + LvtCmci = 0x2F0, // LVT Corrected Machine Check Interrupt (CMCI) Register + Icr1 = 0x300, // Interrupt Command Register 1 + Icr2 = 0x310, // Interrupt Command Register 2 + LvtT = 0x320, // LVT Timer Register + LvtTsr = 0x330, // LVT Thermal Sensor Register + LvtPmcr = 0x340, // LVT Performance Monitoring Counters Register + LvtLint0 = 0x350, // LVT LINT0 Register + LvtLint1 = 0x360, // LVT LINT1 Register + LvtE = 0x370, // LVT Error Register + Ticr = 0x380, // Initial Count Register (for Timer) + Tccr = 0x390, // Current Count Register (for Timer) + R0x3A0 = 0x3A0, // RESERVED = 0x3A0 + R0x3B0 = 0x3B0, // RESERVED = 0x3B0 + R0x3C0 = 0x3C0, // RESERVED = 0x3C0 + R0x3D0 = 0x3D0, // RESERVED = 0x3D0 + Tdcr = 0x3E0, // Divide Configuration Register (for Timer) + R0x3F0 = 0x3F0, // RESERVED = 0x3F0 +} + +pub struct LAPICAddress { + address: *mut u32, +} + +unsafe impl Send for LAPICAddress {} +unsafe impl Sync for LAPICAddress {} + +impl LAPICAddress { + pub fn new() -> Self { + Self { + address: core::ptr::null_mut(), + } + } +} + +pub struct AcpiHandlerImpl { + physical_memory_offset: VirtAddr, +} + +impl AcpiHandlerImpl { + pub fn new(physical_memory_offset: VirtAddr) -> Self { + Self { + physical_memory_offset, + } + } +} + +unsafe impl Send for AcpiHandlerImpl {} +unsafe impl Sync for AcpiHandlerImpl {} + +impl Clone for AcpiHandlerImpl { + fn clone(&self) -> Self { + Self { + physical_memory_offset: self.physical_memory_offset, + } + } +} + +impl AcpiHandler for AcpiHandlerImpl { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> PhysicalMapping { + let phys_addr = PhysAddr::new(physical_address as u64); + let virt_addr = self.physical_memory_offset + phys_addr.as_u64(); + + PhysicalMapping::new( + physical_address, + NonNull::new(virt_addr.as_mut_ptr()).expect("Failed to get virtual address"), + size, + size, + self.clone(), + ) + } + + fn unmap_physical_region(_region: &PhysicalMapping) { + // No unmapping necessary as we didn't create any new mappings + } +} + +pub unsafe fn init( + rsdp: usize, + physical_memory_offset: VirtAddr, + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) { + let handler = AcpiHandlerImpl::new(physical_memory_offset); + let acpi_tables = AcpiTables::from_rsdp(handler, rsdp).expect("Failed to parse ACPI tables"); + let platform_info = acpi_tables + .platform_info() + .expect("Failed to get platform info"); + match platform_info.interrupt_model { + acpi::InterruptModel::Apic(apic) => { + let io_apic_address = apic.io_apics[0].address; + init_io_apic(io_apic_address as usize, mapper, frame_allocator); + + let local_apic_address = apic.local_apic_address; + init_local_apic(local_apic_address as usize, mapper, frame_allocator); + } + _ => { + // Handle other interrupt models if necessary + } + } + + disable_pic(); + + x86_64::instructions::interrupts::enable(); + IDT.load(); +} + +unsafe fn init_local_apic( + local_apic_addr: usize, + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) { + let virtual_address = map_apic(local_apic_addr as u64, mapper, frame_allocator); + + let lapic_pointer = virtual_address.as_mut_ptr::(); + LAPIC_ADDR.lock().address = lapic_pointer; + + init_timer(lapic_pointer); + init_keyboard(lapic_pointer); +} + +unsafe fn init_timer(lapic_pointer: *mut u32) { + let svr = lapic_pointer.offset(APICOffset::Svr as isize / 4); + svr.write_volatile(svr.read_volatile() | 0x100); // Set bit 8 + + let lvt_lint1 = lapic_pointer.offset(APICOffset::LvtT as isize / 4); + lvt_lint1.write_volatile(0x20 | (1 << 17)); // Vector 0x20, periodic mode + + let tdcr = lapic_pointer.offset(APICOffset::Tdcr as isize / 4); + tdcr.write_volatile(0x3); // Divide by 16 mode + + let ticr = lapic_pointer.offset(APICOffset::Ticr as isize / 4); + ticr.write_volatile(0x100000); // An arbitrary value for the initial value of the timer +} + +unsafe fn init_keyboard(lapic_pointer: *mut u32) { + let keyboard_register = lapic_pointer.offset(APICOffset::LvtLint1 as isize / 4); + keyboard_register.write_volatile(InterruptIndex::Keyboard as u8 as u32); +} + +unsafe fn init_io_apic( + ioapic_address: usize, + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) { + let virt_addr = map_apic(ioapic_address as u64, mapper, frame_allocator); + + let ioapic_pointer = virt_addr.as_mut_ptr::(); + + ioapic_pointer.offset(0).write_volatile(0x12); + ioapic_pointer + .offset(4) + .write_volatile(InterruptIndex::Keyboard as u8 as u32); +} + +fn map_apic( + physical_address: u64, + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) -> VirtAddr { + use x86_64::structures::paging::Page; + use x86_64::structures::paging::PageTableFlags as Flags; + + let physical_address = PhysAddr::new(physical_address); + let page = Page::containing_address(VirtAddr::new(physical_address.as_u64())); + let frame = PhysFrame::containing_address(physical_address); + + let flags = Flags::PRESENT | Flags::WRITABLE | Flags::NO_CACHE; + + unsafe { + mapper + .map_to(page, frame, flags, frame_allocator) + .expect("APIC mapping failed") + .flush(); + } + + page.start_address() +} + +fn disable_pic() { + // Disable any unneeded PIC features, such as timer or keyboard to prevent it from firing interrupts + use x86_64::instructions::port::Port; + + unsafe { + Port::::new(0xA1).write(0xFF); // PIC2 (Slave PIC) + } +} + +pub fn end_interrupt() { + unsafe { + let lapic_ptr = LAPIC_ADDR.lock().address; + lapic_ptr + .offset(APICOffset::Eoi as isize / 4) + .write_volatile(0); + } +} diff --git a/docs/apic_example/src/frame_allocator.rs b/docs/apic_example/src/frame_allocator.rs new file mode 100644 index 00000000..2db1d55e --- /dev/null +++ b/docs/apic_example/src/frame_allocator.rs @@ -0,0 +1,51 @@ +use bootloader_api::info::MemoryRegionKind::Usable; +use bootloader_api::info::MemoryRegions; +use x86_64::registers::control::Cr3; +use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}; +use x86_64::{PhysAddr, VirtAddr}; + +pub struct BootInfoFrameAllocator { + memory_map: &'static MemoryRegions, + next: usize, +} + +impl BootInfoFrameAllocator { + pub fn new(memory_map: &'static MemoryRegions) -> Self { + BootInfoFrameAllocator { + memory_map, + next: 0, + } + } + pub fn usable_frames(&self) -> impl Iterator { + let regions = self.memory_map.iter(); + + let usable_regions = regions.filter(|region| region.kind == Usable); + let address_ranges = usable_regions.map(|region| region.start..region.end); + let frame_addresses = address_ranges.flat_map(|region| region.step_by(4096)); + + frame_addresses.map(|address| PhysFrame::containing_address(PhysAddr::new(address))) + } +} + +unsafe impl FrameAllocator for BootInfoFrameAllocator { + fn allocate_frame(&mut self) -> Option> { + let frame = self.usable_frames().nth(self.next); + self.next += 1; + frame + } +} + +pub fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { + let level4_table = active_level4_table(physical_memory_offset); + unsafe { OffsetPageTable::new(level4_table, physical_memory_offset) } +} + +fn active_level4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { + let (level4_table_frame, _) = Cr3::read(); + + let physical_address = level4_table_frame.start_address(); + let virtual_address = physical_memory_offset + physical_address.as_u64(); + let page_table_pointer: *mut PageTable = virtual_address.as_mut_ptr(); + + unsafe { &mut *page_table_pointer } +} diff --git a/docs/apic_example/src/gdt.rs b/docs/apic_example/src/gdt.rs new file mode 100644 index 00000000..fe74b016 --- /dev/null +++ b/docs/apic_example/src/gdt.rs @@ -0,0 +1,60 @@ +use core::ptr::addr_of; +use lazy_static::lazy_static; +use x86_64::instructions::segmentation::{Segment, CS, DS, ES, FS, GS, SS}; +use x86_64::instructions::tables::load_tss; +use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; +use x86_64::structures::tss::TaskStateSegment; +use x86_64::VirtAddr; + +pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; + +lazy_static! { + static ref TSS: TaskStateSegment = { + let mut tss = TaskStateSegment::new(); + tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { + const STACK_SIZE: usize = 4096 * 5; + static STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; + + let stack_start = VirtAddr::from_ptr( addr_of!(STACK) ); + stack_start + STACK_SIZE as u64 // stack_end + }; + tss + }; + + static ref GDT: (GlobalDescriptorTable, Selectors) = { + let mut gdt: GlobalDescriptorTable = GlobalDescriptorTable::new(); + + let code_selector = gdt.append(Descriptor::kernel_code_segment()); + let data_selector = gdt.append(Descriptor::kernel_data_segment()); + let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); + + ( + gdt, + Selectors { + code_selector, + data_selector, + tss_selector, + }, + ) + }; +} + +struct Selectors { + code_selector: SegmentSelector, + data_selector: SegmentSelector, + tss_selector: SegmentSelector, +} + +pub fn init() { + GDT.0.load(); + unsafe { + CS::set_reg(GDT.1.code_selector); + SS::set_reg(GDT.1.data_selector); + DS::set_reg(GDT.1.data_selector); + ES::set_reg(GDT.1.data_selector); + FS::set_reg(GDT.1.data_selector); + GS::set_reg(GDT.1.data_selector); + + load_tss(GDT.1.tss_selector) + } +} diff --git a/docs/apic_example/src/idt.rs b/docs/apic_example/src/idt.rs new file mode 100644 index 00000000..d5c0f5c2 --- /dev/null +++ b/docs/apic_example/src/idt.rs @@ -0,0 +1,77 @@ +use crate::apic; +use crate::gdt::DOUBLE_FAULT_IST_INDEX; +use lazy_static::lazy_static; +use log::info; +use x86_64::instructions::hlt; +use x86_64::registers::control::Cr2; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; + +pub const PIC_1_OFFSET: u8 = 0x20; + +lazy_static! { + pub static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + + idt.breakpoint.set_handler_fn(handle_breakpoint); + idt.page_fault.set_handler_fn(handle_page_fault); + + idt[InterruptIndex::Timer as u8].set_handler_fn(handle_timer); + idt[InterruptIndex::Keyboard as u8].set_handler_fn(handle_keyboard); + + unsafe { + idt.double_fault + .set_handler_fn(handle_double_fault) + .set_stack_index(DOUBLE_FAULT_IST_INDEX); + } + + idt + }; +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, +} + +pub extern "x86-interrupt" fn handle_timer(_stack_frame: InterruptStackFrame) { + // Handle logic + + apic::end_interrupt(); +} + +pub extern "x86-interrupt" fn handle_breakpoint(stack_frame: InterruptStackFrame) { + info!("Breakpoint hit:\n{:#?}", stack_frame); +} + +pub extern "x86-interrupt" fn handle_double_fault( + stack_frame: InterruptStackFrame, + _error_code: u64, +) -> ! { + info!("\nDouble fault:\n{:#?}", stack_frame); + + loop { + hlt() + } +} + +pub extern "x86-interrupt" fn handle_page_fault( + stack_frame: InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + info!("Exception : Page Fault"); + info!("Accessed address : {:?}", Cr2::read()); + info!("ErrorCode : {:?}", error_code); + info!("{:#?}", stack_frame); + + loop { + hlt() + } +} + +pub extern "x86-interrupt" fn handle_keyboard(_stack_frame: InterruptStackFrame) { + // Handle logic + + apic::end_interrupt(); +} diff --git a/docs/apic_example/src/main.rs b/docs/apic_example/src/main.rs new file mode 100644 index 00000000..231d8162 --- /dev/null +++ b/docs/apic_example/src/main.rs @@ -0,0 +1,51 @@ +#![feature(abi_x86_interrupt)] +#![no_std] +#![no_main] + +extern crate alloc; +mod apic; +mod frame_allocator; +mod gdt; +mod idt; + +use crate::frame_allocator::BootInfoFrameAllocator; +use bootloader_api::config::Mapping; +use bootloader_api::{entry_point, BootInfo}; +use x86_64::instructions::hlt; +use x86_64::structures::paging::OffsetPageTable; +use x86_64::VirtAddr; + +pub const CONFIG: bootloader_api::BootloaderConfig = { + let mut config = bootloader_api::BootloaderConfig::new_default(); + config.mappings.physical_memory = Some(Mapping::Dynamic); + config +}; + +entry_point!(kernel_main, config = &CONFIG); + +pub fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + let physical_memory_offset = VirtAddr::new( + boot_info + .physical_memory_offset + .take() + .expect("Failed to find physical memory offset"), + ); + let mut mapper: OffsetPageTable<'static> = frame_allocator::init(physical_memory_offset); + let mut frame_allocator = BootInfoFrameAllocator::new(&boot_info.memory_regions); + + let rsdp: Option = boot_info.rsdp_addr.take(); + + gdt::init(); + unsafe { + apic::init( + rsdp.expect("Failed to get RSDP address") as usize, + physical_memory_offset, + &mut mapper, + &mut frame_allocator, + ); + } + + loop { + hlt() + } +} diff --git a/docs/migration/v0.9.md b/docs/migration/v0.9.md index 2770e432..4575738c 100644 --- a/docs/migration/v0.9.md +++ b/docs/migration/v0.9.md @@ -34,10 +34,7 @@ This guide summarizes the steps for migrating from `bootloader v0.9.X` to `bootl ``` See the [`BootloaderConfig`](https://docs.rs/bootloader_api/0.11/bootloader_api/config/struct.BootloaderConfig.html) struct for all configuration options. - The `v0.11` version of the bootloader sets up a pixel-based framebuffer instead of using the VGA text mode. This means that you have to rewrite your screen output code. See the [`common/src/framebuffer.rs`](../../common/src/framebuffer.rs) module for an example implementation based on the [`noto-sans-mono-bitmap`](https://docs.rs/noto-sans-mono-bitmap/latest/noto_sans_mono_bitmap/index.html) and [`log`](https://docs.rs/log/latest) crates. -- If you want to use UEFI booting, you cannot use the [legacy PIC](https://wiki.osdev.org/8259_PIC) for interrupt handling. Instead, you have to set up the [`APIC`](https://wiki.osdev.org/APIC). Unfortunately, we don't have a guide for this yet, but the basic steps are: - - The `boot_info.rsdp_addr` field will tell you the physical address of the [`RSDP`](https://wiki.osdev.org/RSDP) structure, which is part of the [`ACPI`](https://en.wikipedia.org/wiki/ACPI) standard. Note that `ACPI` (_Advanced Configuration and Power Interface_) and `APIC` (_Advanced Programmable Interrupt Controller_) are completely different things that just have confusingly similar acronyms. - - Use the [`acpi`](https://docs.rs/acpi/4.1.1/acpi/index.html) crate and its [`AcpiTables::from_rsdp`](https://docs.rs/acpi/4.1.1/acpi/struct.AcpiTables.html#method.from_rsdp) function to load the ACPI tables. Then use the [`platform_info`](https://docs.rs/acpi/4.1.1/acpi/struct.AcpiTables.html#method.platform_info) method and read the [`interrupt_model`](https://docs.rs/acpi/4.1.1/acpi/platform/struct.PlatformInfo.html#structfield.interrupt_model) field of the returned `PlatformInfo`. This field gives you all the necessary information about the system's interrupt controller. - - Parse and set up the local and IO APIC. We're working on a [crate](https://github.com/rust-osdev/apic) for that, but it's still in a very early state. We would love contributions! Alternatively, there are some other crates on crates.io that might work too. +- If you want to use UEFI booting, you cannot use the [legacy PIC](https://wiki.osdev.org/8259_PIC) for interrupt handling. Instead, you have to set up the [`APIC`](https://wiki.osdev.org/APIC). To do so you can use the provided [example](../apic_example/apic.md) as a starting point. - If you reload the GDT at some point, ensure that all segment registers are written, including `ss` and `ds`. The `v0.9` version of the bootloader used to initialize some of them to 0, but this is no longer the case. If you don't do this, [a general protection fault might happen on `iretq`](https://github.com/rust-osdev/bootloader/issues/196). To build your kernel, run **`cargo build --target x86_64-unknown-none`**. Since the `x86_64-unknown-none` target is a Tier-2 target, there is no need for `bootimage`, `cargo-xbuild`, or `xargo` anymore. Instead, you can run `rustup target add x86_64-unknown-none` to download precompiled versions of the `core` and `alloc` crates. There is no need for custom JSON-based target files anymore.