diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 0efc34f..669504b 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -1,6 +1,6 @@ [build] target = "i686-unknown-none.json" -rustflags = ["-Clink-arg=--script=link.x"] +rustflags = ["-Clink-arg=--script=link.x", "-Clink-arg=--gc-sections"] [unstable] build-std = ["core", "alloc"] \ No newline at end of file diff --git a/kernel/src/kernel/arch/example_impl/mod.rs b/kernel/src/kernel/arch/example_impl/mod.rs index 27dbf66..2b50065 100644 --- a/kernel/src/kernel/arch/example_impl/mod.rs +++ b/kernel/src/kernel/arch/example_impl/mod.rs @@ -7,6 +7,10 @@ //! actual architecture, of course): //! #![cfg(any(target_arch = "arch"))] +pub const fn get_arch() -> super::Architecture { + super::Architecture::ExampleDummy +} + pub mod interrupts { //! Interrupt-related functions. diff --git a/kernel/src/kernel/arch/mod.rs b/kernel/src/kernel/arch/mod.rs index 5668ec3..8498953 100644 --- a/kernel/src/kernel/arch/mod.rs +++ b/kernel/src/kernel/arch/mod.rs @@ -5,4 +5,15 @@ mod x86; pub mod example_impl; -pub use x86::*; \ No newline at end of file +pub use x86::*; + +/// The enum returned by arch::*::get_arch. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum Architecture { + /// Returned by [example_impl]. If this is returned by arch::*::get_arch, something + /// is incredibly wrong and a panic should occur immediately. + #[default] + ExampleDummy, + /// 32-bit x86. + X86, +} \ No newline at end of file diff --git a/kernel/src/kernel/arch/x86/gdt.rs b/kernel/src/kernel/arch/x86/gdt.rs new file mode 100644 index 0000000..e2f3112 --- /dev/null +++ b/kernel/src/kernel/arch/x86/gdt.rs @@ -0,0 +1,61 @@ +//! GDT initalization. + +use core::alloc::Layout; + +use alloc::vec::Vec; + +/// Writes a series of GDT entries to an allocated section of memory and returns a pointer. +pub unsafe fn write_gdt_entries(entries: Vec) -> Result<*const [u8], crate::Error<'static>> { + let mut mem = unsafe { alloc::alloc::alloc(Layout::from_size_align(8*entries.len(), 1).unwrap()) }; + for ele in &entries { + let ele: &GDTEntry = ele; + unsafe { ele.write_to_addr(mem as *mut ())? } + mem = (mem as usize + 8) as *mut u8; + } + + Ok(core::ptr::from_raw_parts(mem, 8*entries.len())) +} + +/// A GDT entry. +#[derive(Clone, Copy)] +pub struct GDTEntry { + /// The size of the entry. Has to be less than 0xFFFFF. + pub limit: u32, + /// The base address of the entry. + pub base: u32, + /// The access byte of the entry. + pub access: u8, + /// The flags of the entry. + pub flags: u8, +} + +/// An error returned by [GDTEntry::write_to_addr] when the limit is greater than 0xFFFFF. +const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1; + +impl GDTEntry { + const unsafe fn write_to_addr(self, ptr: *mut ()) -> Result<(), crate::Error<'static>> { + if self.limit > 0xFFFFF { + return Err(crate::Error::new("Invalid GDT entry limit(more than 0xFFFFF)", GDT_WRITE_ADDR_INVALID_LIMIT)); + } + let mut serialized = (0u64).to_ne_bytes(); + + serialized[0] = (self.limit & 0xFF) as u8; + serialized[1] = ((self.limit >> 8) & 0xFF) as u8; + serialized[6] = ((self.limit >> 16) & 0x0F) as u8; + + serialized[2] = (self.base & 0xFF) as u8; + serialized[3] = ((self.base >> 8) & 0xFF) as u8; + serialized[4] = ((self.base >> 16) & 0xFF) as u8; + serialized[7] = ((self.base >> 24) & 0xFF) as u8; + + serialized[5] = self.access; + + serialized[6] |= self.flags << 4; + + unsafe { + core::ptr::write(ptr as *mut [u8; 8], serialized); + } + + Ok(()) + } +} \ No newline at end of file diff --git a/kernel/src/kernel/arch/x86/interrupts.rs b/kernel/src/kernel/arch/x86/interrupts.rs index 57d1c77..cbe7f27 100644 --- a/kernel/src/kernel/arch/x86/interrupts.rs +++ b/kernel/src/kernel/arch/x86/interrupts.rs @@ -28,9 +28,21 @@ pub fn disable_interrupts() { } } +/// PoppedInterrupts implements drop and restores the interrupts upon being dropped. +/// This is useful in functions where you need interrupts disabled during it but also +/// want to use functions like [Result::unwrap] or [Option::unwrap]. +#[derive(Clone)] +pub struct PoppedInterrupts(u32); + +impl Drop for PoppedInterrupts { + fn drop(&mut self) { + restore_irq(self.clone()); + } +} + /// Disables interrupts and returns the value of them. #[aphrodite_proc_macros::kernel_item(InterruptsPop)] -pub fn pop_irq() -> u64 { +pub fn pop_irq() -> PoppedInterrupts { let flags: u32; unsafe { asm!( @@ -39,13 +51,13 @@ pub fn pop_irq() -> u64 { "pop {0:e}", out(reg) flags ) } - flags as u64 + PoppedInterrupts(flags) } /// Restores interrupts after a [pop_irq] call. #[aphrodite_proc_macros::kernel_item(InterruptsRestore)] -pub fn restore_irq(flags: u64) { - let flags = flags as u32; +pub fn restore_irq(flags: PoppedInterrupts) { + let flags = flags.0; unsafe { asm!( "push {0:e}", in(reg) flags @@ -87,9 +99,8 @@ fn activate_idt(idt: Idt, alloc: crate::mem::MemoryMapAlloc) { let mem = alloc.allocate(unsafe { Layout::from_size_align_unchecked(8*idt.len, 1) }).unwrap().as_mut_ptr(); for i in 0..idt.len { let vector = idt.vectors[i]; - let func = unsafe { idt.funcs[i].assume_init() } as usize as u32; + let func = unsafe { idt.funcs[i].assume_init() } as usize as u64; let user_callable = idt.user_callable[i]; - let output: u64 = (func & 0b1111111111111111) as u64; } } diff --git a/kernel/src/kernel/arch/x86/memory.rs b/kernel/src/kernel/arch/x86/memory.rs new file mode 100644 index 0000000..b309ee5 --- /dev/null +++ b/kernel/src/kernel/arch/x86/memory.rs @@ -0,0 +1,174 @@ +//! Hardware-level memory sections. Unimplemented for certain hardware, x86 implements with GDT. + +use core::arch::asm; + +use alloc::{vec, vec::Vec}; + +use crate::memsections::*; + +use super::gdt::{write_gdt_entries, GDTEntry}; + +pub struct MemorySections { + sections: Vec, +} + +#[repr(packed)] +struct GDTR { + address: u32, + size: u16, +} + +unsafe impl crate::memsections::MemorySections for MemorySections { + unsafe fn write(self) -> Result<(), crate::Error<'static>> { + let mut entries: Vec = vec![]; + + for section in self.sections { + let mut section: MemorySection = section; + // rust-analyzer doesn't want to cooperate and recognize that section is already MemorySection, so I'm telling it here. + fn make_entry(section: &mut MemorySection, entries: &mut Vec) { + if section.length == 0 { + return; + } + let mut len = section.length as u32; + while len > 0xFFFFF { + len -= 0xFFFFF; + } + let mut access = 0b10000001u8; + match section.owner { + Owner::Kernelspace => { + access |= 0b0000000; + } + Owner::Modulespace => { + access |= 0b0100000; + } + Owner::Userspace => { + access |= 0b1100000; + } + } + if let SectionType::TaskSection { busy } = section.section_type { + access |= 0b00000; + if busy { + access |= 0x9; + } else { + access |= 0xB; + } + } else { + access |= 0b10000; + if let SectionType::CodeSection { + can_powerful_sections_jump, + } = section.section_type + { + access |= 0b1000; + if can_powerful_sections_jump { + access |= 0b100; + } + if section.readable { + access |= 0b10; + } + } else if section.section_type == SectionType::DataSection { + access |= 0b0000; + if section.writable { + access |= 0b10; + } + } + } + + let flags = 0b1100u8; + + let entry = GDTEntry { + limit: len, + base: section.address as u32, + access, + flags, + }; + if section.length > 0xFFFFF { + section.length -= 0xFFFFF; + } + entries.push(entry); + } + while section.length > 0xFFFFF { + make_entry(&mut section, &mut entries); + } + make_entry(&mut section, &mut entries); + } + unsafe { + let _ = super::interrupts::pop_irq(); + + let segment_entries: Vec = entries.clone(); + + let ptr = write_gdt_entries(entries)?; + + let gdtr = GDTR { + address: ptr as *const u8 as usize as u32, + size: (ptr.len()-1) as u16 + }; + + let addr = &gdtr as *const GDTR as *const () as usize as u32; + + asm!( + "lgdt eax", + in("eax") addr + ); + + let mut code_segment = 0u16; + let mut code_set = false; + let mut data_segment = 0u16; + let mut data_set = false; + + let mut i = 0; + for entry in segment_entries { + let entry: GDTEntry = entry; + i += 1; + if code_set && data_set { + break; + } + + if entry.access & 0b11000 == 0b11000 && !code_set { + code_segment = i-1; + code_set = true; + } else if entry.access & 0b10000 == 0b10000 && !data_set { + data_segment = i-1; + data_set = true; + } + } + + asm!( + "jmp bx:2 ; `#[deny(named_asm_labels)]` on by default; see + 2: ; ax is already loaded with the correct value from rustland + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax", + in("bx") code_segment, + in("ax") data_segment, + options(preserves_flags, nomem, nostack) + ); + } + + Ok(()) + } +} + +/// A memory section builder. +pub struct MemorySectionBuilder { + sections: Vec, +} + +impl MemorySectionBuilder { + pub fn new() -> Self { + MemorySectionBuilder { sections: vec![] } + } + + pub fn add_section(&mut self, section: MemorySection) -> &mut Self { + self.sections.push(section); + + self + } + + pub fn finish(self) -> MemorySections { + MemorySections { + sections: self.sections, + } + } +} diff --git a/kernel/src/kernel/arch/x86/mod.rs b/kernel/src/kernel/arch/x86/mod.rs index dba4213..116141e 100644 --- a/kernel/src/kernel/arch/x86/mod.rs +++ b/kernel/src/kernel/arch/x86/mod.rs @@ -8,6 +8,8 @@ pub mod ports; pub mod output; pub mod egatext; pub mod paging; +mod gdt; +pub mod memory; mod constants; @@ -15,6 +17,10 @@ pub(self) use constants::*; use interrupts::{pop_irq, restore_irq}; use ports::{inb, outb}; +pub const fn get_arch() -> super::Architecture { + super::Architecture::X86 +} + #[aphrodite_proc_macros::kernel_item(PagingAvailabe)] pub fn paging_available() -> bool { true diff --git a/kernel/src/kernel/errors.rs b/kernel/src/kernel/errors.rs index ee788dc..107ab4b 100644 --- a/kernel/src/kernel/errors.rs +++ b/kernel/src/kernel/errors.rs @@ -11,7 +11,7 @@ pub struct Error<'a> { impl<'a> Error<'a> { /// Creates a new error. - pub fn new(message: &'a str, code: i16) -> Self { + pub const fn new(message: &'a str, code: i16) -> Self { Error { message, code } } } diff --git a/kernel/src/kernel/indep_boot_entry.rs b/kernel/src/kernel/indep_boot_entry.rs index 67bd6f4..eea3bac 100644 --- a/kernel/src/kernel/indep_boot_entry.rs +++ b/kernel/src/kernel/indep_boot_entry.rs @@ -17,6 +17,7 @@ fn indep_boot_entry( display: Option<&dyn crate::display::TextDisplay>, #[allow(non_snake_case)] BI: &crate::boot::BootInfo, ) -> ! { + assert_ne!(crate::arch::get_arch(), crate::arch::Architecture::ExampleDummy); crate::arch::output::sdebugsln("IndepBootEntry called"); let display = display.unwrap_or(&NoneTextDisplay {}); diff --git a/kernel/src/kernel/memsections.rs b/kernel/src/kernel/memsections.rs new file mode 100644 index 0000000..4726fac --- /dev/null +++ b/kernel/src/kernel/memsections.rs @@ -0,0 +1,69 @@ +//! Architecture-independt memory section stuff. +//! +//! arch::*::memory is the architecture-dependent counterpart. + +/// Section types for [MemorySection]. +/// If the architecture doesn't support, for example, writing +/// to a code section, then this should take precedence over +/// [MemorySection] attributes. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum SectionType { + /// A code section. Generally at least one of these for the kernel. + CodeSection { + /// Whether more powerful owners can jump to this if the owner + /// is less powerful. + can_powerful_sections_jump: bool + }, + /// A data section. Generally at least one of these for the kernel. + DataSection, + /// A [task state section](https://wiki.osdev.org/Task_State_Segment). + /// These can be interpreted as [DataSection](SectionType::DataSection)s + /// if they aren't directly supported by the hardware. + TaskSection { + /// Whether the section is busy. + busy: bool + }, +} + +/// The owner of a [MemorySection]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Owner { + /// Userspace. + Userspace, + /// Kernelspace. + Kernelspace, + /// Modulespace. + Modulespace, +} + +/// A memory section. +#[derive(Clone, Copy)] +pub struct MemorySection { + /// The type of section. + pub section_type: SectionType, + /// The owner of the section. + pub owner: Owner, + /// Whether the kernel should minimize reads to the section. + pub minimal_read: bool, + /// Whether the segment is readable. + pub readable: bool, + /// Whether the segment is writable. + pub writable: bool, + /// The base address. + pub address: u64, + /// The length. If the implementation has a maximum length of + /// sections, it should be automatically split into however many sections are necessary. + pub length: u64, +} + +/// Implemented by arch::*::memory::MemorySections. Note to implementers: +/// Copy should NOT be implemented. That would lead to issues where a +/// struct implementing this trait could be used after [write](MemorySections::write) +/// is called, which is not supposed to happen. +pub unsafe trait MemorySections { + /// Write the sections to an allocated region and then activate them. + /// + /// This intentionally takes ownership of the MemorySections as it + /// shouldn't be used after this is called. + unsafe fn write(self) -> Result<(), crate::Error<'static>>; +} \ No newline at end of file diff --git a/kernel/src/kernel/mod.rs b/kernel/src/kernel/mod.rs index 72299be..358c01d 100644 --- a/kernel/src/kernel/mod.rs +++ b/kernel/src/kernel/mod.rs @@ -1,6 +1,9 @@ //! This provides raw methods for internal kernel usage for the Aphrodite kernel. See aphrodite_user for userspace. #![no_std] #![warn(missing_docs)] +#![warn(rustdoc::missing_crate_level_docs)] +#![deny(rustdoc::invalid_html_tags)] +#![deny(rustdoc::invalid_rust_codeblocks)] // tidy-alphabetical-start #![feature(ptr_metadata)] #![feature(const_trait_impl)] @@ -31,9 +34,10 @@ pub mod output; pub mod psfont; mod traits; mod util; +pub mod memsections; #[macro_use] -mod cfg; +pub(crate) mod cfg; #[allow(unused_imports)] // if there are no constants, then it gives a warning pub use constants::*;