I really need to commit more, but made good progress! Untested memory manager implemented.

This commit is contained in:
Arthur Beck 2025-02-06 18:32:48 -06:00
parent 2da90ef035
commit 25528c9952
14 changed files with 702 additions and 65 deletions

View file

@ -3,6 +3,10 @@
( (
set -e set -e
if [[ -n "$KERNEL_DIR" ]]; then
export KERNEL_DIR=$(readlink -e .)
fi
DIR="${BASH_SOURCE%/*}" DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
@ -34,7 +38,8 @@
cd ../kernel cd ../kernel
for target in $TARGETS; do 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 done
# build the kernel's entrypoint # build the kernel's entrypoint

View file

@ -31,4 +31,7 @@ CONFIG_PREUSER_OUTPUT_FATAL=true
# Whether to build an iso with GRUB. Used in ./build. # Whether to build an iso with GRUB. Used in ./build.
CONFIG_BUILD_GRUB=true 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 # End configs

View file

@ -3,10 +3,27 @@
#![allow(unexpected_cfgs)] #![allow(unexpected_cfgs)]
#![allow(static_mut_refs)] #![allow(static_mut_refs)]
use core::alloc::{Allocator, Layout};
use crate::output::*; 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. /// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files eventually call this.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn _entry(display: Option<&dyn crate::display::TextDisplay>, BI: &crate::boot::BootInfo) -> ! { 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 {} loop {}
} }

View file

@ -5,7 +5,7 @@
/// except for memory with type [MemoryType::Free] /// except for memory with type [MemoryType::Free]
/// or [MemoryType::HardwareSpecific] memory with /// or [MemoryType::HardwareSpecific] memory with
/// the boolean argument set. /// the boolean argument set.
#[derive(Clone, Copy)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum MemoryType { pub enum MemoryType {
/// Free RAM with no use. /// Free RAM with no use.
Free, Free,
@ -23,27 +23,76 @@ pub enum MemoryType {
/// whether memory can be allocated in this region. /// whether memory can be allocated in this region.
HardwareSpecific(u32, bool), HardwareSpecific(u32, bool),
/// Flash/semi-permanent memory. Generally used in embedded systems. /// Flash/semi-permanent memory. Generally used in embedded systems.
Permanent Permanent,
} }
/// A single memory mapping for [MemoryMap]. /// A single memory mapping for [MemoryMap].
pub trait MemoryMapping { #[derive(Clone, Copy)]
pub struct MemoryMapping {
/// Returns the type of the memory. /// Returns the type of the memory.
fn get_type(&self) -> MemoryType; pub mem_type: MemoryType,
/// Returns the beginning of the memory. /// Returns the beginning of the memory.
fn get_start(&self) -> u64; pub start: u64,
/// Returns the length of the memory. /// Returns the length of the memory.
fn get_length(&self) -> u64; pub len: u64,
} }
/// Memory mapping. #[derive(Clone, Copy)]
pub trait _MemoryMap: core::iter::Iterator<Item = &'static dyn MemoryMapping> + core::ops::Index<usize, Output = dyn MemoryMapping> { pub struct MemoryMap {
/// Returns the number of [MemoryMapping]s in the MemoryMap. This is total, not remainder. pub len: u64,
fn len(&self) -> usize; 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. impl MemoryMap {
pub trait MemoryMap: _MemoryMap + core::any::Any {} 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<usize> 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::Item> {
self.idx += 1;
if self.sections.len()<=self.idx-1 {
return None;
}
Some(self.sections[self.idx-1].into())
}
}
/// Bootloader-independent information. /// Bootloader-independent information.
#[derive(Clone)] #[derive(Clone)]
@ -51,13 +100,13 @@ pub struct BootInfo<'a> {
/// The commandline of the kernel. /// The commandline of the kernel.
/// See <https://aphrodite-os.github.io/book/command-line.html> for the format. /// See <https://aphrodite-os.github.io/book/command-line.html> for the format.
pub cmdline: Option<&'static str>, pub cmdline: Option<&'static str>,
/// The memory map provided by the bootloader. If None, the kernel will attempt to generate it. /// 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<MemoryMap>,
/// The name of the bootloader(for example, "GRUB 2.12"). /// The name of the bootloader(for example, "GRUB 2.12").
pub bootloader_name: Option<&'static str>, pub bootloader_name: Option<&'static str>,
/// Provides a way to display text. /// Provides a way to display text.
pub output: Option<&'a dyn crate::display::TextDisplay>, pub output: Option<&'a dyn crate::display::TextDisplay>,
} }

13
kernel/src/include/cfg.rs Normal file
View file

@ -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)
}
}
};
}

View file

@ -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<Validates = Argument> {}
/// A [Validator] that validates flags.
pub trait FlagValidator: Validator<Validates = Flag> {}
/// 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(())
}
}

View file

@ -1,6 +1,9 @@
//! Stuff related to errors. //! Stuff related to errors.
use crate::display::TextDisplay;
/// An error used by aphrodite /// An error used by aphrodite
#[derive(Clone, Copy)]
pub struct Error<'a> { pub struct Error<'a> {
message: &'a str, message: &'a str,
code: i16 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<'_> { impl core::fmt::Debug for Error<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 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())?; f.write_str(core::str::from_utf8(&crate::i16_as_u8_slice(self.code)).unwrap())?;

343
kernel/src/include/mem.rs Normal file
View file

@ -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<<Self as Iterator>::Item> {
if self.idx >= self.num_allocations {
return None;
}
Some(&unsafe {
*((self.ptr as usize + (size_of::<Allocation>() * 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<MemoryMapAlloc<'a>, 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::<Allocation>() * 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::<AllocationHeader>()),
(),
);
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::<AllocationHeader>()),
(),
);
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::<Allocation>() * 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::<Allocation>() as u64 * (num_allocations))
{
if unsafe { *self.allocationheader }.len + size_of::<Allocation>() 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::<Allocation>() 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::<Allocation>() * (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::<Allocation>() * 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<u64>) -> 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::ptr::NonNull<[u8]>, 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::<u8>::without_provenance(NonZero::new(addr as usize).unwrap()),
layout.size(),
))
}
unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, _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;
}
}
}
}

View file

@ -3,6 +3,11 @@
#![warn(missing_docs)] #![warn(missing_docs)]
#![feature(ptr_metadata)] #![feature(ptr_metadata)]
#![feature(const_trait_impl)] #![feature(const_trait_impl)]
#![feature(f128)]
#![feature(ptr_alignment_type)]
#![feature(allocator_api)]
#![feature(slice_ptr_get)]
#![feature(nonnull_provenance)]
mod constants; mod constants;
mod util; mod util;
@ -15,6 +20,13 @@ pub mod output;
pub mod boot; pub mod boot;
pub mod psfont; pub mod psfont;
pub mod display; 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 #[allow(unused_imports)] // if there are no constants, then it gives a warning
pub use constants::*; pub use constants::*;

View file

@ -1,5 +1,7 @@
//! Definitions of structs for multiboot2 information. Mostly used during pre-userspace. //! 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. /// 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)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
@ -35,7 +37,7 @@ pub struct Module {
/// One memory section provided by a Multiboot2 bootloader. /// One memory section provided by a Multiboot2 bootloader.
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone, Copy)]
pub struct MemorySection { pub struct MemorySection {
/// The base address of the section. /// The base address of the section.
pub base_addr: u64, pub base_addr: u64,
@ -48,22 +50,20 @@ pub struct MemorySection {
reserved: u32, reserved: u32,
} }
impl crate::boot::MemoryMapping for MemorySection { impl Into<crate::boot::MemoryMapping> for MemorySection {
fn get_type(&self) -> crate::boot::MemoryType { fn into(self) -> crate::boot::MemoryMapping {
match self.mem_type { MemoryMapping {
1 => crate::boot::MemoryType::Free, mem_type: match self.mem_type {
2 => crate::boot::MemoryType::HardwareReserved, 1 => crate::boot::MemoryType::Free,
3 => crate::boot::MemoryType::HardwareSpecific(3, false), 2 => crate::boot::MemoryType::HardwareReserved,
5 => crate::boot::MemoryType::Faulty, 3 => crate::boot::MemoryType::HardwareSpecific(3, false),
_ => crate::boot::MemoryType::Reserved 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 /// The raw memory map provided by a Multiboot2 bootloader. This is interpreted
@ -87,39 +87,33 @@ pub struct RawMemoryMap {
pub struct MemoryMap { pub struct MemoryMap {
/// The version of the memory map. Should be disregarded as it's 0. /// The version of the memory map. Should be disregarded as it's 0.
pub version: u32, // currently is 0, future Multiboot2 versions may increment 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, pub entry_size: u32,
/// All sections. /// All sections.
pub sections: &'static [MemorySection], pub sections: &'static [crate::boot::MemoryMapping],
/// Iterator's index. /// Iterator's index.
pub idx: usize, pub idx: usize,
} }
impl crate::boot::MemoryMap for MemoryMap {} impl MemoryMap {
pub fn reset_iter(&mut self) {
impl crate::boot::_MemoryMap for MemoryMap { self.idx = 0;
fn len(&self) -> usize {
self.sections.len()
} }
} pub fn mem_size(&mut self) -> u64 {
let curr_idx = self.idx;
impl core::ops::Index<usize> for MemoryMap { self.reset_iter();
type Output = dyn crate::boot::MemoryMapping; let mut out = 0u64;
for ele in self.sections {
fn index(&self, index: usize) -> &Self::Output { if ele.mem_type == crate::boot::MemoryType::Free {
&self.sections[index] as &'static dyn crate::boot::MemoryMapping out += ele.len;
} } else if let crate::boot::MemoryType::HardwareSpecific(_, free) = ele.mem_type {
} if free {
out += ele.len;
impl core::iter::Iterator for MemoryMap { }
type Item = &'static dyn crate::boot::MemoryMapping; }
fn next(&mut self) -> Option<Self::Item> {
self.idx += 1;
if self.sections.len()<=self.idx-1 {
return None;
} }
Some(&self.sections[self.idx-1]) self.idx = curr_idx;
out
} }
} }

View file

@ -1,7 +1,7 @@
//! Utility functions //! Utility functions
/// Converts an i16 to an [u8; 6]. /// 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 buf = [0u8; 6];
let mut i = 0; let mut i = 0;
if value < 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]. /// 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 buf = [0u8; 10];
let mut i = 9; let mut i = 9;
if value == 0 { 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]. /// 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 buf = [0u8; 3];
let mut i = 2; let mut i = 2;
if value == 0 { 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]. /// 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 buf = [0u8; 20];
let mut i = 19; let mut i = 19;
if value == 0 { 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]. /// 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 buf = [0u8; 20];
let mut i = 19; let mut i = 19;
if value == 0 { if value == 0 {
@ -89,3 +89,77 @@ pub fn u64_as_u8_slice(mut value: u64) -> [u8; 20] {
} }
buf 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
}

View file

@ -1,4 +1,5 @@
//! The entrypoint to the kernel; placed before all other code. //! The entrypoint to the kernel; placed before all other code.
#![cfg(any(target_arch = "x86"))]
#![no_std] #![no_std]
#![no_main] #![no_main]
#![warn(missing_docs)] #![warn(missing_docs)]
@ -13,6 +14,7 @@ use aphrodite::multiboot2::{FramebufferInfo, MemoryMap, MemorySection, RawMemory
use aphrodite::arch::output::*; use aphrodite::arch::output::*;
use aphrodite::arch::egatext as egatext; use aphrodite::arch::egatext as egatext;
use aphrodite::output::*; use aphrodite::output::*;
use aphrodite::display::COLOR_DEFAULT;
#[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))] #[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))]
#[unsafe(link_section = ".multiboot2")] #[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()), sections: &*core::ptr::from_raw_parts((&(*rawmemorymap).sections[0]) as &MemorySection, (*rawmemorymap).sections.len()),
idx: 0 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 2 => { // Bootloader name
if current_tag.tag_len < 8 { // Unexpected size, something is probably up 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..."); sdebugsln("Beginning output to screen...");
let ega: &dyn aphrodite::TextDisplay = &framebuffer_info; let ega: &dyn aphrodite::display::TextDisplay = &framebuffer_info;
framebuffer_info.disable_cursor(); 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(); toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); toutputsln("Testing EGA Text framebuffer...", ega).unwrap();

View file

@ -1,4 +1,4 @@
# Targets used for documentation. # Targets used for documentation.
x86=$KERNEL_DIR/i686-unknown-none.json x86=$KERNEL_DIR/i686-unknown-none.json
TARGETS=$x86 TARGETS=x86

3
kernel/validate-cfg Normal file
View file

@ -0,0 +1,3 @@
#!/bin/bash
set -e