diff --git a/bootloader/shared/src/kctx.rs b/bootloader/shared/src/kctx.rs index ecabf45..37c12e9 100644 --- a/bootloader/shared/src/kctx.rs +++ b/bootloader/shared/src/kctx.rs @@ -1,13 +1,13 @@ +//! The module holds the data structure for passing nessessary information to kernel. + use i386::{ driver::mem::e820::E820MemInfo, - driver::disk::ata::pio::ATADiskInfo + driver::disk::ata::pio::ATADiskInfo, mem::paging::Paging }; use crate::mem::MEMINFO_MAX; -/// The module holds the data structure for passing nessessary information to kernel. - - pub struct KernelContext { pub disk_info: ATADiskInfo, - pub mem_info: E820MemInfo + pub mem_info: E820MemInfo, + pub kernel_paging: &'static dyn Paging } diff --git a/bootloader/stage_2/src/mode_switch.rs b/bootloader/stage_2/src/mode_switch.rs index 615eba4..4493eca 100644 --- a/bootloader/stage_2/src/mode_switch.rs +++ b/bootloader/stage_2/src/mode_switch.rs @@ -38,7 +38,7 @@ pub fn to_protect() -> ! { "mov eax, cr0", "or eax, 1", "mov cr0, eax", - // 5. Load DS, SS, ES, FS and GS with corresponding GDT selectors + // 5. Load DS, SS, ES, FS and GS with corresponding GDT selectors "mov ax, {data}", "mov ds, ax", "mov es, ax", @@ -48,15 +48,15 @@ pub fn to_protect() -> ! { "mov esp, {stack_but}", "mov ax, {null}", "mov fs, ax", - // 6. re-enable hardware interrupts - // TODO: Enable hardware interrupt. - // Currently directly executing sti instruction causes weird behavior of QEMU - // due to the lack of IDT. - // See https://lists.gnu.org/archive/html/qemu-discuss/2015-01/msg00033.html - // "sti" - // 4. Do a far jump to the next instruction to serialize the processer - // (clear the pipeline, I don't know how does this work =-=) - // This step also sets the cs register. + // 6. re-enable hardware interrupts + // TODO: Enable hardware interrupt. + // Currently directly executing sti instruction causes weird behavior of QEMU + // due to the lack of IDT. + // See https://lists.gnu.org/archive/html/qemu-discuss/2015-01/msg00033.html + // "sti" + // 4. Do a far jump to the next instruction to serialize the processer + // (clear the pipeline, I don't know how does this work =-=) + // This step also sets the cs register. "jmp {CS}, offset {target}", data = const GDTSelector::DATA as u16, stack = const GDTSelector::STACK as u16, diff --git a/bootloader/stage_3/src/main.rs b/bootloader/stage_3/src/main.rs index bd516b7..056d095 100644 --- a/bootloader/stage_3/src/main.rs +++ b/bootloader/stage_3/src/main.rs @@ -34,7 +34,7 @@ use shared::{ }; use static_alloc::Bump; -use crate::load_kernel::KERNEL_PTR; +use crate::{load_kernel::KERNEL_PTR, paging::{KERNEL_PAGING, enable_paging}}; #[global_allocator] static ALLOC: Bump<[u8; 1 << 16]> = Bump::uninit(); @@ -64,11 +64,14 @@ fn main() -> Result { .map_err(|x| as Into>::into(x))?; load_kernel(&fs)?; println!("Kernel loaded."); + + enable_paging(); // switch to real mode and poweroff, just for illustrating our mode switching works. // crate::mode_switch::to_real(crate::mode_switch::poweroff as u16); Ok(KernelContext { disk_info: fs.get_disk_info(), - mem_info: unsafe { MEMINFO.clone() } + mem_info: unsafe { MEMINFO.clone() }, + kernel_paging: &KERNEL_PAGING }) } diff --git a/bootloader/stage_3/src/paging.rs b/bootloader/stage_3/src/paging.rs index e69de29..56e5ed8 100644 --- a/bootloader/stage_3/src/paging.rs +++ b/bootloader/stage_3/src/paging.rs @@ -0,0 +1,44 @@ +use i386::mem::paging::{ + pae::{PDEntry, PDTable, PDPTable, PDPTEntry, PAEPaging}, + PATMemoryType, Paging +}; + +/// kernel occupies 3 2MiB pages, this value can be adjusted accordingly +#[allow(dead_code)] +const KERNEL_PAGENUM: usize = 2; +const MB: u64 = 1 << 20; + +/// 4MB kernel PDT page table entry (directly map virtual address to the same physical address) +static KERNEL_PDT: PDTable = PDTable::with_entries([ + PDEntry::new_page( + true, + false, + PATMemoryType::new(false, false, false), + false, + 0 * MB, + false + ), + PDEntry::new_page( + true, + false, + PATMemoryType::new(false, false, false), + false, + 2 * MB, + false + ) +]); + +/// kernel top level page table +static mut KERNEL_PDPT: PDPTable = PDPTable::new(); + +pub static KERNEL_PAGING: PAEPaging = PAEPaging::new(unsafe { &KERNEL_PDPT }); + +pub fn enable_paging() { + unsafe { KERNEL_PDPT.entries[0] = PDPTEntry::new( + PATMemoryType::new(false, false, false), + &KERNEL_PDT as *const PDTable as u64 + )}; + + unsafe { KERNEL_PDPT.entries[1] = PDPTEntry(0xffffffffffffffff); } + KERNEL_PAGING.enable(); +} diff --git a/i386/src/mem.rs b/i386/src/mem.rs index 174fd82..b5fe8a9 100644 --- a/i386/src/mem.rs +++ b/i386/src/mem.rs @@ -1,6 +1,7 @@ +use core::ops::Sub; + /// This module contains code for memory management -#[cfg(feature = "alloc")] pub mod paging; pub mod dt; @@ -9,4 +10,20 @@ pub mod info; /// currently this must be u64, DO NOT change it pub type PhysAddr = u64; -pub type VirtAddr = usize; \ No newline at end of file +pub type VirtAddr = usize; + +/// A memory range +pub struct MemRange { + pub start: T, + pub end: T, + pub len: T, +} + +impl + Copy> MemRange { + pub fn new(start: T, end: T) -> Self { + Self { + start, end, + len: end - start + } + } +} diff --git a/i386/src/mem/info.rs b/i386/src/mem/info.rs index a97132b..b1081c3 100644 --- a/i386/src/mem/info.rs +++ b/i386/src/mem/info.rs @@ -2,31 +2,13 @@ extern crate alloc; -use core::ops::Sub; - use alloc::vec::Vec; use crate::{ driver::mem::e820::{E820MemInfo, E820MemType}, - mem::PhysAddr + mem::{PhysAddr, MemRange} }; -/// A memory range -pub struct MemRange { - pub start: T, - pub end: T, - pub len: T, -} - -impl + Copy> MemRange { - pub fn new(start: T, end: T) -> Self { - Self { - start, end, - len: end - start - } - } -} - pub struct PhysMemInfo { pub segs: Vec> } diff --git a/i386/src/mem/paging.rs b/i386/src/mem/paging.rs index 998915f..c4408a0 100644 --- a/i386/src/mem/paging.rs +++ b/i386/src/mem/paging.rs @@ -1,8 +1,6 @@ //! This module defines data structures and related utilities about paging. //! We only support PAE mode paging now. -extern crate alloc; - pub mod pae; /// supported paging modes @@ -36,6 +34,12 @@ pub struct PATMemoryType { pcd: bool } +impl PATMemoryType { + pub const fn new(pat: bool, pwt: bool, pcd: bool) -> Self { + Self { pat, pwt, pcd } + } +} + /// The unified interface for paging modes. every paging mode should implement this trait. pub trait Paging { /// Enter paging mode diff --git a/i386/src/mem/paging/pae.rs b/i386/src/mem/paging/pae.rs index f41d5e7..12d92ab 100644 --- a/i386/src/mem/paging/pae.rs +++ b/i386/src/mem/paging/pae.rs @@ -5,7 +5,7 @@ use crate::{ utils::bitwise::mask_assign, instrs::{CR0_PG, CR4_PAE}, - mem::{PhysAddr, info::MemRange, VirtAddr} + mem::{PhysAddr, MemRange, VirtAddr} }; use core::arch::asm; use super::{Paging, PATMemoryType}; @@ -26,9 +26,14 @@ const ENTRY_PRESENT: u64 = 1; pub struct PTEntry(u64); impl PTEntry { + pub const fn empty() -> Self { + Self(0) + } + + /// Make sure that page_phys is 4KiB aligned (1 << 12) pub const fn new( writable: bool, - // if false, user mode cannot access this pag + // if false, user mode cannot access this page user_access: bool, // memory cache type mem_ty: PATMemoryType, @@ -37,14 +42,13 @@ impl PTEntry { page_phys: PhysAddr, xd: bool ) -> Self { - let mut entry = ENTRY_PRESENT; + let mut entry = page_phys | ENTRY_PRESENT; entry = mask_assign(entry, writable as u64, 1, 0, 1); entry = mask_assign(entry, user_access as u64, 2, 0, 1); entry = mask_assign(entry, mem_ty.pwt as u64, 3, 0, 1); entry = mask_assign(entry, mem_ty.pcd as u64, 4, 0, 1); entry = mask_assign(entry, mem_ty.pat as u64, 7, 0, 1); entry = mask_assign(entry, global as u64, 8, 0, 1); - entry = mask_assign(entry, page_phys as u64, 12, 0, 51); entry = mask_assign(entry, xd as u64, 63, 0, 1); Self(entry) } @@ -57,17 +61,16 @@ pub struct PTable { pub entries: [PTEntry; PTE_NUM] } -impl Default for PTable { - fn default() -> Self { - Self { entries: [PTEntry(0); PTE_NUM] } - } -} - #[derive(Clone, Copy)] -pub struct PDTEntry(u64); +pub struct PDEntry(u64); -impl PDTEntry { - /// create a PDT entry representing a 2MiB page +impl PDEntry { + pub const fn empty() -> Self { + Self(0) + } + + /// Create a PDT entry representing a 2MiB page. + /// Make sure that page_phys is 2MiB aligned (1 << 21) pub const fn new_page( writable: bool, // if false, user mode cannot access this pag @@ -79,7 +82,7 @@ impl PDTEntry { page_phys: PhysAddr, xd: bool ) -> Self { - let mut entry = ENTRY_PRESENT; + let mut entry = page_phys | ENTRY_PRESENT; entry = mask_assign(entry, writable as u64, 1, 0, 1); entry = mask_assign(entry, user_access as u64, 2, 0, 1); entry = mask_assign(entry, mem_ty.pwt as u64, 3, 0, 1); @@ -87,26 +90,25 @@ impl PDTEntry { entry = mask_assign(entry, 1, 7, 0, 1); entry = mask_assign(entry, global as u64, 8, 0, 1); entry = mask_assign(entry, mem_ty.pat as u64, 12, 0, 1); - entry = mask_assign(entry, page_phys as u64, 21, 0, 42); entry = mask_assign(entry, xd as u64, 63, 0, 1); Self(entry) } - /// create a PDT entry representing a Page Table Entry - pub const fn new_entry( + /// Create a PDT entry representing a Page Table Entry. + /// Make sure this page_phys is properly aligned (1 << 12 aligned) + pub const fn new_table( writable: bool, user_access: bool, mem_ty: PATMemoryType, - page_phys: PhysAddr, + page_table_phys: PhysAddr, xd: bool ) -> Self { - let mut entry = ENTRY_PRESENT; + let mut entry = page_table_phys | ENTRY_PRESENT; entry = mask_assign(entry, writable as u64, 1, 0, 1); entry = mask_assign(entry, user_access as u64, 2, 0, 1); entry = mask_assign(entry, mem_ty.pwt as u64, 3, 0, 1); entry = mask_assign(entry, mem_ty.pcd as u64, 4, 0, 1); entry = mask_assign(entry, 0, 7, 0, 1); - entry = mask_assign(entry, page_phys as u64, 12, 0, 51); entry = mask_assign(entry, xd as u64, 63, 0, 1); Self(entry) } @@ -114,25 +116,25 @@ impl PDTEntry { #[repr(align(4096))] pub struct PDTable { - pub entries: [PDTEntry; PDE_NUM] -} - -impl Default for PDTable { - fn default() -> Self { - Self { entries: [PDTEntry(0); PDE_NUM] } - } + pub entries: [PDEntry; PDE_NUM] } /// This data structure encodes a pointer to a Page Directory #[derive(Clone, Copy)] -pub struct PDPTEntry(u64); +pub struct PDPTEntry(pub u64); impl PDPTEntry { + /// get an empty entry + pub const fn empty() -> Self { + Self(0) + } + + /// Make sure page_dir_phys is properly aligned pub const fn new(mem_ty: PATMemoryType, page_dir_phys: u64) -> Self { - let mut entry = ENTRY_PRESENT; + // It's caller's responsibility to make sure page_dir_phys is properly aligned + let mut entry = page_dir_phys | ENTRY_PRESENT; entry = mask_assign(entry, mem_ty.pwt as u64, 3, 0, 1); entry = mask_assign(entry, mem_ty.pcd as u64, 4, 0, 1); - entry = mask_assign(entry, page_dir_phys, 12, 0, 52); Self(entry) } } @@ -144,16 +146,6 @@ pub struct PDPTable { pub entries: [PDPTEntry; PDPTE_NUM] } -impl Default for PDPTable { - fn default() -> Self { - Self { entries: [PDPTEntry(0); PDPTE_NUM] } - } -} - -pub enum PagingError { - MapError -} - impl PDPTable { pub fn add_map(virt: MemRange, phys: MemRange) -> Result<(), PagingError> { if virt.len != phys.len as VirtAddr { @@ -164,12 +156,42 @@ impl PDPTable { } } +macro_rules! impl_page_table { + ($table:ty, $entry:ty, $default_num:expr) => { + impl $table { + pub const fn new() -> Self { + Self { entries: [<$entry>::empty(); $default_num] } + } + + pub const fn with_entries(entries: [$entry; NUM]) -> Self { + let mut res = Self::new(); + + let mut i = 0; + while i < NUM { + res.entries[i] = entries[i]; + i += 1; + } + + res + } + } + } +} + +impl_page_table!(PTable, PTEntry, PTE_NUM); +impl_page_table!(PDTable, PDEntry, PDE_NUM); +impl_page_table!(PDPTable, PDPTEntry, PDPTE_NUM); + +pub enum PagingError { + MapError +} + pub struct PAEPaging<'a> { page_table: &'a PDPTable } impl<'a> PAEPaging<'a> { - pub fn new(table: &'a PDPTable) -> Self { + pub const fn new(table: &'a PDPTable) -> Self { Self { page_table: table } } } @@ -185,19 +207,26 @@ impl<'a> Paging for PAEPaging<'a> { fn enable(&self) { unsafe { asm!( - // Firstly, we need to make sure that the page table is properly set. - // Then we load the physical address of PDPT into cr3. - "mov cr3, {page_table}", // Finally, we enable PAE paging mode // FIXME: complete these operations with encapsulated control register // operations. // FIXME: add support for SMAP and SMEP - "mov eax, cr0", - "or eax, {PG}", - "mov cr0, eax", + + // enable PAE, note that we must do this step first, or we will go through + // 32-bit paging + // See *Intel Developer Manual Vol. 3A 4-3* "mov eax, cr4", "or eax, {PAE}", "mov cr4, eax", + + // We need to make sure that the page table is properly set. + // Then we load the physical address of PDPT into cr3. + "mov cr3, {page_table}", + + // enable paging + "mov eax, cr0", + "or eax, {PG}", + "mov cr0, eax", page_table = in(reg) self.page_table, PG = const CR0_PG, PAE = const CR4_PAE, diff --git a/kbuild/src/main.rs b/kbuild/src/main.rs index 006fb30..54663bc 100644 --- a/kbuild/src/main.rs +++ b/kbuild/src/main.rs @@ -24,18 +24,21 @@ fn read_to_bytes(path: &PathBuf) -> Vec { fn build(target: &Path) { let target = File::create(target).unwrap(); + let mut wrote_bytes = 0; - bootloader::build() + let mut f = bootloader::build() .iter() .chain(kernel::build().iter()) .map(|x| read_to_bytes(x)) - .fold(target, - |mut f, bin| { - println!("Size is {}", bin.len()); - f.write(&bin).unwrap(); - f - } - ); + .fold(target, |mut f, bin| { + wrote_bytes += bin.len(); + println!("Size is {}", bin.len()); + f.write(&bin).unwrap(); + f + }); + + let padding = vec![0; (1 << 9) - (((1 << 9) - 1) & wrote_bytes)]; + f.write(&padding).unwrap(); } fn run(target: &Path) { diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 77dd85b..0e1984e 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -26,7 +26,7 @@ fn panic(info: &PanicInfo) -> ! { } /// log some hardware information on screen -fn show_info(ctx: &KernelContext) { +fn show_info(ctx: &KernelContext<>) { // show memory information println!("\nMemory Information: \n"); println!(" {:<12}{:<12}{:<12}", "Base", "End", "Type");