From 25528c99520c42e8d5895edb098b04990d8dfc29 Mon Sep 17 00:00:00 2001 From: Arthur Beck Date: Thu, 6 Feb 2025 18:32:48 -0600 Subject: [PATCH] I really need to commit more, but made good progress! Untested memory manager implemented. --- kernel/build | 7 +- kernel/config.aphro.example | 3 + kernel/src/include/_entry.rs | 17 + kernel/src/include/boot.rs | 79 ++++- kernel/src/include/cfg.rs | 13 + kernel/src/include/cmdline.rs | 104 +++++++ kernel/src/include/errors.rs | 11 + kernel/src/include/mem.rs | 343 +++++++++++++++++++++ kernel/src/include/mod.rs | 12 + kernel/src/include/multiboot2.rs | 74 ++--- kernel/src/include/util.rs | 84 ++++- kernel/src/internal/arch/x86_asmp/entry.rs | 15 +- kernel/targets | 2 +- kernel/validate-cfg | 3 + 14 files changed, 702 insertions(+), 65 deletions(-) create mode 100644 kernel/src/include/cfg.rs create mode 100644 kernel/src/include/cmdline.rs create mode 100644 kernel/src/include/mem.rs create mode 100644 kernel/validate-cfg diff --git a/kernel/build b/kernel/build index c0434d5..2e3b033 100755 --- a/kernel/build +++ b/kernel/build @@ -3,6 +3,10 @@ ( set -e + if [[ -n "$KERNEL_DIR" ]]; then + export KERNEL_DIR=$(readlink -e .) + fi + DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi @@ -34,7 +38,8 @@ cd ../kernel for target in $TARGETS; do - cargo build --target "$target" --release -Zbuild-std --bin entrypoint + real_target=${!target} + cargo build --target "$real_target" --release -Zbuild-std --bin entrypoint_$target done # build the kernel's entrypoint diff --git a/kernel/config.aphro.example b/kernel/config.aphro.example index adde185..0970aa8 100644 --- a/kernel/config.aphro.example +++ b/kernel/config.aphro.example @@ -31,4 +31,7 @@ CONFIG_PREUSER_OUTPUT_FATAL=true # Whether to build an iso with GRUB. Used in ./build. CONFIG_BUILD_GRUB=true + +# The precision of the allocator. The size of the allocated region is divided by this to get how much to change it by each time. +CONFIG_ALLOC_PRECISION=4 # End configs \ No newline at end of file diff --git a/kernel/src/include/_entry.rs b/kernel/src/include/_entry.rs index c8c191b..6395466 100644 --- a/kernel/src/include/_entry.rs +++ b/kernel/src/include/_entry.rs @@ -3,10 +3,27 @@ #![allow(unexpected_cfgs)] #![allow(static_mut_refs)] +use core::alloc::{Allocator, Layout}; + use crate::output::*; +const MEM_TEST_SIZES: [usize; 8] = [1, 2, 4, 8, 16, 32, 64, 128]; + /// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files eventually call this. #[allow(non_snake_case)] pub fn _entry(display: Option<&dyn crate::display::TextDisplay>, BI: &crate::boot::BootInfo) -> ! { + let mut mem_map = BI.memory_map.unwrap(); + let allocator = crate::mem::MemoryMapAlloc::new(&mut mem_map).unwrap(); + tdebugsln("Testing allocator...", display.unwrap()); + + for size in MEM_TEST_SIZES { + tdebugs("Allocating ", display.unwrap()); + tdebugbnp(&crate::usize_as_u8_slice(size), display.unwrap()); + tdebugsnpln(" bytes of memory...", display.unwrap()); + if let Err(_) = allocator.allocate(Layout::from_size_align(size, 1).unwrap()) { + terrors("Failed to allocate: ",display.unwrap()); + unsafe { crate::mem::LAST_MEMMAP_ERR.unwrap_err().display_np(display.unwrap()) } + } + } loop {} } \ No newline at end of file diff --git a/kernel/src/include/boot.rs b/kernel/src/include/boot.rs index 3985897..523e03f 100644 --- a/kernel/src/include/boot.rs +++ b/kernel/src/include/boot.rs @@ -5,7 +5,7 @@ /// except for memory with type [MemoryType::Free] /// or [MemoryType::HardwareSpecific] memory with /// the boolean argument set. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum MemoryType { /// Free RAM with no use. Free, @@ -23,27 +23,76 @@ pub enum MemoryType { /// whether memory can be allocated in this region. HardwareSpecific(u32, bool), /// Flash/semi-permanent memory. Generally used in embedded systems. - Permanent + Permanent, } /// A single memory mapping for [MemoryMap]. -pub trait MemoryMapping { +#[derive(Clone, Copy)] +pub struct MemoryMapping { /// Returns the type of the memory. - fn get_type(&self) -> MemoryType; + pub mem_type: MemoryType, /// Returns the beginning of the memory. - fn get_start(&self) -> u64; + pub start: u64, /// Returns the length of the memory. - fn get_length(&self) -> u64; + pub len: u64, } -/// Memory mapping. -pub trait _MemoryMap: core::iter::Iterator + core::ops::Index { - /// Returns the number of [MemoryMapping]s in the MemoryMap. This is total, not remainder. - fn len(&self) -> usize; +#[derive(Clone, Copy)] +pub struct MemoryMap { + pub len: u64, + pub size_pages: u64, + pub page_size: u64, + + /// All sections. + pub sections: &'static [MemoryMapping], + + /// Iterator's index. + pub idx: usize, } -/// Memory mapping. Used so that we can downcast. -pub trait MemoryMap: _MemoryMap + core::any::Any {} +impl MemoryMap { + pub fn len(&self) -> u64 { + self.sections.len() as u64 + } + pub fn reset_iter(&mut self) { + self.idx = 0; + } + pub fn mem_size(&mut self) -> u64 { + let curr_idx = self.idx; + self.reset_iter(); + let mut out = 0u64; + for ele in self.sections { + if ele.mem_type == crate::boot::MemoryType::Free { + out += ele.len; + } else if let crate::boot::MemoryType::HardwareSpecific(_, free) = ele.mem_type { + if free { + out += ele.len; + } + } + } + self.idx = curr_idx; + out + } +} + +impl core::ops::Index for MemoryMap { + type Output = MemoryMapping; + + fn index(&self, index: usize) -> &Self::Output { + &self.sections[index] + } +} + +impl core::iter::Iterator for MemoryMap { + type Item = MemoryMapping; + fn next(&mut self) -> Option { + self.idx += 1; + if self.sections.len()<=self.idx-1 { + return None; + } + Some(self.sections[self.idx-1].into()) + } +} /// Bootloader-independent information. #[derive(Clone)] @@ -51,13 +100,13 @@ pub struct BootInfo<'a> { /// The commandline of the kernel. /// See for the format. pub cmdline: Option<&'static str>, - + /// The memory map provided by the bootloader. If None, the kernel will attempt to generate it. - pub memory_map: Option<&'a dyn MemoryMap>, + pub memory_map: Option, /// The name of the bootloader(for example, "GRUB 2.12"). pub bootloader_name: Option<&'static str>, /// Provides a way to display text. pub output: Option<&'a dyn crate::display::TextDisplay>, -} \ No newline at end of file +} diff --git a/kernel/src/include/cfg.rs b/kernel/src/include/cfg.rs new file mode 100644 index 0000000..33f0fd0 --- /dev/null +++ b/kernel/src/include/cfg.rs @@ -0,0 +1,13 @@ +//! Config-related stuff. +//! +#[macro_export] +macro_rules! cfg_int { + ($cfg:literal, $type:ident) => { + paste::paste! { + { + let cfg = env!($cfg).as_bytes(); + crate::[< str_as_ $type >](cfg) + } + } + }; +} \ No newline at end of file diff --git a/kernel/src/include/cmdline.rs b/kernel/src/include/cmdline.rs new file mode 100644 index 0000000..cfaf0ee --- /dev/null +++ b/kernel/src/include/cmdline.rs @@ -0,0 +1,104 @@ +//! Provide functions and structs for parsing a kernel command line. + +/// A value of an argument. +#[derive(Clone, Copy)] +pub enum ArgumentValue { + /// A string argument with the preceeding and following single quotes removed, and any \' replaced with '. + Str(&'static str), + /// A float argument. + Float(f128), + /// A signed argument. + Signed(i128), + /// A unsigned argument. + Unsigned(u128), +} + +/// A single argument in a [Cmdline]. +#[derive(Clone, Copy)] +pub struct Argument { + /// The name of an argument. + pub name: &'static str, + /// The value of an argument. + pub value: ArgumentValue +} + +/// A single flag in a [Cmdline]. +#[derive(Clone, Copy)] +pub struct Flag { + /// The name of a flag. + pub name: &'static str +} + +/// A kernel command line. +#[derive(Clone)] +pub struct Cmdline { + /// The arguments of the Cmdline. + pub arguments: &'static [Argument], + /// The flags of the Cmdline. + pub flags: &'static [Flag], + + /// The argument validators. When using [CmdlineValidator], it will check all of them + /// and if ALL of them report ANY of the arguments incorrect, then it will return an error. + pub argument_validators: &'static [&'static dyn ArgumentValidator], + + /// The flag validators. When using [CmdlineValidator], it will check all of them + /// and if ALL of them report ANY of the flags incorrect, then it will return an error. + pub flag_validators: &'static [&'static dyn FlagValidator], +} + +/// A validator of one of the types in this module. +pub trait Validator { + /// The type that the trait validates. + type Validates; + + /// Validate a value. + fn validate<'a>(&self, value: Self::Validates) -> Result<(), crate::Error<'a>>; +} + +/// A [Validator] that validates arguments. +pub trait ArgumentValidator: Validator {} + +/// A [Validator] that validates flags. +pub trait FlagValidator: Validator {} + +/// A [Validator] that validates cmdlines. +pub struct CmdlineValidator {} + +/// Error returned by [CmdlineValidator::validate] when an argument is incorrect +pub const ERR_INVALID_ARGUMENT: i16 = -1; + +/// Error returned by [CmdlineValidator::validate] when a flag is incorrect +pub const ERR_INVALID_FLAG: i16 = -2; + +impl Validator for CmdlineValidator { + type Validates = Cmdline; + + fn validate<'a>(&self, value: Self::Validates) -> Result<(), crate::Error<'a>> { + for arg in value.arguments { + let mut correct = false; + for validator in value.argument_validators { + if validator.validate(*arg).is_ok() { + correct = true; + break; + } + } + if !correct { + return Err(crate::Error::new("invalid argument in command line", ERR_INVALID_ARGUMENT)); + } + } + + for arg in value.flags { + let mut correct = false; + for validator in value.flag_validators { + if validator.validate(*arg).is_ok() { + correct = true; + break; + } + } + if !correct { + return Err(crate::Error::new("invalid flag in command line", ERR_INVALID_FLAG)); + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/kernel/src/include/errors.rs b/kernel/src/include/errors.rs index d8fbc17..e2771f1 100644 --- a/kernel/src/include/errors.rs +++ b/kernel/src/include/errors.rs @@ -1,6 +1,9 @@ //! Stuff related to errors. +use crate::display::TextDisplay; + /// An error used by aphrodite +#[derive(Clone, Copy)] pub struct Error<'a> { message: &'a str, code: i16 @@ -13,6 +16,14 @@ impl<'a> Error<'a> { } } +impl Error<'_> { + pub fn display_np(&self, display: &dyn TextDisplay) { + crate::output::terrorbnp(&crate::i16_as_u8_slice(self.code), display); + crate::output::terrorsnp(": ", display); + crate::output::terrorsnpln(self.message, display); + } +} + impl core::fmt::Debug for Error<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str(core::str::from_utf8(&crate::i16_as_u8_slice(self.code)).unwrap())?; diff --git a/kernel/src/include/mem.rs b/kernel/src/include/mem.rs new file mode 100644 index 0000000..cde1bb1 --- /dev/null +++ b/kernel/src/include/mem.rs @@ -0,0 +1,343 @@ +//! Memory allocation. + +use core::{ + alloc::{Allocator, GlobalAlloc}, + num::NonZero, + ops::Range, + ptr::{NonNull, null_mut}, +}; + +use crate::boot::MemoryType; + +#[derive(Clone, Copy)] +struct Allocation { + pub used: bool, + pub addr: u64, + pub len: u64, +} + +#[derive(Clone, Copy)] +struct AllocationHeader { + pub used: bool, + pub addr: u64, + pub len: u64, + pub num_allocations: u64, +} + +struct AllocationIter { + ptr: *const Allocation, + num_allocations: u64, + idx: u64, +} + +impl Iterator for AllocationIter { + type Item = *mut Allocation; + fn next(&mut self) -> Option<::Item> { + if self.idx >= self.num_allocations { + return None; + } + Some(&unsafe { + *((self.ptr as usize + (size_of::() * self.idx as usize)) + as *const Allocation) + } as *const Allocation as *mut Allocation) + } +} + +/// A implementation of a physical memory allocator that uses a [crate::boot::MemoryMap]. +pub struct MemoryMapAlloc<'a> { + /// The memory map to use to allocate memory. + pub memory_map: &'a mut crate::boot::MemoryMap, + + allocationheader: *mut AllocationHeader, + allocations: *mut Allocation, + max_allocations_size: u64, +} + +/// Too many allocations have been created, pushing the size of [MemoryMapAlloc::allocations] over [MemoryMapAlloc::max_allocations_size]. +const TOO_MANY_ALLOCATIONS: i16 = -2; + +/// There isn't enough space for 32 allocations(the minimum available). +pub const ALLOCATIONS_NOT_ENOUGH_SPACE: i16 = -3; + +/// The index provided to [MemoryMapAlloc::extend_allocation] is too big. +const EXTEND_ALLOCATION_INVALID_INDEX: i16 = -4; + +/// The allocation provided to [MemoryMapAlloc::extend_allocation] is unused. +const EXTEND_ALLOCATION_ALLOCATION_UNUSED: i16 = -5; + +/// The allocation provided to [MemoryMapAlloc::extend_allocation], if extended, would extend into another allocation. +const EXTEND_ALLOCATION_OTHER_ALLOCATION: i16 = -6; + +impl<'a> MemoryMapAlloc<'a> { + /// Creates a new [MemoryMapAlloc]. Please call this method instead of creating it manually! + /// This method uses the memory mapping to + pub fn new( + memory_map: &'a mut crate::boot::MemoryMap, + ) -> Result, crate::Error<'a>> { + let mut out = MemoryMapAlloc { + memory_map, + allocations: core::ptr::null_mut(), + allocationheader: core::ptr::null_mut(), + max_allocations_size: 0, + }; + out.memory_map.reset_iter(); + for mapping in &mut *out.memory_map { + if mapping.len < (size_of::() * 32) as u64 { + continue; + } + if mapping.mem_type == MemoryType::Free { + out.allocationheader = core::ptr::from_raw_parts_mut( + core::ptr::without_provenance_mut::<()>(mapping.start as usize), + (), + ); + out.allocations = core::ptr::from_raw_parts_mut( + core::ptr::without_provenance_mut::<()>(mapping.start as usize+size_of::()), + (), + ); + out.max_allocations_size = mapping.len; + } else if let MemoryType::HardwareSpecific(_, allocatable) = mapping.mem_type { + if allocatable { + out.allocationheader = core::ptr::from_raw_parts_mut( + core::ptr::without_provenance_mut::<()>(mapping.start as usize), + (), + ); + out.allocations = core::ptr::from_raw_parts_mut( + core::ptr::without_provenance_mut::<()>(mapping.start as usize+size_of::()), + (), + ); + out.max_allocations_size = mapping.len; + } + } + } + if out.allocations == core::ptr::null_mut() { + return Err(crate::Error::new( + "no free memory with space for 32 allocations", + ALLOCATIONS_NOT_ENOUGH_SPACE, + )); + } + unsafe { + (*out.allocations) = Allocation { + used: false, + addr: 0, + len: 0, + }; + (*out.allocationheader) = AllocationHeader { + used: true, + addr: out.allocations as usize as u64, + len: (size_of::() * 32) as u64, + num_allocations: 0 + } + } + Ok(out) + } + + /// Creates a [AllocationIter] to iterate over the current allocations. + fn allocations_iter(&self) -> AllocationIter { + AllocationIter { + ptr: self.allocations, + num_allocations: unsafe { *self.allocationheader }.num_allocations, + idx: 0, + } + } + + /// Add an allocation to [MemoryMapAlloc::allocations]. It will overwrite allocations with `used` set to false. + fn add_allocation(&self, allocation: Allocation) -> Result<(), crate::Error<'static>> { + let mut created_allocation = false; + for alloc in self.allocations_iter() { + if !unsafe { *alloc }.used { + unsafe { (*alloc) = allocation } + created_allocation = true; + break; + } + } + if created_allocation { + return Ok(()); + } + + unsafe { *self.allocationheader }.num_allocations += 1; + + let num_allocations = unsafe { *self.allocationheader }.num_allocations; + + if unsafe { *self.allocations }.len + < (size_of::() as u64 * (num_allocations)) + { + if unsafe { *self.allocationheader }.len + size_of::() as u64 >= self.max_allocations_size { + return Err(crate::Error::new( + "not enough space for another allocation", + TOO_MANY_ALLOCATIONS, + )); + } + + let res = self.extend_allocation(0, size_of::() as u64); + if let Err(err) = res { + unsafe { *self.allocationheader }.num_allocations -= 1; + return Err(err); + } + } + + let new_alloc = (self.allocations as usize + + (size_of::() * (num_allocations) as usize)) + as *const Allocation as *mut Allocation; + + unsafe { (*new_alloc) = allocation } + + Ok(()) + } + + /// Extend an allocation. This has numerous checks, so please use this + /// instead of manually changing [Allocation::len]! + #[inline(always)] + fn extend_allocation(&self, idx: u64, by: u64) -> Result<(), crate::Error<'static>> { + if idx > unsafe { *self.allocationheader }.num_allocations { + return Err(crate::Error::new( + "the index provided to extend_allocation is too large", + EXTEND_ALLOCATION_INVALID_INDEX, + )); + } + let alloc = (self.allocations as usize + (size_of::() * idx as usize)) + as *const Allocation as *mut Allocation; + + if !unsafe { *alloc }.used { + return Err(crate::Error::new( + "the allocation provided to extend_allocation is unused", + EXTEND_ALLOCATION_ALLOCATION_UNUSED, + )); + } + + if self.check_range( + (unsafe { *alloc }.addr + unsafe { *alloc }.len) + ..(unsafe { *alloc }.addr + unsafe { *alloc }.len + by), + ) { + return Err(crate::Error::new( + "the allocation, if extended, would extend into another allocation", + EXTEND_ALLOCATION_OTHER_ALLOCATION, + )); + } + + unsafe { + (*alloc).len += by; + } + Ok(()) + } + + /// Check to see if any allocations contain the given address. Returns true if so. + fn check_addr(&self, addr: u64) -> bool { + for ele in self.allocations_iter() { + let alloc = unsafe { *ele }; + if addr > alloc.addr && addr < alloc.addr + alloc.len { + return true; + } + } + false + } + + /// Check to see if a range of addresses have any allocations within. Returns true if so. + fn check_range(&self, addr: Range) -> bool { + for addr in addr { + // REALLY inefficient, but I don't think there's a better way. + if self.check_addr(addr) { + return true; + } + } + false + } +} + +/// Error returned when free memory is not available. +pub const FREE_MEMORY_UNAVAILABLE: i16 = -1; + +/// Error returned when memory wasn't allocated. +pub const MEMORY_NOT_ALLOCATED: i16 = -7; + +unsafe impl<'a> GlobalAlloc for MemoryMapAlloc<'a> { + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + let result = self.allocate(layout); + if result.is_err() { + return null_mut(); + } + result.unwrap().as_mut_ptr() as *mut u8 + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { + unsafe { + self.deallocate( + NonNull::without_provenance(NonZero::new(ptr as usize).unwrap()), + layout, + ); + } + } +} + +/// The last status of memory allocation or deallocation for a [MemoryMapAlloc]. +/// This can be used for more insight to why an allocation or deallocation failed. +pub static mut LAST_MEMMAP_ERR: Result<(), crate::Error<'static>> = Ok(()); + +unsafe impl<'a> Allocator for MemoryMapAlloc<'a> { + fn allocate( + &self, + layout: core::alloc::Layout, + ) -> Result, core::alloc::AllocError> { + unsafe { LAST_MEMMAP_ERR = Ok(()) } + if self.allocations == core::ptr::null_mut() { + unsafe { + LAST_MEMMAP_ERR = Err(crate::Error::new( + "Allocations storage not set up", + FREE_MEMORY_UNAVAILABLE, + )) + } + return Err(core::alloc::AllocError {}); + } + let mut addr = 0u64; + for mapping in self.memory_map.clone() { + if mapping.len < layout.size() as u64 { + continue; + } + let mut allocatable = false; + if mapping.mem_type == MemoryType::Free { + allocatable = true; + } else if let MemoryType::HardwareSpecific(_, alloc) = mapping.mem_type { + allocatable = alloc; + } + if allocatable { + addr = mapping.start+mapping.len-layout.size() as u64; + while self.check_range(addr..addr+layout.size() as u64) && (addr as usize % layout.align() != 0) { + addr -= layout.size() as u64/crate::cfg_int!("CONFIG_ALLOC_PRECISION", u64); + } + } + } + if let Err(err) = self.add_allocation(Allocation { used: true, addr, len: layout.size() as u64 }) { + unsafe { LAST_MEMMAP_ERR = Err(err) } + return Err(core::alloc::AllocError {}); + } + + Ok(NonNull::from_raw_parts( + NonNull::::without_provenance(NonZero::new(addr as usize).unwrap()), + layout.size(), + )) + } + unsafe fn deallocate(&self, ptr: core::ptr::NonNull, _layout: core::alloc::Layout) { + unsafe { LAST_MEMMAP_ERR = Ok(()) } + let addr = ptr.addr().get() as u64; + if !self.check_addr(addr) { // Memory not allocated, something is up + unsafe { LAST_MEMMAP_ERR = Err(crate::Error::new("memory not allocated", MEMORY_NOT_ALLOCATED)) } + return; + } + if self.allocations == core::ptr::null_mut() { + unsafe { + LAST_MEMMAP_ERR = Err(crate::Error::new( + "Allocations storage not set up", + FREE_MEMORY_UNAVAILABLE, + )) + } + return; + } + for allocation in self.allocations_iter() { + if !unsafe { *allocation }.used { + continue; + } + if unsafe { *allocation }.addr == addr { + unsafe { *allocation }.used = false; + break; + } + } + } +} diff --git a/kernel/src/include/mod.rs b/kernel/src/include/mod.rs index f433cbc..50ecfd6 100644 --- a/kernel/src/include/mod.rs +++ b/kernel/src/include/mod.rs @@ -3,6 +3,11 @@ #![warn(missing_docs)] #![feature(ptr_metadata)] #![feature(const_trait_impl)] +#![feature(f128)] +#![feature(ptr_alignment_type)] +#![feature(allocator_api)] +#![feature(slice_ptr_get)] +#![feature(nonnull_provenance)] mod constants; mod util; @@ -15,6 +20,13 @@ pub mod output; pub mod boot; pub mod psfont; pub mod display; +pub mod cmdline; +pub mod mem; + +#[macro_use] +mod cfg; + +pub use cfg::*; #[allow(unused_imports)] // if there are no constants, then it gives a warning pub use constants::*; diff --git a/kernel/src/include/multiboot2.rs b/kernel/src/include/multiboot2.rs index 714628d..ec603ab 100644 --- a/kernel/src/include/multiboot2.rs +++ b/kernel/src/include/multiboot2.rs @@ -1,5 +1,7 @@ //! Definitions of structs for multiboot2 information. Mostly used during pre-userspace. +use crate::boot::MemoryMapping; + /// Used for Multiboot2 tags. This shouldn't be used after a [crate::boot::BootInfo] struct has been initalized, but it still can be used. #[repr(C)] #[derive(Clone)] @@ -35,7 +37,7 @@ pub struct Module { /// One memory section provided by a Multiboot2 bootloader. #[repr(C)] -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct MemorySection { /// The base address of the section. pub base_addr: u64, @@ -48,22 +50,20 @@ pub struct MemorySection { reserved: u32, } -impl crate::boot::MemoryMapping for MemorySection { - fn get_type(&self) -> crate::boot::MemoryType { - match self.mem_type { - 1 => crate::boot::MemoryType::Free, - 2 => crate::boot::MemoryType::HardwareReserved, - 3 => crate::boot::MemoryType::HardwareSpecific(3, false), - 5 => crate::boot::MemoryType::Faulty, - _ => crate::boot::MemoryType::Reserved +impl Into for MemorySection { + fn into(self) -> crate::boot::MemoryMapping { + MemoryMapping { + mem_type: match self.mem_type { + 1 => crate::boot::MemoryType::Free, + 2 => crate::boot::MemoryType::HardwareReserved, + 3 => crate::boot::MemoryType::HardwareSpecific(3, false), + 5 => crate::boot::MemoryType::Faulty, + _ => crate::boot::MemoryType::Reserved + }, + start: self.base_addr, + len: self.length } } - fn get_start(&self) -> u64 { - self.base_addr - } - fn get_length(&self) -> u64 { - self.length - } } /// The raw memory map provided by a Multiboot2 bootloader. This is interpreted @@ -87,39 +87,33 @@ pub struct RawMemoryMap { pub struct MemoryMap { /// The version of the memory map. Should be disregarded as it's 0. pub version: u32, // currently is 0, future Multiboot2 versions may increment - /// Size of one entry(one [MemorySection] for Aphrodite) + /// Size of one entry(one [MemorySection] for Aphrodite's Multiboot2 support) pub entry_size: u32, /// All sections. - pub sections: &'static [MemorySection], - + pub sections: &'static [crate::boot::MemoryMapping], /// Iterator's index. pub idx: usize, } -impl crate::boot::MemoryMap for MemoryMap {} - -impl crate::boot::_MemoryMap for MemoryMap { - fn len(&self) -> usize { - self.sections.len() +impl MemoryMap { + pub fn reset_iter(&mut self) { + self.idx = 0; } -} - -impl core::ops::Index for MemoryMap { - type Output = dyn crate::boot::MemoryMapping; - - fn index(&self, index: usize) -> &Self::Output { - &self.sections[index] as &'static dyn crate::boot::MemoryMapping - } -} - -impl core::iter::Iterator for MemoryMap { - type Item = &'static dyn crate::boot::MemoryMapping; - fn next(&mut self) -> Option { - self.idx += 1; - if self.sections.len()<=self.idx-1 { - return None; + pub fn mem_size(&mut self) -> u64 { + let curr_idx = self.idx; + self.reset_iter(); + let mut out = 0u64; + for ele in self.sections { + if ele.mem_type == crate::boot::MemoryType::Free { + out += ele.len; + } else if let crate::boot::MemoryType::HardwareSpecific(_, free) = ele.mem_type { + if free { + out += ele.len; + } + } } - Some(&self.sections[self.idx-1]) + self.idx = curr_idx; + out } } diff --git a/kernel/src/include/util.rs b/kernel/src/include/util.rs index 1cad947..c90a7cf 100644 --- a/kernel/src/include/util.rs +++ b/kernel/src/include/util.rs @@ -1,7 +1,7 @@ //! Utility functions /// Converts an i16 to an [u8; 6]. -pub fn i16_as_u8_slice(mut value: i16) -> [u8; 6] { +pub const fn i16_as_u8_slice(mut value: i16) -> [u8; 6] { let mut buf = [0u8; 6]; let mut i = 0; if value < 0 { @@ -23,7 +23,7 @@ pub fn i16_as_u8_slice(mut value: i16) -> [u8; 6] { } /// Converts an u32 to an [u8; 10]. -pub fn u32_as_u8_slice(mut value: u32) -> [u8; 10] { +pub const fn u32_as_u8_slice(mut value: u32) -> [u8; 10] { let mut buf = [0u8; 10]; let mut i = 9; if value == 0 { @@ -40,7 +40,7 @@ pub fn u32_as_u8_slice(mut value: u32) -> [u8; 10] { } /// Converts an u8 to an [u8; 3]. -pub fn u8_as_u8_slice(mut value: u8) -> [u8; 3] { +pub const fn u8_as_u8_slice(mut value: u8) -> [u8; 3] { let mut buf = [0u8; 3]; let mut i = 2; if value == 0 { @@ -57,7 +57,7 @@ pub fn u8_as_u8_slice(mut value: u8) -> [u8; 3] { } /// Converts an usize(32 or 64 bit) to an [u8; 10]. -pub fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { +pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { let mut buf = [0u8; 20]; let mut i = 19; if value == 0 { @@ -74,7 +74,7 @@ pub fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { } /// Converts an u64 to an [u8; 10]. -pub fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { +pub const fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { let mut buf = [0u8; 20]; let mut i = 19; if value == 0 { @@ -89,3 +89,77 @@ pub fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { } buf } + +/// Converts an &mut \[u8] to a i16. `value` is clobbered. +pub fn str_as_i16(mut value: &mut [u8]) -> i16 { + let mut out = 0i16; + let negative = core::str::from_utf8(value).unwrap().starts_with("-"); + if negative { + value = &mut value[1..]; + } + value.reverse(); + for byte in value { + let byte = *byte; + if byte == b'_' { + continue; + } + out *= 10; + out += (byte-b'0') as i16; + } + if negative { + out = -out; + } + out +} + +/// Converts an &mut \[u8] to a u32. `value` is clobbered. +pub fn str_as_u32(value: &mut [u8]) -> u32 { + let mut out = 0u32; + value.reverse(); + for byte in value { + let byte = *byte; + if byte == b'_' { + continue; + } + out *= 10; + out += (byte-b'0') as u32; + } + out +} + +/// Converts an &mut \[u8] to a u128. `value` is clobbered. +pub fn str_as_u128(value: &mut [u8]) -> u128 { + let mut out = 0u128; + value.reverse(); + for byte in value { + let byte = *byte; + if byte == b'_' { + continue; + } + out *= 10; + out += (byte-b'0') as u128; + } + out +} + +/// Converts an &mut \[u8] to a u64. +pub fn str_as_u64(value: &[u8]) -> u64 { + let mut out = 0u64; + for byte in value { + let byte = *byte; + if byte < b'0' || byte > b'9' { + continue; + } + out *= 10; + out += (byte-b'0') as u64; + } + + let mut reversed = 0; + + while out != 0 { + reversed = reversed * 10 + out % 10; + out /= 10; + } + + reversed +} \ No newline at end of file diff --git a/kernel/src/internal/arch/x86_asmp/entry.rs b/kernel/src/internal/arch/x86_asmp/entry.rs index ff5aaf6..72ce2da 100644 --- a/kernel/src/internal/arch/x86_asmp/entry.rs +++ b/kernel/src/internal/arch/x86_asmp/entry.rs @@ -1,4 +1,5 @@ //! The entrypoint to the kernel; placed before all other code. +#![cfg(any(target_arch = "x86"))] #![no_std] #![no_main] #![warn(missing_docs)] @@ -13,6 +14,7 @@ use aphrodite::multiboot2::{FramebufferInfo, MemoryMap, MemorySection, RawMemory use aphrodite::arch::output::*; use aphrodite::arch::egatext as egatext; use aphrodite::output::*; +use aphrodite::display::COLOR_DEFAULT; #[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))] #[unsafe(link_section = ".multiboot2")] @@ -152,7 +154,14 @@ extern "C" fn _start() -> ! { sections: &*core::ptr::from_raw_parts((&(*rawmemorymap).sections[0]) as &MemorySection, (*rawmemorymap).sections.len()), idx: 0 }; - BI.memory_map = Some(&MM); + let mm2 = aphrodite::boot::MemoryMap { + len: MM.sections.len() as u64, + size_pages: 1, + page_size: MM.mem_size(), + sections: MM.sections, + idx: 0 + }; + BI.memory_map = Some(mm2); }, 2 => { // Bootloader name if current_tag.tag_len < 8 { // Unexpected size, something is probably up @@ -248,9 +257,9 @@ extern "C" fn _start() -> ! { sdebugsln("Beginning output to screen..."); - let ega: &dyn aphrodite::TextDisplay = &framebuffer_info; + let ega: &dyn aphrodite::display::TextDisplay = &framebuffer_info; framebuffer_info.disable_cursor(); - ega.clear_screen(aphrodite::COLOR_DEFAULT); + ega.clear_screen(COLOR_DEFAULT); toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); diff --git a/kernel/targets b/kernel/targets index c453be0..b95f733 100644 --- a/kernel/targets +++ b/kernel/targets @@ -1,4 +1,4 @@ # Targets used for documentation. x86=$KERNEL_DIR/i686-unknown-none.json -TARGETS=$x86 \ No newline at end of file +TARGETS=x86 \ No newline at end of file diff --git a/kernel/validate-cfg b/kernel/validate-cfg new file mode 100644 index 0000000..1e92c83 --- /dev/null +++ b/kernel/validate-cfg @@ -0,0 +1,3 @@ +#!/bin/bash + +set -e \ No newline at end of file