From fc8e077095f54432719c23d3a823b300e1fea4de Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:37:46 +0000 Subject: [PATCH 01/28] wip: added rbtree. scheduler rewrite start. --- Cargo.lock | 75 ++- Cargo.toml | 2 + machine/arm/src/sched.rs | 2 +- machine/testing/src/sched.rs | 2 +- macros/src/lib.rs | 12 + macros/src/tree.rs | 99 ++++ src/lib.rs | 2 +- src/mem.rs | 61 +- src/mem/alloc.rs | 3 +- src/mem/array.rs | 262 +++++++-- src/mem/rbtree.rs | 1061 ++++++++++++++++++++++++++++++++++ src/mem/traits.rs | 24 + src/mem/view.rs | 63 ++ src/sched.rs | 71 ++- src/sched/rt.rs | 42 ++ src/sched/task.rs | 8 +- src/sched/thread.rs | 216 ++++--- src/syscalls/tasks.rs | 3 +- src/utils.rs | 4 + 19 files changed, 1807 insertions(+), 205 deletions(-) create mode 100644 macros/src/tree.rs create mode 100644 src/mem/rbtree.rs create mode 100644 src/mem/traits.rs create mode 100644 src/mem/view.rs create mode 100644 src/sched/rt.rs diff --git a/Cargo.lock b/Cargo.lock index 933791c..9f3703b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,36 @@ dependencies = [ "syn", ] +[[package]] +name = "kani" +version = "0.67.0" +source = "git+https://github.com/model-checking/kani#ac1f0a1c03fcbb805002154689ba15184e2f36b7" +dependencies = [ + "kani_core", + "kani_macros", +] + +[[package]] +name = "kani_core" +version = "0.67.0" +source = "git+https://github.com/model-checking/kani#ac1f0a1c03fcbb805002154689ba15184e2f36b7" +dependencies = [ + "kani_macros", +] + +[[package]] +name = "kani_macros" +version = "0.67.0" +source = "git+https://github.com/model-checking/kani#ac1f0a1c03fcbb805002154689ba15184e2f36b7" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "strum 0.27.2", + "strum_macros 0.27.2", + "syn", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1067,6 +1097,7 @@ dependencies = [ "hal-select", "hal-testing", "interface", + "kani", "macros", "quote", "rand", @@ -1154,6 +1185,28 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.105" @@ -1223,7 +1276,7 @@ dependencies = [ "itertools 0.13.0", "lru", "paste", - "strum", + "strum 0.26.3", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -1500,9 +1553,15 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", ] +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + [[package]] name = "strum_macros" version = "0.26.4" @@ -1516,6 +1575,18 @@ dependencies = [ "syn", ] +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "2.0.114" diff --git a/Cargo.toml b/Cargo.toml index cb3fff1..38c8641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ envparse = "0.1.0" # This is a host-compatible HAL which will be used for running tests and verification on the host. hal-testing = { path = "machine/testing", features = [] } +[target.'cfg(kani_ra)'.dependencies] +kani = { git = "https://github.com/model-checking/kani" } [features] default = [] diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index 18b463f..3026071 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -55,7 +55,7 @@ impl Add for StackPtr { } /// A stack on arm is 4 byte aligned and grows downwards. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct ArmStack { /// The top of the stack (highest address). /// Safety: NonNull can safely be covariant over u32. diff --git a/machine/testing/src/sched.rs b/machine/testing/src/sched.rs index 7bf874d..9715b16 100644 --- a/machine/testing/src/sched.rs +++ b/machine/testing/src/sched.rs @@ -5,7 +5,7 @@ use hal_api::{ stack::{StackDescriptor, Stacklike}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct TestingStack {} impl Stacklike for TestingStack { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 903945a..c871c86 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -3,6 +3,18 @@ use syn::parse_macro_input; use proc_macro2::TokenStream; +mod tree; + +#[proc_macro_derive(TaggedLinks, attributes(rbtree))] +pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + match tree::derive_tagged_links(&input) { + Ok(tokens) => tokens, + Err(e) => e.to_compile_error(), + }.into() +} + #[proc_macro_attribute] pub fn service( attr: proc_macro::TokenStream, diff --git a/macros/src/tree.rs b/macros/src/tree.rs new file mode 100644 index 0000000..f6c3b39 --- /dev/null +++ b/macros/src/tree.rs @@ -0,0 +1,99 @@ +use quote::quote; +use syn::{ + spanned::Spanned, Data, DeriveInput, Error, Fields, Path, +}; + +pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result { + let fields = match &input.data { + Data::Struct(ds) => match &ds.fields { + Fields::Named(named) => &named.named, + _ => { + return Err(Error::new( + ds.fields.span(), + "TaggedLinks only supports structs with named fields", + )) + } + }, + _ => { + return Err(Error::new( + input.span(), + "TaggedLinks can only be derived for structs", + )) + } + }; + + let rbtree_impls = impl_rbtree(input, fields)?; + + Ok(quote! { + #rbtree_impls + }) +} + +fn impl_rbtree(input: &DeriveInput, fields: &syn::punctuated::Punctuated) -> syn::Result { + let struct_ident = &input.ident; + let generics = &input.generics; + + let mut impls = Vec::new(); + + for field in fields { + let Some(field_ident) = field.ident.clone() else { continue }; + + if let (Some(tag_path), Some(idx_path)) = find_rbtree(&field.attrs)? { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let impl_block = quote! { + impl #impl_generics crate::mem::rbtree::Linkable<#tag_path, #idx_path> for #struct_ident #ty_generics #where_clause { + #[inline] + fn links(&self) -> &crate::mem::rbtree::Links<#tag_path, #idx_path> { + &self.#field_ident + } + #[inline] + fn links_mut(&mut self) -> &mut crate::mem::rbtree::Links<#tag_path, #idx_path> { + &mut self.#field_ident + } + } + }; + + impls.push(impl_block); + } + } + + if impls.is_empty() { + return Err(Error::new( + input.span(), + "No fields found with #[rbtree(tag = ..., idx = ...)] attribute", + )); + } + + Ok(quote! { #(#impls)* }) +} + +fn find_rbtree(attrs: &[syn::Attribute]) -> syn::Result<(Option, Option)> { + for attr in attrs { + if !attr.path().is_ident("rbtree") { + continue; + } + + let mut tag: Option = None; + let mut idx: Option = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("tag") { + let value = meta.value()?; // expects '=' + let p: Path = value.parse()?; + tag = Some(p); + Ok(()) + } else if meta.path.is_ident("idx") { + let value = meta.value()?; // expects '=' + let p: Path = value.parse()?; + idx = Some(p); + Ok(()) + } else { + Err(meta.error("expected `tag = SomePath` or `idx = SomePath`")) + } + })?; + + return Ok((tag, idx)); + } + Ok((None, None)) +} diff --git a/src/lib.rs b/src/lib.rs index cb40e60..287fd3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ pub mod print; pub mod sched; pub mod sync; pub mod syscalls; -pub mod time; +//pub mod time; pub mod uspace; use hal::Machinelike; diff --git a/src/mem.rs b/src/mem.rs index 897e031..6865d6b 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -11,6 +11,9 @@ pub mod boxed; pub mod heap; pub mod pool; pub mod queue; +pub mod rbtree; +pub mod traits; +pub mod view; /// The possible types of memory. Which is compatible with the multiboot2 memory map. /// Link: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html @@ -88,61 +91,3 @@ pub fn align_up(size: usize) -> usize { let align = align_of::(); (size + align - 1) & !(align - 1) } - -// VERIFICATION ----------------------------------------------------------------------------------- -#[cfg(kani)] -mod verification { - use super::*; - use crate::mem::alloc::MAX_ADDR; - - fn mock_ptr_write(dst: *mut T, src: T) { - // noop - } - - #[kani::proof] - #[kani::stub(core::ptr::write, mock_ptr_write)] - fn proof_init_allocator_good() { - const MAX_REGIONS: usize = 8; - let regions: [(&str, usize, usize); MAX_REGIONS] = - core::array::from_fn(|i| ("dummy", kani::any(), kani::any())); - - // contrain all regions - for &(_, base, size) in regions.iter() { - kani::assume(base % align_of::() == 0); - kani::assume(base > 0); - kani::assume(size > 0); - kani::assume(size < alloc::MAX_ADDR && size > alloc::BestFitAllocator::MIN_RANGE_SIZE); - kani::assume(base < alloc::MAX_ADDR - size); - } - - // for any i, j, i != j as indices into the memory regions the following should hold - let i: usize = kani::any(); - let j: usize = kani::any(); - kani::assume(i < MAX_REGIONS); - kani::assume(j < MAX_REGIONS); - kani::assume(i != j); - - // non-overlapping regions - let (_, base_i, size_i) = regions[i]; - let (_, base_j, size_j) = regions[j]; - kani::assume(base_i + size_i <= base_j || base_j + size_j <= base_i); - - // verify memory init - assert!(init_memory(®ions).is_ok()); - } - - #[kani::proof] - fn check_align_up() { - let size = kani::any(); - kani::assume(size > 0); - - let align = align_up(size); - assert_ne!(align, 0); - - if align != usize::MAX { - assert_eq!(align % align_of::(), 0); - assert!(align >= size); - } - } -} -// END VERIFICATION diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index 710ad03..415bccc 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -45,7 +45,8 @@ pub struct BestFitAllocator { /// Implementation of the BestFitAllocator. impl BestFitAllocator { - pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; + pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; + /// Creates a new BestFitAllocator. /// /// Returns the new BestFitAllocator. diff --git a/src/mem/array.rs b/src/mem/array.rs index e7274db..4e40954 100644 --- a/src/mem/array.rs +++ b/src/mem/array.rs @@ -1,18 +1,26 @@ //! This module implements static and dynamic arrays for in-kernel use. use super::boxed::Box; -use crate::utils::KernelError; +use crate::{ + mem::traits::{Get, GetMut, ToIndex}, + utils::KernelError, +}; use core::{borrow::Borrow, mem::MaybeUninit}; +use std::{ + ops::{Index, IndexMut}, +}; /// This is a fixed-size map that can store up to N consecutive elements. #[derive(Debug)] -pub struct IndexMap + Default, V, const N: usize> { +pub struct IndexMap +{ data: [Option; N], phantom: core::marker::PhantomData, } #[allow(dead_code)] -impl + Default, V, const N: usize> IndexMap { +impl IndexMap +{ /// Create a new IndexMap. /// /// Returns a new IndexMap. @@ -23,47 +31,17 @@ impl + Default, V, const N: usize> IndexMap { } } - /// Get the element at the given index. - /// - /// `index` - The index to get the element from. - /// - /// Returns `Some(&T)` if the index is in-bounds, otherwise `None`. - pub fn get(&self, index: &K) -> Option<&V> { - let index = *index.borrow(); - - if index < N { - self.data[index].as_ref() - } else { - None - } - } - - /// Get a mutable reference to the element at the given index. - /// - /// `index` - The index to get the element from. - /// - /// Returns `Some(&mut T)` if the index is in-bounds, otherwise `None`. - pub fn get_mut(&mut self, index: &K) -> Option<&mut V> { - let index = *index.borrow(); - - if index < N { - self.data[index].as_mut() - } else { - None - } - } - /// Insert a value at the given index. /// /// `index` - The index to insert the value at. /// `value` - The value to insert. /// /// Returns `Ok(())` if the index was in-bounds, otherwise `Err(KernelError::OutOfMemory)`. - pub fn insert(&mut self, index: &K, value: V) -> Result<(), KernelError> { - let index = *index.borrow(); + pub fn insert(&mut self, idx: &K, value: V) -> Result<(), KernelError> { + let idx = K::to_index(Some(idx)); - if index < N { - self.data[index] = Some(value); + if idx < N { + self.data[idx] = Some(value); Ok(()) } else { Err(KernelError::OutOfMemory) @@ -91,11 +69,11 @@ impl + Default, V, const N: usize> IndexMap { /// `index` - The index to remove the value from. /// /// Returns the value if it was removed, otherwise `None`. - pub fn remove(&mut self, index: &K) -> Option { - let index = *index.borrow(); + pub fn remove(&mut self, idx: &K) -> Option { + let idx = K::to_index(Some(idx)); - if index < N { - self.data[index].take() + if idx < N { + self.data[idx].take() } else { None } @@ -113,8 +91,8 @@ impl + Default, V, const N: usize> IndexMap { /// `index` - The index to start the iterator from. /// /// Returns an iterator over the elements in the map. - pub fn iter_from_cycle(&self, index: &K) -> impl Iterator> { - self.data.iter().cycle().skip(index.borrow() + 1) + pub fn iter_from_cycle(&self, idx: Option<&K>) -> impl Iterator> { + self.data.iter().cycle().skip(K::to_index(idx) + 1) } /// Get the next index that contains a value (this will cycle). @@ -122,13 +100,11 @@ impl + Default, V, const N: usize> IndexMap { /// `index` - The index to start the search from. /// /// Returns the next index (potentially < index) that contains a value, otherwise `None`. - pub fn next(&self, index: Option<&K>) -> Option { - let default = K::default(); - let index = index.unwrap_or(&default); - - for (i, elem) in self.iter_from_cycle(index).enumerate() { + pub fn next(&self, idx: Option<&K>) -> Option { + for (i, elem) in self.iter_from_cycle(idx).enumerate() { if elem.is_some() { - return Some((index.borrow() + i + 1) % N); + let idx = K::to_index(idx); + return Some((idx + i + 1) % N); } } @@ -146,6 +122,88 @@ impl + Default, V, const N: usize> IndexMap { } } +impl Index for IndexMap +{ + type Output = V; + + fn index(&self, index: K) -> &Self::Output { + self.get(&index).unwrap() + } +} + +impl IndexMut for IndexMap +{ + fn index_mut(&mut self, index: K) -> &mut Self::Output { + self.get_mut(&index).unwrap() + } +} + +impl Get for IndexMap +{ + type Output = V; + + fn get>(&self, index: Q) -> Option<&Self::Output> { + let idx = K::to_index(Some(index.borrow())); + if idx < N { + self.data[idx].as_ref() + } else { + None + } + } +} + +impl GetMut for IndexMap { + fn get_mut>(&mut self, index: Q) -> Option<&mut Self::Output> { + let idx = K::to_index(Some(index.borrow())); + if idx < N { + self.data[idx].as_mut() + } else { + None + } + } + + fn get2_mut>(&mut self, index1: Q, index2: Q) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { + let index1 = K::to_index(Some(index1.borrow())); + let index2 = K::to_index(Some(index2.borrow())); + + if index1 == index2 { + debug_assert!(false, "get2_mut called with identical indices"); + return (None, None); + } + + let ptr1 = &mut self.data[index1] as *mut Option; + let ptr2 = &mut self.data[index2] as *mut Option; + + // Safety: the elements at index1 and index2 are nowhere else borrowed mutably by function contract. + // And they are disjoint because of the check above. + unsafe { ((*ptr1).as_mut(), (*ptr2).as_mut()) } + } + + fn get3_mut>( + &mut self, + index1: Q, + index2: Q, + index3: Q, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>) { + let index1 = K::to_index(Some(index1.borrow())); + let index2 = K::to_index(Some(index2.borrow())); + let index3 = K::to_index(Some(index3.borrow())); + + if index1 == index2 || index1 == index3 || index2 == index3 { + debug_assert!(false, "get3_mut called with identical indices"); + return (None, None, None); + } + + let ptr1 = &mut self.data[index1] as *mut Option; + let ptr2 = &mut self.data[index2] as *mut Option; + let ptr3 = &mut self.data[index3] as *mut Option; + + // Safety: the elements at index1, index2 and index3 are nowhere else borrowed mutably by function contract. + // And they are disjoint because of the check above. + unsafe { ((*ptr1).as_mut(), (*ptr2).as_mut(), (*ptr3).as_mut()) } + } +} + /// This is a vector that can store up to N elements inline and will allocate on the heap if more are needed. #[derive(Debug)] pub struct Vec { @@ -403,6 +461,66 @@ impl Vec { } } + fn at_mut_unchecked(&mut self, index: usize) -> *mut T { + if index < N { + // Safety: the elements until self.len are initialized. + // The element at index is nowhere else borrowed mutably by function contract. + self.data[index].as_mut_ptr() + } else { + let index = index - N; + // Safety: the elements until self.len - N are initialized. + // The element at index is nowhere else borrowed mutably by function contract. + self.extra[index].as_mut_ptr() + } + } + + /// Get disjoint mutable references to the values at the given indices. + /// + /// `index1` - The first index. + /// `index2` - The second index. + /// + /// Returns `Some(&mut T, &mut T)` if the indices are in-bounds and disjoint, otherwise `None`. + pub fn at2_mut(&mut self, index1: usize, index2: usize) -> (Option<&mut T>, Option<&mut T>) { + if index1 == index2 { + debug_assert!(false, "at2_mut called with identical indices"); + return (None, None); + } + + let ptr1 = self.at_mut_unchecked(index1); + let ptr2 = self.at_mut_unchecked(index2); + + // Safety: the elements at index1 and index2 are nowhere else borrowed mutably by function contract. + // And they are disjoint because of the check above. + unsafe { (Some(&mut *ptr1), Some(&mut *ptr2)) } + } + + /// Get disjoint mutable references to the values at the given indices. + /// + /// `index1` - The first index. + /// `index2` - The second index. + /// `index3` - The third index. + /// + /// Returns `Some(&mut T, &mut T, &mut T)` if the indices are in-bounds and disjoint, otherwise `None`. + pub fn at3_mut( + &mut self, + index1: usize, + index2: usize, + index3: usize, + ) -> (Option<&mut T>, Option<&mut T>, Option<&mut T>) { + if index1 == index2 || index1 == index3 || index2 == index3 { + debug_assert!(false, "at3_mut called with identical indices"); + return (None, None, None); + } + + let ptr1 = self.at_mut_unchecked(index1); + let ptr2 = self.at_mut_unchecked(index2); + let ptr3 = self.at_mut_unchecked(index3); + + // Safety: the elements at index1, index2 and index3 are nowhere else borrowed mutably by function contract. + // And they are disjoint because of the check above. + unsafe { (Some(&mut *ptr1), Some(&mut *ptr2), Some(&mut *ptr3)) } + } + /// Swap the values at the given indices. /// /// `a` - The first index. @@ -465,7 +583,47 @@ impl Drop for Vec { } } -#[cfg(kani)] -mod verification { - use super::*; +impl Index for Vec { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + self.at(index).unwrap() + } +} + +impl IndexMut for Vec { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.at_mut(index).unwrap() + } +} + +impl Get for Vec { + type Output = T; + + fn get>(&self, index: Q) -> Option<&Self::Output> { + self.at(*index.borrow()) + } +} + +impl GetMut for Vec { + fn get_mut>(&mut self, index: Q) -> Option<&mut Self::Output> { + self.at_mut(*index.borrow()) + } + + fn get2_mut>( + &mut self, + index1: Q, + index2: Q, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { + self.at2_mut(*index1.borrow(), *index2.borrow()) + } + + fn get3_mut>( + &mut self, + index1: Q, + index2: Q, + index3: Q, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>) { + self.at3_mut(*index1.borrow(), *index2.borrow(), *index3.borrow()) + } } diff --git a/src/mem/rbtree.rs b/src/mem/rbtree.rs new file mode 100644 index 0000000..2f7a5f0 --- /dev/null +++ b/src/mem/rbtree.rs @@ -0,0 +1,1061 @@ +use std::{marker::PhantomData}; + +use crate::mem::traits::{Get, GetMut}; + +#[allow(dead_code)] +pub struct RbTree { + root: Option, + min: Option, + _tag: PhantomData, +} + +#[allow(dead_code)] +pub trait Linkable { + fn links(&self) -> &Links; + fn links_mut(&mut self) -> &mut Links; +} + +pub trait Compare { + fn cmp(&self, other: &Self) -> core::cmp::Ordering; +} + +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Links { + parent: Option, + left: Option, + right: Option, + color: Color, + _tag: PhantomData, +} + +#[allow(dead_code)] +impl Links { + pub fn new() -> Self { + Self { + parent: None, + left: None, + right: None, + color: Color::Red, + _tag: PhantomData, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Color { + Red, + Black, +} + +#[allow(dead_code)] +impl RbTree +{ + pub fn new() -> Self { + Self { + root: None, + min: None, + _tag: PhantomData, + } + } + + pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare,{ + let mut last = None; + + { + let node = storage.get(id).ok_or(())?; + let mut current = self.root; + + while let Some(current_id) = current { + last = current; + let current_node = storage.get(current_id).ok_or(())?; + let go_left = node.cmp(current_node) == core::cmp::Ordering::Less; + + current = if go_left { + current_node.links().left + } else { + current_node.links().right + }; + } + } + + { + let node = storage.get_mut(id).ok_or(())?.links_mut(); + node.parent = last; + node.left = None; + node.right = None; + node.color = Color::Red; + } + + match last { + None => self.root = Some(id), + Some(last_id) => { + if let (Some(node), Some(last)) = storage.get2_mut(id, last_id) { + if node.cmp(last) == core::cmp::Ordering::Less { + last.links_mut().left = Some(id); + } else { + last.links_mut().right = Some(id); + } + } + } + } + + if let Some(min_id) = self.min { + let node = storage.get(id).ok_or(())?; + let min_node = storage.get(min_id).ok_or(())?; + if node.cmp(min_node) == core::cmp::Ordering::Less { + self.min = Some(id); + } + } else { + self.min = Some(id); + } + + self.insert_fixup(id, storage) + } + + pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare { + let (node_left, node_right, node_parent, node_is_red) = { + let node = storage.get(id).ok_or(())?; + ( + node.links().left, + node.links().right, + node.links().parent, + matches!(node.links().color, Color::Red), + ) + }; + + let mut succ_was_red = node_is_red; + let child: Option; + let child_parent: Option; + + if node_left.is_none() { + child = node_right; + child_parent = node_parent; + + self.transplant(id, node_right, storage)?; + } else if node_right.is_none() { + child = node_left; + child_parent = node_parent; + + self.transplant(id, node_left, storage)?; + } else { + let right_id = node_right.ok_or(())?; + let succ = self.minimum(right_id, storage)?; + let succ_right = storage.get(succ).and_then(|n| n.links().right); + let succ_parent = storage.get(succ).and_then(|n| n.links().parent); + + succ_was_red = storage + .get(succ) + .map_or(false, |n| matches!(n.links().color, Color::Red)); + child = succ_right; + + if succ_parent == Some(id) { + child_parent = Some(succ); + } else { + self.transplant(succ, succ_right, storage)?; + + if let (Some(succ_node), Some(right_node)) = storage.get2_mut(succ, right_id) { + succ_node.links_mut().right = Some(right_id); + right_node.links_mut().parent = Some(succ); + } else { + return Err(()); + } + + child_parent = succ_parent; + } + + self.transplant(id, Some(succ), storage)?; + + let left_id = node_left.ok_or(())?; + + if let (Some(succ_node), Some(left_node)) = storage.get2_mut(succ, left_id) { + succ_node.links_mut().left = Some(left_id); + left_node.links_mut().parent = Some(succ); + } else { + return Err(()); + } + + if let Some(succ_node) = storage.get_mut(succ) { + succ_node.links_mut().color = if node_is_red { + Color::Red + } else { + Color::Black + }; + } else { + return Err(()); + } + } + + if !succ_was_red { + self.delete_fixup(child, child_parent, storage)?; + } + + if self.min == Some(id) { + self.min = match self.root { + Some(root_id) => Some(self.minimum(root_id, storage)?), + None => None, + }; + } + + Ok(()) + } + + pub fn min(&self) -> Option { + self.min + } + + fn insert_fixup + GetMut>(&mut self, mut id: T, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare, { + while let Some(parent) = storage.get(id).and_then(|n| n.links().parent) + && storage + .get(parent) + .map_or(false, |n| matches!(n.links().color, Color::Red)) + { + let grandparent = storage + .get(parent) + .and_then(|n| n.links().parent) + .ok_or(())?; + + // Is left child node + if storage + .get(grandparent) + .map_or(false, |n| n.links().left == Some(parent)) + { + // Uncle node must be the right child node + let uncle = storage.get(grandparent).and_then(|n| n.links().right); + + if let Some(uncle_id) = uncle + && storage + .get(uncle_id) + .map_or(false, |n| matches!(n.links().color, Color::Red)) + { + // Parent and uncle nodes are red + if let (Some(parent_node), Some(uncle_node), Some(grandparent_node)) = + storage.get3_mut(parent, uncle_id, grandparent) + { + parent_node.links_mut().color = Color::Black; + uncle_node.links_mut().color = Color::Black; + grandparent_node.links_mut().color = Color::Red; + } + id = grandparent; + } else { + // Uncle node is black + if storage + .get(parent) + .map_or(false, |n| n.links().right == Some(id)) + { + let old_parent = parent; + self.rotate_left(parent, id, storage)?; + id = old_parent; + } + + let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(())?; + let grandparent = storage + .get(parent) + .and_then(|n| n.links().parent) + .ok_or(())?; + + if let (Some(parent_node), Some(grandparent_node)) = + storage.get2_mut(parent, grandparent) + { + parent_node.links_mut().color = Color::Black; + grandparent_node.links_mut().color = Color::Red; + } + self.rotate_right(grandparent, parent, storage)?; + break; + } + } else { + // Uncle node must be the left child + let uncle = storage.get(grandparent).and_then(|n| n.links().left); + + if let Some(uncle_id) = uncle + && storage + .get(uncle_id) + .map_or(false, |n| matches!(n.links().color, Color::Red)) + { + // Parent and uncle nodes are red + if let (Some(parent_node), Some(uncle_node), Some(grandparent_node)) = + storage.get3_mut(parent, uncle_id, grandparent) + { + parent_node.links_mut().color = Color::Black; + uncle_node.links_mut().color = Color::Black; + grandparent_node.links_mut().color = Color::Red; + } + id = grandparent; + } else { + // Uncle node is black + if storage + .get(parent) + .map_or(false, |n| n.links().left == Some(id)) + { + let old_parent = parent; + self.rotate_right(parent, id, storage)?; + id = old_parent; + } + + let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(())?; + let grandparent = storage + .get(parent) + .and_then(|n| n.links().parent) + .ok_or(())?; + + if let (Some(parent_node), Some(grandparent_node)) = + storage.get2_mut(parent, grandparent) + { + parent_node.links_mut().color = Color::Black; + grandparent_node.links_mut().color = Color::Red; + } + self.rotate_left(grandparent, parent, storage)?; + break; + } + } + } + + if let Some(root_id) = self.root { + if let Some(root_node) = storage.get_mut(root_id) { + root_node.links_mut().color = Color::Black; + } + } + + Ok(()) + } + + fn delete_fixup + GetMut>( + &mut self, + mut id: Option, + mut parent: Option, + storage: &mut S, + ) -> Result<(), ()> + where >::Output: Linkable + Compare, { + let is_red = |node_id: Option, storage: &S| -> bool { + node_id + .and_then(|id| storage.get(id)) + .map_or(false, |n| matches!(n.links().color, Color::Red)) + }; + + let is_black = |node_id: Option, storage: &S| -> bool { !is_red(node_id, storage) }; + + while id != self.root && is_black(id, storage) { + let parent_id = parent.ok_or(())?; + + let is_left_child = storage + .get(parent_id) + .map_or(false, |n| n.links().left == id); + + if is_left_child { + let mut sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); + + if is_red(sibling_opt, storage) { + let sibling_id = sibling_opt.ok_or(())?; + // Color sibling node black and parent node red, rotate + if let (Some(sib), Some(par)) = storage.get2_mut(sibling_id, parent_id) { + sib.links_mut().color = Color::Black; + par.links_mut().color = Color::Red; + } else { + return Err(()); + } + self.rotate_left(parent_id, sibling_id, storage)?; + sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); + } + + // Sibling node is black + let sibling_id = sibling_opt.ok_or(())?; + let sib_left = storage.get(sibling_id).and_then(|n| n.links().left); + let sib_right = storage.get(sibling_id).and_then(|n| n.links().right); + + if is_black(sib_left, storage) && is_black(sib_right, storage) { + // Color sibling node red and move up + if let Some(sib) = storage.get_mut(sibling_id) { + sib.links_mut().color = Color::Red; + } else { + return Err(()); + } + id = Some(parent_id); + parent = storage.get(parent_id).and_then(|n| n.links().parent); + } else { + // Sibling's left node is red + if is_black(sib_right, storage) { + let sib_left_id = sib_left.ok_or(())?; + if let (Some(sib), Some(left)) = storage.get2_mut(sibling_id, sib_left_id) { + sib.links_mut().color = Color::Red; + left.links_mut().color = Color::Black; + } else { + return Err(()); + } + self.rotate_right(sibling_id, sib_left_id, storage)?; + sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); + } + + // Sibling's right child node is red + let sibling_id = sibling_opt.ok_or(())?; + let parent_is_red = storage + .get(parent_id) + .map_or(false, |n| matches!(n.links().color, Color::Red)); + + if let Some(sib) = storage.get_mut(sibling_id) { + sib.links_mut().color = if parent_is_red { + Color::Red + } else { + Color::Black + }; + } + if let Some(par) = storage.get_mut(parent_id) { + par.links_mut().color = Color::Black; + } + + let sib_right = storage.get(sibling_id).and_then(|n| n.links().right); + if let Some(sib_right_id) = sib_right { + if let Some(right) = storage.get_mut(sib_right_id) { + right.links_mut().color = Color::Black; + } + } + + self.rotate_left(parent_id, sibling_id, storage)?; + id = self.root; + break; + } + } else { + let mut sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); + + if is_red(sibling_opt, storage) { + let sibling_id = sibling_opt.ok_or(())?; + if let (Some(sib), Some(par)) = storage.get2_mut(sibling_id, parent_id) { + sib.links_mut().color = Color::Black; + par.links_mut().color = Color::Red; + } else { + return Err(()); + } + self.rotate_right(parent_id, sibling_id, storage)?; + sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); + } + + // Sibling node is black + let sibling_id = sibling_opt.ok_or(())?; + let sib_left = storage.get(sibling_id).and_then(|n| n.links().left); + let sib_right = storage.get(sibling_id).and_then(|n| n.links().right); + + if is_black(sib_left, storage) && is_black(sib_right, storage) { + if let Some(sib) = storage.get_mut(sibling_id) { + sib.links_mut().color = Color::Red; + } else { + return Err(()); + } + id = Some(parent_id); + parent = storage.get(parent_id).and_then(|n| n.links().parent); + } else { + // Sibling's right node is red + if is_black(sib_left, storage) { + let sib_right_id = sib_right.ok_or(())?; + if let (Some(sib), Some(right)) = storage.get2_mut(sibling_id, sib_right_id) + { + sib.links_mut().color = Color::Red; + right.links_mut().color = Color::Black; + } else { + return Err(()); + } + self.rotate_left(sibling_id, sib_right_id, storage)?; + sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); + } + + // Sibling's left child node is red + let sibling_id = sibling_opt.ok_or(())?; + let parent_is_red = storage + .get(parent_id) + .map_or(false, |n| matches!(n.links().color, Color::Red)); + + if let Some(sib) = storage.get_mut(sibling_id) { + sib.links_mut().color = if parent_is_red { + Color::Red + } else { + Color::Black + }; + } + if let Some(par) = storage.get_mut(parent_id) { + par.links_mut().color = Color::Black; + } + + let sib_left = storage.get(sibling_id).and_then(|n| n.links().left); + if let Some(sib_left_id) = sib_left { + if let Some(left) = storage.get_mut(sib_left_id) { + left.links_mut().color = Color::Black; + } + } + + self.rotate_right(parent_id, sibling_id, storage)?; + id = self.root; + break; + } + } + } + + // Color the root node black + if let Some(id) = id { + if let Some(node) = storage.get_mut(id) { + node.links_mut().color = Color::Black; + } + } + + Ok(()) + } + + fn minimum>(&self, mut id: T, storage: &S) -> Result + where >::Output: Linkable + Compare, { + loop { + let left = storage.get(id).ok_or(())?.links().left; + match left { + Some(left_id) => id = left_id, + None => return Ok(id), + } + } + } + + fn transplant + GetMut>(&mut self, u: T, v: Option, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare, { + let u_parent = storage.get(u).and_then(|n| n.links().parent); + + match u_parent { + None => self.root = v, + Some(parent_id) => { + if let Some(parent_node) = storage.get_mut(parent_id) { + if parent_node.links().left == Some(u) { + parent_node.links_mut().left = v; + } else { + parent_node.links_mut().right = v; + } + } else { + return Err(()); + } + } + } + + if let Some(v_id) = v { + if let Some(v_node) = storage.get_mut(v_id) { + v_node.links_mut().parent = u_parent; + } else { + return Err(()); + } + } + + Ok(()) + } + + fn rotate_right + GetMut>(&mut self, pivot: T, left: T, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare, { + if pivot == left { + return Err(()); + } + + let (right, parent) = + if let (Some(pivot_node), Some(left_node)) = storage.get2_mut(pivot, left) { + // Add left child's right subtree as pivot's left subtree + pivot_node.links_mut().left = left_node.links().right; + + // Add pivot's parent as left child's parent + left_node.links_mut().parent = pivot_node.links().parent; + + let old_right = left_node.links().right; + + // Set pivot as the right child of left child + left_node.links_mut().right = Some(pivot); + + let old_parent = pivot_node.links().parent; + + // Set pivot's parent to left child + pivot_node.links_mut().parent = Some(left); + + (old_right, old_parent) + } else { + return Err(()); + }; + + if let Some(right_id) = right { + if let Some(right_node) = storage.get_mut(right_id) { + right_node.links_mut().parent = Some(pivot); + } + } + + match parent { + None => self.root = Some(left), + Some(parent_id) => { + if let Some(parent_node) = storage.get_mut(parent_id) { + if parent_node.links().left == Some(pivot) { + parent_node.links_mut().left = Some(left); + } else { + parent_node.links_mut().right = Some(left); + } + } else { + return Err(()); + } + } + } + + Ok(()) + } + + fn rotate_left + GetMut>(&mut self, pivot: T, right: T, storage: &mut S) -> Result<(), ()> + where >::Output: Linkable + Compare, { + if pivot == right { + return Err(()); + } + + let (left, parent) = + if let (Some(pivot_node), Some(right_node)) = storage.get2_mut(pivot, right) { + // Add right child's left subtree as pivot's right subtree + pivot_node.links_mut().right = right_node.links().left; + + // Add pivot's parent as right child's parent + right_node.links_mut().parent = pivot_node.links().parent; + + let old_left = right_node.links().left; + + // Set pivot as the left child of right child + right_node.links_mut().left = Some(pivot); + + let old_parent = pivot_node.links().parent; + + // Set pivot's parent to right child + pivot_node.links_mut().parent = Some(right); + + (old_left, old_parent) + } else { + return Err(()); + }; + + if let Some(left_id) = left { + if let Some(left_node) = storage.get_mut(left_id) { + left_node.links_mut().parent = Some(pivot); + } + } + + match parent { + None => self.root = Some(right), + Some(parent_id) => { + if let Some(parent_node) = storage.get_mut(parent_id) { + if parent_node.links().left == Some(pivot) { + parent_node.links_mut().left = Some(right); + } else { + parent_node.links_mut().right = Some(right); + } + } else { + return Err(()); + } + } + } + Ok(()) + } +} + +// TESTING ------------------------------------------------------------------------------------------------------------ + +#[cfg(test)] +mod tests { + use super::*; + use crate::mem::traits::{Get, GetMut}; + use std::borrow::Borrow; + use std::collections::HashSet; + + struct Tree; + + struct Node { + key: i32, + links: Links, + } + + impl Node { + fn new(key: i32) -> Self { + Self { + key, + links: Links::new(), + } + } + } + + impl Compare for Node { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.key.cmp(&other.key) + } + } + + impl Linkable for Node { + fn links(&self) -> &Links { + &self.links + } + + fn links_mut(&mut self) -> &mut Links { + &mut self.links + } + } + + struct NodeStore { + nodes: Vec, + } + + impl NodeStore { + fn new(keys: &[i32]) -> Self { + Self { + nodes: keys.iter().copied().map(Node::new).collect(), + } + } + } + + impl Get for NodeStore { + type Output = Node; + + fn get>(&self, index: K) -> Option<&Self::Output> { + self.nodes.get(*index.borrow()) + } + } + + impl GetMut for NodeStore { + fn get_mut>(&mut self, index: K) -> Option<&mut Self::Output> { + self.nodes.get_mut(*index.borrow()) + } + + fn get2_mut>( + &mut self, + index1: K, + index2: K, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { + if *index1.borrow() == *index2.borrow() { + return (None, None); + } + + let ptr = self.nodes.as_ptr(); + + return unsafe { + ( + Some(&mut *(ptr.add(*index1.borrow()) as *mut Self::Output)), + Some(&mut *(ptr.add(*index2.borrow()) as *mut Self::Output)), + ) + }; + } + + fn get3_mut>( + &mut self, + index1: K, + index2: K, + index3: K, + ) -> ( + Option<&mut Self::Output>, + Option<&mut Self::Output>, + Option<&mut Self::Output>, + ) { + if *index1.borrow() == *index2.borrow() + || *index1.borrow() == *index3.borrow() + || *index2.borrow() == *index3.borrow() + { + return (None, None, None); + } + + let ptr = self.nodes.as_ptr(); + return unsafe { + ( + Some(&mut *(ptr.add(*index1.borrow()) as *mut Self::Output)), + Some(&mut *(ptr.add(*index2.borrow()) as *mut Self::Output)), + Some(&mut *(ptr.add(*index3.borrow()) as *mut Self::Output)), + ) + }; + } + } + + fn validate_tree(tree: &RbTree, store: &NodeStore, expected: &[i32]) { + let mut visited = HashSet::new(); + + if let Some(root_id) = tree.root { + let root = store.get(root_id).expect("root missing from store"); + assert!(matches!(root.links().color, Color::Black)); + assert_eq!(root.links().parent, None); + } + + let (count, _) = validate_node(tree.root, store, &mut visited, expected); + assert_eq!(count, expected.len()); + + if !expected.is_empty() { + let min = tree_min_key(tree, store).expect("non-empty tree must contain a min."); + assert_eq!(min, expected[0]); + } + } + + fn tree_min_key(tree: &RbTree, store: &NodeStore) -> Option { + tree.min().map(|id| store.get(id).expect("min missing").key) + } + + fn validate_node( + id: Option, + store: &NodeStore, + visited: &mut HashSet, + expected: &[i32], + ) -> (usize, usize) { + let Some(id) = id else { + return (0, 1); + }; + + assert!(visited.insert(id)); + + let node = store.get(id).expect("node missing from store"); + + let left = node.links().left; + let right = node.links().right; + + if matches!(node.links().color, Color::Red) { + if let Some(left_id) = left { + let left_node = store.get(left_id).expect("left missing"); + assert!(matches!(left_node.links().color, Color::Black)); + } + if let Some(right_id) = right { + let right_node = store.get(right_id).expect("right missing"); + assert!(matches!(right_node.links().color, Color::Black)); + } + } + + if let Some(left_id) = left { + let left_node = store.get(left_id).expect("left missing"); + assert_eq!(left_node.links().parent, Some(id)); + } + if let Some(right_id) = right { + let right_node = store.get(right_id).expect("right missing"); + assert_eq!(right_node.links().parent, Some(id)); + } + + let (left_count, left_bh) = validate_node(left, store, visited, &expected); + assert_eq!( + node.key, expected[left_count], + "expected key {}, found {}", + expected[left_count], node.key + ); + let (right_count, right_bh) = + validate_node(right, store, visited, &expected[1 + left_count..]); + + assert_eq!( + left_bh, right_bh, + "black height mismatch at node with key {}", + node.key + ); + + let self_bh = if matches!(node.links().color, Color::Black) { + left_bh + 1 + } else { + left_bh + }; + + (1 + left_count + right_count, self_bh) + } + + fn lcg(seed: &mut u64) -> u64 { + *seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); + *seed + } + + fn shuffle(ids: &mut [usize]) { + let mut seed = 0x6b8b_4567_9a1c_def0u64; + for i in (1..ids.len()).rev() { + let j = (lcg(&mut seed) % (i as u64 + 1)) as usize; + ids.swap(i, j); + } + } + + #[test] + fn insert_validates() { + let keys: Vec = (0..200).collect(); + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + let mut order: Vec = (0..keys.len()).collect(); + + shuffle(&mut order); + for id in order { + tree.insert(id, &mut store).unwrap(); + } + + validate_tree(&tree, &store, &keys); + } + + #[test] + fn min_updates_on_insert_and_remove() { + let keys = vec![10, 5, 15, 3, 7, 12, 18, 1, 6]; + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + + for id in 0..keys.len() { + tree.insert(id, &mut store).unwrap(); + } + + let mut sorted_keys = keys.clone(); + sorted_keys.sort(); + + validate_tree(&tree, &store, &sorted_keys); + assert_eq!(tree_min_key(&tree, &store), Some(1)); + + // Remove index 7 (key=1) + tree.remove(7, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 1); + validate_tree(&tree, &store, &sorted_keys); + assert_eq!(tree_min_key(&tree, &store), Some(3)); + + // Remove index 8 (key=6) + tree.remove(8, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 6); + validate_tree(&tree, &store, &sorted_keys); + assert_eq!(tree_min_key(&tree, &store), Some(3)); + + // Remove index 3 (key=3) + tree.remove(3, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 3); + validate_tree(&tree, &store, &sorted_keys); + assert_eq!(tree_min_key(&tree, &store), Some(5)); + } + + #[test] + fn remove_leaf_one_child_two_children() { + let keys = vec![10, 5, 15, 3, 7, 12, 18, 1, 6]; + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + + for id in 0..keys.len() { + tree.insert(id, &mut store).unwrap(); + } + + let mut sorted_keys = keys.clone(); + sorted_keys.sort(); + validate_tree(&tree, &store, &sorted_keys); + + // Remove node at index 4 (key=7) + tree.remove(4, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 7); + validate_tree(&tree, &store, &sorted_keys); + + // Remove node at index 3 (key=3) + tree.remove(3, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 3); + validate_tree(&tree, &store, &sorted_keys); + + // Remove node at index 7 (key=1) + tree.remove(7, &mut store).unwrap(); + sorted_keys.retain(|&x| x != 1); + validate_tree(&tree, &store, &sorted_keys); + } + + #[test] + fn remove_root_with_two_children() { + let keys = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + + for id in 0..keys.len() { + tree.insert(id, &mut store).unwrap(); + } + + let mut sorted_keys: Vec = keys.to_vec(); + sorted_keys.sort(); + validate_tree(&tree, &store, &sorted_keys); + + let root_id = tree.root.expect("root missing"); + let root_key = store.get(root_id).expect("root missing").key; + + tree.remove(root_id, &mut store).unwrap(); + sorted_keys.retain(|&x| x != root_key); + validate_tree(&tree, &store, &sorted_keys); + } + + #[test] + fn remove_all_nodes() { + let keys: Vec = (0..128).collect(); + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + let mut order: Vec = (0..keys.len()).collect(); + shuffle(&mut order); + + for id in &order { + tree.insert(*id, &mut store).unwrap(); + } + + let mut remaining_keys = keys.clone(); + validate_tree(&tree, &store, &remaining_keys); + + for id in order { + let removed_key = keys[id]; + tree.remove(id, &mut store).unwrap(); + remaining_keys.retain(|&k| k != removed_key); + validate_tree(&tree, &store, &remaining_keys); + } + + assert_eq!(tree.root, None); + } + + #[test] + fn interleaved_operations() { + let keys: Vec = (0..100).collect(); + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + let mut order: Vec = (0..keys.len()).collect(); + shuffle(&mut order); + + // Build initial tree with 50 nodes + let mut active_keys: Vec = Vec::new(); + for id in order.iter().take(50) { + tree.insert(*id, &mut store).unwrap(); + active_keys.push(keys[*id]); + } + active_keys.sort(); + validate_tree(&tree, &store, &active_keys); + + // Alternate: remove oldest, insert new + for i in 0..50 { + let removed_key = keys[order[i]]; + tree.remove(order[i], &mut store).unwrap(); + active_keys.retain(|&k| k != removed_key); + validate_tree(&tree, &store, &active_keys); + + tree.insert(order[50 + i], &mut store).unwrap(); + active_keys.push(keys[order[50 + i]]); + active_keys.sort(); + validate_tree(&tree, &store, &active_keys); + } + } + + #[test] + fn stress_test() { + let keys: Vec = (0..500).collect(); + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + let mut order: Vec = (0..keys.len()).collect(); + shuffle(&mut order); + + let mut seed = 0x6b8b_4567_9a1c_def0u64; + let mut active_nodes = Vec::new(); + let mut available_nodes = order.clone(); + + for _ in 0..10000 { + let do_insert = if active_nodes.is_empty() { + true + } else if available_nodes.is_empty() { + false + } else { + (lcg(&mut seed) % 10) < 7 + }; + + if do_insert { + let idx = (lcg(&mut seed) as usize) % available_nodes.len(); + let node_id = available_nodes.swap_remove(idx); + tree.insert(node_id, &mut store).unwrap(); + active_nodes.push(node_id); + } else { + let idx = (lcg(&mut seed) as usize) % active_nodes.len(); + let node_id = active_nodes.swap_remove(idx); + tree.remove(node_id, &mut store).unwrap(); + available_nodes.push(node_id); + } + + let mut expected_keys: Vec = active_nodes.iter().map(|&id| keys[id]).collect(); + expected_keys.sort(); + validate_tree(&tree, &store, &expected_keys); + } + + let mut expected_keys: Vec = active_nodes.iter().map(|&id| keys[id]).collect(); + expected_keys.sort(); + validate_tree(&tree, &store, &expected_keys); + } +} + +// END TESTING diff --git a/src/mem/traits.rs b/src/mem/traits.rs new file mode 100644 index 0000000..cc02d85 --- /dev/null +++ b/src/mem/traits.rs @@ -0,0 +1,24 @@ +use core::borrow::Borrow; + +pub trait Get { + type Output: ?Sized; + + fn get>(&self, index: K) -> Option<&Self::Output>; +} + +pub trait GetMut: Get { + fn get_mut>(&mut self, index: K) -> Option<&mut Self::Output>; + + // Getting multiple disjoint mutable references at once + fn get2_mut>(&mut self, index1: K, index2: K) -> (Option<&mut Self::Output>, Option<&mut Self::Output>); + fn get3_mut>(&mut self, index1: K, index2: K, index3: K) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>); +} + +pub trait ToIndex { + fn to_index>(index: Option) -> usize; +} + +pub trait Project

{ + fn project(&self) -> Option<&P>; + fn project_mut(&mut self) -> Option<&mut P>; +} \ No newline at end of file diff --git a/src/mem/view.rs b/src/mem/view.rs new file mode 100644 index 0000000..c8bd4bc --- /dev/null +++ b/src/mem/view.rs @@ -0,0 +1,63 @@ +use core::borrow::Borrow; +use std::marker::PhantomData; + +use crate::mem::traits::{Get, GetMut, Project, ToIndex}; + +pub struct ViewMut<'a, K: ?Sized + ToIndex, P, S: GetMut> +where + S::Output: Project

, +{ + data: &'a mut S, + _k: PhantomData, + _proj: PhantomData

, +} + +impl<'a, K: ?Sized + ToIndex, P, S: GetMut> ViewMut<'a, K, P, S> +where + S::Output: Project

, +{ + pub fn new(data: &'a mut S) -> Self { + Self { + data, + _k: PhantomData, + _proj: PhantomData, + } + } +} + +impl<'a, K: ?Sized + ToIndex, P, S: GetMut> Get for ViewMut<'a, K, P, S> +where + S::Output: Project

, +{ + type Output = P; + + fn get>(&self, idx: Q) -> Option<&P> { + self.data.get(idx).and_then(Project::project) + } +} + +impl<'a, K: ?Sized + ToIndex, P, S: GetMut> GetMut for ViewMut<'a, K, P, S> +where + S::Output: Project

, +{ + fn get_mut>(&mut self, idx: Q) -> Option<&mut P> { + self.data.get_mut(idx).and_then(Project::project_mut) + } + + fn get2_mut>(&mut self, idx1: Q, idx2: Q) -> (Option<&mut P>, Option<&mut P>) { + let (a, b) = self.data.get2_mut(idx1, idx2); + ( + a.and_then(Project::project_mut), + b.and_then(Project::project_mut), + ) + } + + fn get3_mut>(&mut self, idx1: Q, idx2: Q, idx3: Q) -> (Option<&mut P>, Option<&mut P>, Option<&mut P>) { + let (a, b, c) = self.data.get3_mut(idx1, idx2, idx3); + ( + a.and_then(Project::project_mut), + b.and_then(Project::project_mut), + c.and_then(Project::project_mut), + ) + } +} \ No newline at end of file diff --git a/src/sched.rs b/src/sched.rs index 70ae413..72ce054 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,18 +1,83 @@ //! This module provides access to the scheduler. -pub mod scheduler; +pub mod rt; +//pub mod scheduler; pub mod task; pub mod thread; use hal::Schedable; -use crate::utils::KernelError; +use crate::mem::{array::IndexMap, rbtree::RbTree, view::ViewMut}; + +type ThreadMap = IndexMap; + +pub struct Scheduler { + threads: ThreadMap, + rt_scheduler: rt::Scheduler, + + wakeup: RbTree, + + last_tick: u64, +} + +impl Scheduler { + pub fn new() -> Self { + Self { + threads: IndexMap::new(), + rt_scheduler: rt::Scheduler::new(), + wakeup: RbTree::new(), + last_tick: 0, + } + } + + pub fn enqueue(&mut self, thread: thread::Thread) { + let uid = thread.uid(); + let rt = thread.rt_server().is_some(); + self.threads.insert(&thread.uid(), thread); + + if rt { + let mut view = ViewMut::>::new( + &mut self.threads, + ); + self.rt_scheduler.enqueue(uid, &mut view); + } + } + + pub fn do_sched(&mut self, now: u64, old: Option) -> Option { + let dt = now - self.last_tick; + self.last_tick = now; + + if let Some(old) = old { + let mut view = rt::ServerView::::new(&mut self.threads); + // If this is not a real-time thread, this will just do nothing. + self.rt_scheduler.put(old, dt, &mut view); + + // TODO: thread is still enqueued. Dequeue if blocked or sleeping and put to the respective tree/list. + // If it exited remove it completely. + } + + let mut view = rt::ServerView::::new(&mut self.threads); + self.rt_scheduler.pick(now, &mut view) + } + + pub fn dequeue(&mut self, uid: thread::UId) -> Option { + let mut view = rt::ServerView::::new(&mut self.threads); + // If this is not a real-time thread, this will just do nothing. + self.rt_scheduler.dequeue(uid, &mut view); + + self.threads.remove(&uid) + } +} /// Reschedule the tasks. pub fn reschedule() { hal::Machine::trigger_reschedule(); } +/* + + + /// Create a new task. /// /// `desc` - The task descriptor. @@ -54,3 +119,5 @@ pub fn tick_scheduler() -> bool { scheduler::SCHEDULER.lock().tick() } + + */ diff --git a/src/sched/rt.rs b/src/sched/rt.rs new file mode 100644 index 0000000..c6c6b00 --- /dev/null +++ b/src/sched/rt.rs @@ -0,0 +1,42 @@ +use crate::{mem::{rbtree::RbTree, traits::{Get, GetMut}, view::ViewMut}, sched::{ThreadMap, thread::{self}}}; + +pub struct Scheduler { + edf: RbTree, +} + +pub type ServerView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::RtServer, ThreadMap>; + +impl Scheduler { + pub fn new() -> Self { + Self { + edf: RbTree::new(), + } + } + + pub fn enqueue(&mut self, uid: thread::UId, storage: &mut ServerView) { + self.edf.insert(uid, storage); + } + + pub fn put(&mut self, uid: thread::UId, dt: u64, storage: &mut ServerView) { + if let Some(server) = storage.get_mut(uid) { + server.consume(dt); + } + } + + pub fn pick(&mut self, now: u64, storage: &mut ServerView) -> Option { + let id = self.edf.min()?; + + if storage.get(id)?.budget() == 0 { + self.edf.remove(id, storage); + storage.get_mut(id)?.replenish(now); + self.edf.insert(id, storage); + } + + // Insert updated the min cache. + self.edf.min() + } + + pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) { + self.edf.remove(uid, storage); + } +} \ No newline at end of file diff --git a/src/sched/task.rs b/src/sched/task.rs index 7f6deb8..9f5388b 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -53,6 +53,8 @@ impl From for usize { } } + + /// Descibes a task. pub struct TaskDescriptor { /// The size of the memory that the task requires. @@ -68,8 +70,6 @@ pub struct Task { memory: TaskMemory, /// The counter for the thread ids. tid_cntr: usize, - /// The threads associated with the task. - threads: mem::array::Vec, } impl Task { @@ -80,13 +80,11 @@ impl Task { /// Returns a new task if the task was created successfully, or an error if the task could not be created. pub fn new(memory_size: usize, id: TaskId) -> Result { let memory = TaskMemory::new(memory_size)?; - let threads = mem::array::Vec::new(); Ok(Self { id, memory, tid_cntr: 0, - threads, }) } @@ -187,3 +185,5 @@ impl Drop for TaskMemory { unsafe { mem::free(self.begin, self.size) }; } } + + diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 647d5b9..7a814b4 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -4,18 +4,19 @@ use core::{borrow::Borrow, ffi::c_void}; use hal::Stack; use hal::stack::Stacklike; +use macros::TaggedLinks; -use crate::{mem::array::IndexMap, sched::task::TaskId, utils::KernelError}; +use crate::{mem::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, sched::task::TaskId, utils::KernelError}; /// Id of a task. This is only unique within a Task. #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] -pub struct ThreadId { +pub struct Id { id: usize, owner: TaskId, } #[allow(dead_code)] -impl ThreadId { +impl Id { pub fn new(id: usize, owner: TaskId) -> Self { Self { id, owner } } @@ -28,78 +29,72 @@ impl ThreadId { self.owner } - pub fn get_uid(&self, uid: usize) -> ThreadUId { - ThreadUId { uid, tid: *self } + pub fn get_uid(&self, uid: usize) -> UId { + UId { uid, tid: *self } } } /// Unique identifier for a thread. Build from TaskId and ThreadId. #[derive(Clone, Copy, Debug)] #[allow(dead_code)] -pub struct ThreadUId { +pub struct UId { uid: usize, - tid: ThreadId, + tid: Id, } #[allow(dead_code)] -impl ThreadUId { - pub fn tid(&self) -> ThreadId { +impl UId { + pub fn tid(&self) -> Id { self.tid } } -impl PartialEq for ThreadUId { +impl PartialEq for UId { fn eq(&self, other: &Self) -> bool { self.uid == other.uid } } -impl Eq for ThreadUId {} +impl Eq for UId {} -impl Borrow for ThreadUId { - fn borrow(&self) -> &usize { - &self.uid +impl Into for UId { + fn into(self) -> usize { + self.uid } } -impl Default for ThreadUId { +impl Default for UId { fn default() -> Self { Self { uid: 0, - tid: ThreadId::new(0, TaskId::User(0)), + tid: Id::new(0, TaskId::User(0)), } } } -impl PartialOrd for ThreadUId { +impl PartialOrd for UId { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for ThreadUId { +impl Ord for UId { fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.uid.cmp(&other.uid) } } +impl ToIndex for UId { + fn to_index>(idx: Option) -> usize { + idx.as_ref().map_or(0, |k| k.borrow().uid) + } +} + // ------------------------------------------------------------------------- -pub struct ThreadDescriptor { - pub tid: ThreadId, +pub struct Descriptor { + pub tid: Id, pub stack: Stack, - pub timing: Timing, -} - -/// The timing information for a thread. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Timing { - /// The period of the thread after which it should run again. - pub period: usize, - /// The deadline of the thread. - pub deadline: usize, - /// The execution time of the thread. (How much cpu time it needs) - pub exec_time: usize, } /// The state of a thread. @@ -114,22 +109,98 @@ pub enum RunState { Waits, } -#[derive(Debug)] -pub struct ThreadState { +#[derive(Debug, Clone, Copy)] +pub struct State { run_state: RunState, stack: Stack, } +#[derive(Debug, Clone, Copy)] +#[derive(TaggedLinks)] +pub struct RtServer { + budget: u64, + total_budget: u64, + + reservation: u64, + deadline: u64, + + // Back-reference to the thread uid. + uid: UId, + + /// Real-time tree links for the server. + #[rbtree(tag = RtTree, idx = UId)] + _rt_links: rbtree::Links, +} + +impl RtServer { + pub fn new(budget: u64, reservation: u64, deadline: u64, uid: UId) -> Self { + Self { + budget, + total_budget: budget, + reservation, + deadline, + uid, + _rt_links: rbtree::Links::new(), + } + } + + pub fn budget(&self) -> u64 { + self.budget + } + + pub fn replenish(&mut self, now: u64) { + let next = self.deadline + self.reservation; + self.deadline = next.max(now + self.reservation); + self.budget = self.total_budget; + } + + pub fn consume(&mut self, dt: u64) { + if self.budget >= dt { + self.budget -= dt; + } else { + self.budget = 0; + } + } + + pub fn deadline(&self) -> u64 { + self.deadline + } + + pub fn uid(&self) -> UId { + self.uid + } +} + +impl Compare for RtServer { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + let ord = self.deadline.cmp(&other.deadline); + + if ord == core::cmp::Ordering::Equal { + self.uid.cmp(&other.uid) + } else { + ord + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct WakupTree; +#[derive(Debug, Clone, Copy)] +pub struct RtTree; + /// The struct representing a thread. -#[derive(Debug)] -#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +#[derive(TaggedLinks)] pub struct Thread { /// The current state of the thread. - state: ThreadState, - /// The timing constraints of the thread. - timing: Timing, + state: State, /// The unique identifier of the thread. - tuid: ThreadUId, + uid: UId, + /// If the thread is real-time, its contains a constant bandwidth server. + rt_server: Option, + /// Wakup tree links for the thread. + #[rbtree(tag = WakupTree, idx = UId)] + _wakeup_links: rbtree::Links, } #[allow(dead_code)] @@ -137,74 +208,55 @@ impl Thread { /// Create a new thread. /// /// `stack` - The stack of the thread. - /// `timing` - The timing constraints of the thread. /// /// Returns a new thread. - fn new(tuid: ThreadUId, stack: Stack, timing: Timing) -> Self { + fn new(uid: UId, stack: Stack) -> Self { Self { - state: ThreadState { + state: State { run_state: RunState::Ready, stack, }, - timing, - tuid, + uid, + rt_server: None, + _wakeup_links: rbtree::Links::new(), } } - pub fn update_sp(&mut self, sp: *mut c_void) -> Result<(), KernelError> { - let sp = self.state.stack.create_sp(sp)?; + pub fn save_ctx(&mut self, ctx: *mut c_void) -> Result<(), KernelError> { + let sp = self.state.stack.create_sp(ctx)?; self.state.stack.set_sp(sp); Ok(()) } - pub fn update_run_state(&mut self, state: RunState) { + pub fn set_run_state(&mut self, state: RunState) { self.state.run_state = state; } - pub fn timing(&self) -> &Timing { - &self.timing + pub fn rt_server(&self) -> Option<&RtServer> { + self.rt_server.as_ref() } - pub fn sp(&self) -> *mut c_void { + pub fn ctx(&self) -> *mut c_void { self.state.stack.sp() } - pub fn tuid(&self) -> ThreadUId { - self.tuid + pub fn uid(&self) -> UId { + self.uid } } -#[derive(Debug)] -pub struct ThreadMap { - map: IndexMap, -} - -#[allow(dead_code)] -impl ThreadMap { - pub const fn new() -> Self { - Self { - map: IndexMap::new(), - } - } - - pub fn create(&mut self, desc: ThreadDescriptor) -> Result { - let idx = self.map.find_empty().ok_or(KernelError::OutOfMemory)?; - let tuid = desc.tid.get_uid(idx); - let thread = Thread::new(tuid, desc.stack, desc.timing); - - self.map.insert(&tuid, thread)?; - Ok(tuid) - } - - pub fn get_mut(&mut self, id: &ThreadUId) -> Option<&mut Thread> { - self.map.get_mut(id) +impl Compare for Thread { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.uid.cmp(&other.uid) } +} - pub fn get(&self, id: &ThreadUId) -> Option<&Thread> { - self.map.get(id) +impl Project for Thread { + fn project(&self) -> Option<&RtServer> { + self.rt_server.as_ref() } - pub fn remove(&mut self, id: &ThreadUId) -> Option { - self.map.remove(id) + fn project_mut(&mut self) -> Option<&mut RtServer> { + self.rt_server.as_mut() } -} +} \ No newline at end of file diff --git a/src/syscalls/tasks.rs b/src/syscalls/tasks.rs index 7bd7b5a..2a6d68d 100644 --- a/src/syscalls/tasks.rs +++ b/src/syscalls/tasks.rs @@ -2,6 +2,7 @@ use core::ffi::c_int; +/* use crate::sched; use macros::syscall_handler; @@ -29,4 +30,4 @@ fn syscall_exec(entry: usize) -> c_int { .and_then(|task| sched::create_thread(task, entry, None, timing)) .map(|_| 0) .unwrap_or(-1) -} +}*/ diff --git a/src/utils.rs b/src/utils.rs index 8d4144f..9fa7644 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,6 +2,8 @@ #![cfg_attr(feature = "nightly", feature(likely_unlikely))] use core::fmt::Debug; +use core::ptr::NonNull; +use core::mem::offset_of; /// These two definitions are copied from https://github.com/rust-lang/hashbrown #[cfg(not(feature = "nightly"))] @@ -11,6 +13,8 @@ pub(crate) use core::convert::{identity as likely, identity as unlikely}; #[cfg(feature = "nightly")] pub(crate) use core::hint::{likely, unlikely}; + + /// This is a macro that is used to panic when a bug is detected. /// It is similar to the BUG() macro in the Linux kernel. Link: [https://www.kernel.org/]() #[macro_export] From bae03f3f66b8fb5521604e9be30902839209078a Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:23:32 +0000 Subject: [PATCH 02/28] remove me --- .cargo/config.toml | 4 +- Cargo.lock | 1 + Cargo.toml | 2 + config.toml | 10 ++ machine/api/src/lib.rs | 2 +- machine/api/src/mem.rs | 48 +++++++ machine/api/src/{ => mem}/stack.rs | 7 +- machine/arm/src/sched.rs | 3 + macros/src/tree.rs | 6 +- src/dispatch.rs | 14 ++ src/{sched => dispatch}/task.rs | 101 +++++-------- src/lib.rs | 4 +- src/mem.rs | 14 +- src/mem/pfa.rs | 57 ++++++++ src/mem/pfa/bitset.rs | 219 +++++++++++++++++++++++++++++ src/mem/vmm.rs | 66 +++++++++ src/mem/vmm/nommu.rs | 87 ++++++++++++ src/sched.rs | 6 +- src/sched/rt.rs | 2 +- src/sched/thread.rs | 2 +- src/types.rs | 8 ++ src/{mem => types}/array.rs | 27 ++-- src/{mem => types}/boxed.rs | 46 +----- src/{mem => types}/heap.rs | 0 src/{mem => types}/pool.rs | 0 src/{mem => types}/queue.rs | 0 src/{mem => types}/rbtree.rs | 6 +- src/{mem => types}/traits.rs | 0 src/{mem => types}/view.rs | 4 +- src/utils.rs | 2 + 30 files changed, 602 insertions(+), 146 deletions(-) create mode 100644 config.toml create mode 100644 machine/api/src/mem.rs rename machine/api/src/{ => mem}/stack.rs (94%) create mode 100644 src/dispatch.rs rename src/{sched => dispatch}/task.rs (64%) create mode 100644 src/mem/pfa.rs create mode 100644 src/mem/pfa/bitset.rs create mode 100644 src/mem/vmm.rs create mode 100644 src/mem/vmm/nommu.rs create mode 100644 src/types.rs rename src/{mem => types}/array.rs (97%) rename src/{mem => types}/boxed.rs (85%) rename src/{mem => types}/heap.rs (100%) rename src/{mem => types}/pool.rs (100%) rename src/{mem => types}/queue.rs (100%) rename src/{mem => types}/rbtree.rs (99%) rename src/{mem => types}/traits.rs (100%) rename src/{mem => types}/view.rs (94%) diff --git a/.cargo/config.toml b/.cargo/config.toml index b73ed2c..0a1e750 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,10 +7,10 @@ xtask = "--config xtasks/.cargo/config.toml run -p xtask --release --" [build] target = "host-tuple" -[target] - [target.'cfg(target_os = "none")'] rustflags = ["-C", "link-arg=--entry=main",] +[target] + [target.thumbv7em-none-eabi] rustflags = ["-C", "relocation-model=ropi-rwpi"] diff --git a/Cargo.lock b/Cargo.lock index 9f3703b..d1469e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1090,6 +1090,7 @@ name = "osiris" version = "0.1.0" dependencies = [ "bindgen 0.69.5", + "bitflags", "cbindgen", "cfg_aliases", "dtgen", diff --git a/Cargo.toml b/Cargo.toml index 38c8641..b6d40be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,12 @@ hal = { package = "hal-select", path = "machine/select" } macros = { path = "macros" } interface = { path = "interface" } envparse = "0.1.0" +bitflags = "2.10.0" [dev-dependencies] # This is a host-compatible HAL which will be used for running tests and verification on the host. hal-testing = { path = "machine/testing", features = [] } +rand = "0.8.5" [target.'cfg(kani_ra)'.dependencies] kani = { git = "https://github.com/model-checking/kani" } diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..519b669 --- /dev/null +++ b/config.toml @@ -0,0 +1,10 @@ +[env] +OSIRIS_ARM_HAL = "stm32l4xx" +OSIRIS_ARM_STM32L4XX_VARIANT = "r5zi" +OSIRIS_DEBUG_UART = "UART5" +OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" +OSIRIS_TUNING_ENABLEFPU = "false" +OSIRIS_STACKPAGES = "1" + +[build] +target = "thumbv7em-none-eabi" diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index a6330a6..36860bc 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -2,7 +2,7 @@ use core::{fmt::Display, ops::Range}; -pub mod stack; +pub mod mem; #[derive(Default, Debug, PartialEq, Eq, Clone)] pub enum Error { diff --git a/machine/api/src/mem.rs b/machine/api/src/mem.rs new file mode 100644 index 0000000..10c8b07 --- /dev/null +++ b/machine/api/src/mem.rs @@ -0,0 +1,48 @@ + +pub mod stack; + +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct PhysAddr(usize); + +impl PhysAddr { + #[inline] + pub fn new(addr: usize) -> Self { + Self(addr) + } + + #[inline] + pub fn as_usize(&self) -> usize { + self.0 + } +} + +impl From for usize { + #[inline] + fn from(addr: PhysAddr) -> Self { + addr.0 + } +} + +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct VirtAddr(usize); + +impl VirtAddr { + #[inline] + pub fn new(addr: usize) -> Self { + Self(addr) + } + + #[inline] + pub fn as_usize(&self) -> usize { + self.0 + } +} + +impl From for usize { + #[inline] + fn from(addr: VirtAddr) -> Self { + addr.0 + } +} \ No newline at end of file diff --git a/machine/api/src/stack.rs b/machine/api/src/mem/stack.rs similarity index 94% rename from machine/api/src/stack.rs rename to machine/api/src/mem/stack.rs index db226a0..4291378 100644 --- a/machine/api/src/stack.rs +++ b/machine/api/src/mem/stack.rs @@ -1,9 +1,8 @@ -use core::{ffi::c_void, num::NonZero, ptr::NonNull}; - -use crate::Result; +use core::{ffi::c_void, num::NonZero}; +use crate::{Result, mem::PhysAddr}; pub struct StackDescriptor { - pub top: NonNull, + pub top: PhysAddr, pub size: NonZero, pub entry: extern "C" fn(), pub fin: Option !>, diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index 3026071..19578fa 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -202,6 +202,9 @@ impl hal_api::stack::Stacklike for ArmStack { fin, } = desc; + // We expect a PhysAddr, which can be converted to a ptr on nommu. + let top = NonNull::new(top as *mut u32).ok_or(hal_api::Error::InvalidAddress)?; + let mut stack = Self { top, sp: StackPtr { offset: 0 }, diff --git a/macros/src/tree.rs b/macros/src/tree.rs index f6c3b39..d62d8f6 100644 --- a/macros/src/tree.rs +++ b/macros/src/tree.rs @@ -42,13 +42,13 @@ fn impl_rbtree(input: &DeriveInput, fields: &syn::punctuated::Punctuated for #struct_ident #ty_generics #where_clause { + impl #impl_generics crate::types::rbtree::Linkable<#tag_path, #idx_path> for #struct_ident #ty_generics #where_clause { #[inline] - fn links(&self) -> &crate::mem::rbtree::Links<#tag_path, #idx_path> { + fn links(&self) -> &crate::types::rbtree::Links<#tag_path, #idx_path> { &self.#field_ident } #[inline] - fn links_mut(&mut self) -> &mut crate::mem::rbtree::Links<#tag_path, #idx_path> { + fn links_mut(&mut self) -> &mut crate::types::rbtree::Links<#tag_path, #idx_path> { &mut self.#field_ident } } diff --git a/src/dispatch.rs b/src/dispatch.rs new file mode 100644 index 0000000..a985fef --- /dev/null +++ b/src/dispatch.rs @@ -0,0 +1,14 @@ +//! This is the owner of all Tasks. It takes care of context switching between them. +//! The idea is that the Schedulers selects one of its threads to run, and then the Dipatcher takes care of context-switching to the associated Task. (e.g. setting up the address space) +//! If the thread is part of the same task as the currently running one, the Dispatcher does effectively nothing. +//! +//! + +mod task; + +use crate::types::array::IndexMap; + +/* +struct Dispatcher { + tasks: IndexMap, +}*/ \ No newline at end of file diff --git a/src/sched/task.rs b/src/dispatch/task.rs similarity index 64% rename from src/sched/task.rs rename to src/dispatch/task.rs index 9f5388b..b3f3d2c 100644 --- a/src/sched/task.rs +++ b/src/dispatch/task.rs @@ -2,74 +2,43 @@ use core::num::NonZero; use core::ops::Range; use core::ptr::NonNull; +use std::borrow::Borrow; -use hal::Stack; +use hal::{Stack, stack}; use hal::stack::Stacklike; -use crate::mem; +use crate::{mem, sched}; use crate::mem::alloc::{Allocator, BestFitAllocator}; -use crate::sched::thread::{ThreadDescriptor, ThreadId, Timing}; +use crate::mem::vmm::{AddressSpace, AddressSpacelike, Region}; +use crate::types::traits::ToIndex; use crate::utils::KernelError; /// Id of a task. This is unique across all tasks. -#[repr(u16)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] -pub enum TaskId { - // Task with normal user privileges in user mode. - User(usize), - // Task with kernel privileges in user mode. - Kernel(usize), +pub struct UId { + uid: usize, } -#[allow(dead_code)] -impl TaskId { - /// Check if the task is a user task. - pub fn is_user(&self) -> bool { - matches!(self, TaskId::User(_)) - } - - /// Check if the task is a kernel task. - pub fn is_kernel(&self) -> bool { - matches!(self, TaskId::Kernel(_)) - } - - pub fn new_user(id: usize) -> Self { - TaskId::User(id) - } - - pub fn new_kernel(id: usize) -> Self { - TaskId::Kernel(id) +impl ToIndex for UId { + fn to_index>(idx: Option) -> usize { + idx.as_ref().map_or(0, |uid| uid.borrow().uid) } } -impl From for usize { - fn from(val: TaskId) -> Self { - match val { - TaskId::User(id) => id, - TaskId::Kernel(id) => id, - } - } -} - - - -/// Descibes a task. -pub struct TaskDescriptor { - /// The size of the memory that the task requires. - pub mem_size: usize, +pub struct Attributes { + reserved: Option>, } /// The struct representing a task. -#[derive(Debug)] pub struct Task { /// The unique identifier of the task. - pub id: TaskId, - /// The memory of the task. - memory: TaskMemory, + pub id: UId, /// The counter for the thread ids. tid_cntr: usize, + /// Sets up the memory for the task. + address_space: mem::vmm::AddressSpace, } impl Task { @@ -78,45 +47,51 @@ impl Task { /// `memory_size` - The size of the memory that the task requires. /// /// Returns a new task if the task was created successfully, or an error if the task could not be created. - pub fn new(memory_size: usize, id: TaskId) -> Result { - let memory = TaskMemory::new(memory_size)?; - + pub fn new(id: UId, attrs: &Attributes) -> Result { Ok(Self { id, - memory, + address_space: AddressSpace::new(), tid_cntr: 0, }) } - fn allocate_tid(&mut self) -> ThreadId { + fn allocate_tid(&mut self) -> sched::thread::Id { let tid = self.tid_cntr; self.tid_cntr += 1; - ThreadId::new(tid, self.id) + sched::thread::Id::new(tid, self.id) + } + + pub fn allocate(&mut self, size: usize, align: usize) -> Result { + self.address_space.map(size, align) } pub fn create_thread( &mut self, entry: extern "C" fn(), fin: Option !>, - timing: Timing, ) -> Result { - // Safe unwrap because stack size is non zero. - // TODO: Make this configurable - let stack_size = NonZero::new(4096usize).unwrap(); - // TODO: Revert if error occurs - let stack_mem = self.memory.malloc(stack_size.into(), align_of::())?; - let stack_top = unsafe { stack_mem.byte_add(stack_size.get()) }; + + // Create the stack for the thread. + let size = 1 * mem::pfa::PAGE_SIZE; // TODO: Make this configurable + let start = self.address_space.end() - size; + let region = mem::vmm::Region::new( + start, + size, + mem::vmm::Backing::Uninit, + mem::vmm::Perms::Read | mem::vmm::Perms::Write, + ); + let stack_pa = self.address_space.map(region)?; let stack = hal::stack::StackDescriptor { - top: stack_top, - size: stack_size, + top: stack_pa, + // Safe unwrap because stack size is non zero. + size: NonZero::new(size).unwrap(), entry, fin, }; let stack = unsafe { Stack::new(stack) }?; - let tid = self.allocate_tid(); // TODO: Revert if error occurs @@ -185,5 +160,3 @@ impl Drop for TaskMemory { unsafe { mem::free(self.begin, self.size) }; } } - - diff --git a/src/lib.rs b/src/lib.rs index 287fd3d..2c023a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,10 @@ mod macros; mod utils; mod faults; mod mem; +mod types; pub mod print; -pub mod sched; +//pub mod sched; +mod dispatch; pub mod sync; pub mod syscalls; //pub mod time; diff --git a/src/mem.rs b/src/mem.rs index 6865d6b..f6c98b1 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -6,14 +6,8 @@ use alloc::Allocator; use core::ptr::NonNull; pub mod alloc; -pub mod array; -pub mod boxed; -pub mod heap; -pub mod pool; -pub mod queue; -pub mod rbtree; -pub mod traits; -pub mod view; +pub mod vmm; +pub mod pfa; /// The possible types of memory. Which is compatible with the multiboot2 memory map. /// Link: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html @@ -41,7 +35,9 @@ static GLOBAL_ALLOCATOR: SpinLocked = /// `regions` - The memory node module of device tree codegen file. /// /// Returns an error if the memory allocator could not be initialized. -pub fn init_memory(regions: &[(&str, usize, usize)]) -> Result<(), utils::KernelError> { +pub fn init_memory(boot_info: &BootInfo) -> Result<(), utils::KernelError> { + pfa::init_pfa(0x20000000)?; // TODO: Get this from the DeviceTree. + let mut allocator = GLOBAL_ALLOCATOR.lock(); for &(_, base, size) in regions { diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs new file mode 100644 index 0000000..9c24df8 --- /dev/null +++ b/src/mem/pfa.rs @@ -0,0 +1,57 @@ +// The top level page frame allocator. + +use crate::sync::spinlock::SpinLocked; +use crate::types::boxed::Box; +use crate::utils::KernelError; + +use interface::PhysAddr; +use core::pin::Pin; + +mod bitset; + +/// Page size constant (typically 4KB) +pub const PAGE_SIZE: usize = 4096; + +const PAGE_CNT: usize = 1024; // TODO: This should be determined by the DeviceTree. + +type AllocatorType = bitset::Allocator; + +static PFA: SpinLocked>>> = SpinLocked::new(None); + +/// This trait abstracts over different page frame allocator implementations. +trait Allocator { + /// Returns an initializer function that can be used to create an instance of the allocator. + /// The initializer function takes a physical address and the amount of pages needed. + /// + /// Safety: + /// + /// - The returned function must only be called with a useable and valid physical address. + fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>, KernelError>; + + fn alloc(&mut self, page_count: usize) -> Option; + fn free(&mut self, addr: PhysAddr, page_count: usize); +} + +pub fn init_pfa(addr: PhysAddr) -> Result<(), KernelError> { + let mut pfa = PFA.lock(); + if pfa.is_some() { + return Err(KernelError::CustomError("Page frame allocator is already initialized")); + } + + let initializer = AllocatorType::initializer(); + *pfa = Some(unsafe { initializer(addr, PAGE_CNT)? }); + + Ok(()) +} + +pub fn alloc_page(page_count: usize) -> Option { + let mut pfa = PFA.lock(); + pfa.as_mut()?.alloc(page_count) +} + +pub fn free_page(addr: PhysAddr, page_count: usize) { + let mut pfa = PFA.lock(); + if let Some(pfa) = pfa.as_mut() { + pfa.free(addr, page_count); + } +} \ No newline at end of file diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs new file mode 100644 index 0000000..d9ab9e3 --- /dev/null +++ b/src/mem/pfa/bitset.rs @@ -0,0 +1,219 @@ +use core::pin::Pin; +use core::ptr::NonNull; + +use crate::{ + types::boxed::{self, Box}, + utils::KernelError, +}; + +use interface::PhysAddr; + +pub struct Allocator { + begin: PhysAddr, + l1: [usize; N], +} + +impl Allocator { + const BITS_PER_WORD: usize = usize::BITS as usize; + + pub fn new(begin: PhysAddr) -> Option { + if !begin.is_multiple_of(super::PAGE_SIZE) { + return None; + } + + if begin > PhysAddr::MAX - (N * super::PAGE_SIZE * usize::BITS as usize) { + return None; + } + + Some(Self { + begin, + l1: [!0; N], // All bits are set to 1, meaning all pages are free. + }) + } +} + +impl super::Allocator for Allocator { + fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>, KernelError> { + |addr: PhysAddr, pcnt: usize| -> Result>, KernelError> { + if pcnt > N { + todo!("Runtime page frame allocator for more than {} pages", N) + } + + if !addr.is_multiple_of(core::mem::align_of::()) { + return Err(KernelError::InvalidAlign); + } + + let ptr = NonNull::new(addr as *mut Self).ok_or(KernelError::InvalidAddress)?; + + // Safety: Ptr is properly aligned and non-null. The validity of the memory at that address is valid by the call contract. + Ok(Pin::new(unsafe { boxed::Box::from_raw(ptr) })) + } + } + + fn alloc(&mut self, page_count: usize) -> Option { + // If a bit is 1 the page is free. If a bit is 0 the page is allocated. + let mut start = 0; + let mut len = 0usize; + + let rem = page_count.saturating_sub(Self::BITS_PER_WORD); + let mask = (!0usize).unbounded_shl((Self::BITS_PER_WORD.saturating_sub(page_count)) as u32); + + for idx in 0..N { + if self.l1[idx] == 0 { + len = 0; + continue; + } + + let mut byte = self.l1[idx]; + + let mut shift = if len > 0 { + 0usize + } else { + byte.leading_zeros() as usize + }; + + byte <<= shift; + + while shift < Self::BITS_PER_WORD { + // Make the mask smaller if we already have some contiguous bits. + let mask = if rem.saturating_sub(len) == 0 { + mask << (len - rem) + } else { + mask + }; + + // We shifted byte to MSB, mask is already aligned to the left. + // We compare them via and and shift to the right to shift out extra bits from the mask that would overflow into the next word. + let mut found = (byte & mask) >> shift; + + // We also need to shift the mask to the right so that we can compare mask and found. + if found == (mask >> shift) { + if len == 0 { + start = idx * Self::BITS_PER_WORD + shift; + } + + // Shift completely to the right. + found >>= found.trailing_zeros(); + + // As all found bits are now on the right we can just count them to get the amount we found. + len += found.trailing_ones() as usize; + // Continue to the next word if we haven't found enough bits yet. + break; + } else { + len = 0; + } + + shift += 1; + byte <<= 1; + } + + if len >= page_count { + // Mark the allocated pages as used. + let mut idx = start / Self::BITS_PER_WORD; + + // Mark all bits in the first word as used. + { + let skip = start % Self::BITS_PER_WORD; + let rem = len.min(Self::BITS_PER_WORD) - skip; + + self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); + + if len <= rem { + return Some(start); + } + + len -= rem; + idx += 1; + } + + // Mark all bits in the middle words as used. + { + let mid_cnt = len / Self::BITS_PER_WORD; + + for i in 0..mid_cnt { + self.l1[idx + i] = 0; + } + + idx += mid_cnt; + } + + // Mark the remaining bits in the last word as used. + self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); + return Some(self.begin + (start * super::PAGE_SIZE)); + } + } + + None + } + + fn free(&mut self, addr: PhysAddr, page_count: usize) { + if !addr.is_multiple_of(super::PAGE_SIZE) { + panic!("Address must be page aligned"); + } + + let mut idx = (addr - self.begin) / super::PAGE_SIZE / Self::BITS_PER_WORD; + let mut bit_idx = ((addr - self.begin) / super::PAGE_SIZE) % Self::BITS_PER_WORD; + + // TODO: slow + for _ in 0..page_count { + self.l1[idx] |= 1 << (Self::BITS_PER_WORD - 1 - bit_idx); + + bit_idx += 1; + + if bit_idx == Self::BITS_PER_WORD { + bit_idx = 0; + idx += 1; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_random_pattern() { + const ITARATIONS: usize = 1000; + + for i in 0..ITARATIONS { + const N: usize = 1024; + const BITS: usize = Allocator::::BITS_PER_WORD; + const ALLOC_SIZE: usize = 100; + + let mut allocator = Allocator::::new(0x0).unwrap(); + + // Generate a random bit pattern. + for i in 0..N { + let is_zero = rand::random::(); + + if is_zero { + allocator.l1[i / BITS] &= !(1 << ((BITS - 1) - (i % BITS))); + } + } + + // Place a run of ALLOC_SIZE contiguous bits set to 1 at a random position. + let start = rand::random::() % (N - ALLOC_SIZE); + for i in start..(start + ALLOC_SIZE) { + allocator.l1[i / BITS] |= 1 << ((BITS - 1) - (i % BITS)); + } + + let pre = allocator.l1.clone(); + + let addr = super::super::Allocator::alloc(&mut allocator, ALLOC_SIZE).unwrap(); + let idx = addr / super::super::PAGE_SIZE; + + // Check that the bits in returned addresses is all ones in pre. + for i in 0..ALLOC_SIZE { + let bit = (pre[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; + assert_eq!(bit, 1, "Bit at index {} is not set", idx + i); + } + + // Check that the bits in returned addresses is all zeros in allocator.l1. + for i in 0..ALLOC_SIZE { + let bit = (allocator.l1[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; + assert_eq!(bit, 0, "Bit at index {} is not cleared", idx + i); + } + } + } +} \ No newline at end of file diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs new file mode 100644 index 0000000..7565658 --- /dev/null +++ b/src/mem/vmm.rs @@ -0,0 +1,66 @@ +use core::ops::Range; + +use crate::{utils::KernelError}; + +use interface::{PhysAddr, VirtAddr}; + +mod nommu; + +pub type AddressSpace = nommu::AddressSpace; + +bitflags::bitflags! { + #[derive(Clone, Copy)] + pub struct Perms: u8 { + const Read = 0b0001; + const Write = 0b0010; + const Exec = 0b0100; + } +} + +#[derive(Clone)] +pub enum Backing { + Zeroed, + Uninit, + Anon(PhysAddr), +} + +#[derive(Clone)] +pub struct Region { + range: Range, + backing: Backing, + perms: Perms, +} + +impl Region { + pub fn new(start: VirtAddr, len: usize, backing: Backing, perms: Perms) -> Self { + Self { + range: start..start.saturating_add(len), + backing, + perms, + } + } + + pub fn start(&self) -> VirtAddr { + self.range.start + } + + pub fn len(&self) -> usize { + self.range.end.saturating_sub(self.range.start) + } + + pub fn contains(&self, addr: VirtAddr) -> bool { + self.range.contains(&addr) + } +} + +pub trait AddressSpacelike { + // Size is the amount of pages in the address space. On nommu systems this will be reserved. + fn new(pages: usize) -> Result where Self: Sized; + fn map(&mut self, region: Region) -> Result; + fn unmap(&mut self, region: &Region) -> Result<(), KernelError>; + fn protect(&mut self, region: &Region, perms: Perms) -> Result<(), KernelError>; + fn virt_to_phys(&self, addr: VirtAddr) -> Option; + fn phys_to_virt(&self, addr: PhysAddr) -> Option; + fn end(&self) -> VirtAddr; + fn activate(&self) -> Result<(), KernelError>; +} \ No newline at end of file diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs new file mode 100644 index 0000000..69603cd --- /dev/null +++ b/src/mem/vmm/nommu.rs @@ -0,0 +1,87 @@ +use core::ptr::copy_nonoverlapping; +use std::num::NonZero; + +use crate::{ + mem::{ + pfa, vmm, + }, + utils::KernelError, +}; + +use interface::{PhysAddr, VirtAddr}; + +pub struct AddressSpace { + begin: VirtAddr, + size: usize, +} + +impl vmm::AddressSpacelike for AddressSpace { + fn new(size: usize) -> Result { + let pg_cnt = size.div_ceil(pfa::PAGE_SIZE); + let begin = pfa::alloc_page(pg_cnt).ok_or(KernelError::OutOfMemory)?; + + Ok(Self { + begin, + size: pg_cnt * pfa::PAGE_SIZE, + }) + } + + fn map(&mut self, region: vmm::Region) -> Result { + if region.start() + region.len() > self.size { + return Err(KernelError::OutOfMemory); + } + + if let Some(test) = NonZero::new(region.start()) { + test. + } + + + match region.backing { + vmm::Backing::Anon(phys) => { + unsafe { + copy_nonoverlapping( + phys as *const u8, + (self.begin + region.start()) as *mut u8, + region.len(), + ) + }; + Ok(self.begin + region.start()) + }, + vmm::Backing::Zeroed => { + unsafe { + core::ptr::write_bytes( + (self.begin + region.start()) as *mut u8, + 0, + region.len(), + ) + }; + Ok(self.begin + region.start()) + }, + vmm::Backing::Uninit => Ok(self.begin + region.start()), + } + } + + fn unmap(&mut self, _region: &vmm::Region) -> Result<(), KernelError> { + Ok(()) + } + + fn protect(&mut self, _region: &vmm::Region, _perms: vmm::Perms) -> Result<(), KernelError> { + Ok(()) + } + + fn phys_to_virt(&self, addr: PhysAddr) -> Option { + addr.checked_sub(self.begin) + } + + fn virt_to_phys(&self, addr: VirtAddr) -> Option { + self.begin.checked_add(addr) + } + + fn end(&self) -> VirtAddr { + self.size + } + + fn activate(&self) -> Result<(), KernelError> { + Ok(()) + } +} diff --git a/src/sched.rs b/src/sched.rs index 72ce054..9edca37 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,13 +1,11 @@ //! This module provides access to the scheduler. pub mod rt; -//pub mod scheduler; -pub mod task; -pub mod thread; +//pub mod thread; use hal::Schedable; -use crate::mem::{array::IndexMap, rbtree::RbTree, view::ViewMut}; +use crate::types::{array::IndexMap, rbtree::RbTree, view::ViewMut}; type ThreadMap = IndexMap; diff --git a/src/sched/rt.rs b/src/sched/rt.rs index c6c6b00..95dfb62 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -1,4 +1,4 @@ -use crate::{mem::{rbtree::RbTree, traits::{Get, GetMut}, view::ViewMut}, sched::{ThreadMap, thread::{self}}}; +use crate::{types::{rbtree::RbTree, traits::{Get, GetMut}, view::ViewMut}, sched::{ThreadMap, thread::{self}}}; pub struct Scheduler { edf: RbTree, diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 7a814b4..f988b97 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -6,7 +6,7 @@ use hal::Stack; use hal::stack::Stacklike; use macros::TaggedLinks; -use crate::{mem::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, sched::task::TaskId, utils::KernelError}; +use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, sched::task::TaskId, utils::KernelError}; /// Id of a task. This is only unique within a Task. #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..26df432 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,8 @@ + +pub mod boxed; +pub mod array; +pub mod heap; +pub mod pool; +pub mod rbtree; +pub mod traits; +pub mod view; \ No newline at end of file diff --git a/src/mem/array.rs b/src/types/array.rs similarity index 97% rename from src/mem/array.rs rename to src/types/array.rs index 4e40954..3e50514 100644 --- a/src/mem/array.rs +++ b/src/types/array.rs @@ -1,12 +1,14 @@ //! This module implements static and dynamic arrays for in-kernel use. -use super::boxed::Box; -use crate::{ - mem::traits::{Get, GetMut, ToIndex}, - utils::KernelError, +use super::{ + traits::{Get, GetMut, ToIndex}, + boxed::Box, }; + +use crate::utils::KernelError; + use core::{borrow::Borrow, mem::MaybeUninit}; -use std::{ +use core::{ ops::{Index, IndexMut}, }; @@ -171,12 +173,17 @@ impl GetMut for IndexMap { return (None, None); } - let ptr1 = &mut self.data[index1] as *mut Option; - let ptr2 = &mut self.data[index2] as *mut Option; + let (left, right) = self.data.split_at_mut(index1.max(index2)); - // Safety: the elements at index1 and index2 are nowhere else borrowed mutably by function contract. - // And they are disjoint because of the check above. - unsafe { ((*ptr1).as_mut(), (*ptr2).as_mut()) } + if index1 < index2 { + let elem1 = left[index1].as_mut(); + let elem2 = right[0].as_mut(); + (elem1, elem2) + } else { + let elem1 = right[0].as_mut(); + let elem2 = left[index2].as_mut(); + (elem1, elem2) + } } fn get3_mut>( diff --git a/src/mem/boxed.rs b/src/types/boxed.rs similarity index 85% rename from src/mem/boxed.rs rename to src/types/boxed.rs index c2af5d7..3e2277a 100644 --- a/src/mem/boxed.rs +++ b/src/types/boxed.rs @@ -1,6 +1,6 @@ //! This module provides a simple heap-allocated memory block for in-kernel use. -use super::{free, malloc}; +use crate::mem; use crate::utils::KernelError; use core::{ mem::{MaybeUninit, forget}, @@ -28,7 +28,7 @@ impl Box<[T]> { return Ok(Self::new_slice_empty()); } - if let Some(ptr) = malloc(size_of::() * len, align_of::()) { + if let Some(ptr) = mem::malloc(size_of::() * len, align_of::()) { let ptr = slice_from_raw_parts_mut(ptr.as_ptr().cast(), len); Ok(Self { ptr: unsafe { NonNull::new_unchecked(ptr) }, @@ -54,7 +54,7 @@ impl Box<[T]> { /// /// Returns a new heap-allocated slice with the given length or an error if the allocation failed. pub fn new_slice_uninit(len: usize) -> Result]>, KernelError> { - if let Some(ptr) = malloc( + if let Some(ptr) = mem::malloc( size_of::>() * len, align_of::>(), ) { @@ -76,7 +76,7 @@ impl Box { /// /// Returns a new heap-allocated value or `None` if the allocation failed. pub fn new(value: T) -> Option { - if let Some(ptr) = malloc(size_of::(), align_of::()) { + if let Some(ptr) = mem::malloc(size_of::(), align_of::()) { unsafe { write(ptr.as_ptr().cast(), value); } @@ -139,7 +139,7 @@ impl Drop for Box { } drop_in_place(self.ptr.as_ptr()); - free(self.ptr.cast(), size); + mem::free(self.ptr.cast(), size); } } } @@ -239,39 +239,3 @@ impl AsMut for Box { self.as_mut() } } - -#[cfg(kani)] -mod verification { - use crate::mem::alloc; - - use super::*; - - /* - fn alloc_range(length: usize) -> Option> { - let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); - let ptr = unsafe { std::alloc::alloc(alloc_range) }; - - if ptr.is_null() || ((ptr as usize) >= isize::MAX as usize - length) { - None - } else { - Some(ptr as usize..ptr as usize + length) - } - } - - #[kani::proof] - fn proof_new_slice_zero() { - let mut allocator = alloc::BestFitAllocator::new(); - allocator - - let len = kani::any(); - kani::assume(len < alloc::MAX_ADDR); - - let b = Box::::new_slice_zeroed(len); - - let index = kani::any(); - kani::assume(index < len); - - assert!(b[index] == 0); - } - */ -} diff --git a/src/mem/heap.rs b/src/types/heap.rs similarity index 100% rename from src/mem/heap.rs rename to src/types/heap.rs diff --git a/src/mem/pool.rs b/src/types/pool.rs similarity index 100% rename from src/mem/pool.rs rename to src/types/pool.rs diff --git a/src/mem/queue.rs b/src/types/queue.rs similarity index 100% rename from src/mem/queue.rs rename to src/types/queue.rs diff --git a/src/mem/rbtree.rs b/src/types/rbtree.rs similarity index 99% rename from src/mem/rbtree.rs rename to src/types/rbtree.rs index 2f7a5f0..ac64a17 100644 --- a/src/mem/rbtree.rs +++ b/src/types/rbtree.rs @@ -1,6 +1,6 @@ -use std::{marker::PhantomData}; +use core::{marker::PhantomData}; -use crate::mem::traits::{Get, GetMut}; +use super::traits::{Get, GetMut}; #[allow(dead_code)] pub struct RbTree { @@ -652,7 +652,7 @@ impl RbTree #[cfg(test)] mod tests { use super::*; - use crate::mem::traits::{Get, GetMut}; + use super::{Get, GetMut}; use std::borrow::Borrow; use std::collections::HashSet; diff --git a/src/mem/traits.rs b/src/types/traits.rs similarity index 100% rename from src/mem/traits.rs rename to src/types/traits.rs diff --git a/src/mem/view.rs b/src/types/view.rs similarity index 94% rename from src/mem/view.rs rename to src/types/view.rs index c8bd4bc..c07de3a 100644 --- a/src/mem/view.rs +++ b/src/types/view.rs @@ -1,7 +1,7 @@ use core::borrow::Borrow; -use std::marker::PhantomData; +use core::marker::PhantomData; -use crate::mem::traits::{Get, GetMut, Project, ToIndex}; +use super::traits::{Get, GetMut, Project, ToIndex}; pub struct ViewMut<'a, K: ?Sized + ToIndex, P, S: GetMut> where diff --git a/src/utils.rs b/src/utils.rs index 9fa7644..c77239a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -58,6 +58,7 @@ pub enum KernelError { InvalidAddress, InvalidArgument, HalError(hal::Error), + CustomError(&'static str), } /// Debug msg implementation for KernelError. @@ -70,6 +71,7 @@ impl Debug for KernelError { KernelError::InvalidAddress => write!(f, "Invalid address"), KernelError::InvalidArgument => write!(f, "Invalid argument"), KernelError::HalError(e) => write!(f, "{e} (in HAL)"), + KernelError::CustomError(msg) => write!(f, "{}", msg), } } } From b74275188d9f23fc0211c8c30dcb7414c0db5953 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:08:03 +0000 Subject: [PATCH 03/28] more scheduler rewrite --- machine/api/src/lib.rs | 5 +- machine/api/src/mem.rs | 67 +++++++++++- machine/api/src/{mem => }/stack.rs | 11 +- machine/arm/src/sched.rs | 8 +- machine/testing/src/sched.rs | 4 +- options.toml | 6 ++ src/dispatch.rs | 14 --- src/dispatch/task.rs | 162 ---------------------------- src/idle.rs | 17 +++ src/lib.rs | 17 +-- src/mem.rs | 34 ++++-- src/mem/pfa.rs | 5 +- src/mem/pfa/bitset.rs | 14 +-- src/mem/vmm.rs | 6 +- src/mem/vmm/nommu.rs | 47 ++++---- src/sched.rs | 165 ++++++++++++++++++++--------- src/sched/dispch.rs | 7 ++ src/sched/rt.rs | 6 +- src/sched/task.rs | 129 ++++++++++++++++++++++ src/sched/thread.rs | 49 +++++---- src/sync/atomic.rs | 117 +++++++++++++++++++- src/time.rs | 38 ++----- src/types/rbtree.rs | 2 +- 23 files changed, 584 insertions(+), 346 deletions(-) rename machine/api/src/{mem => }/stack.rs (89%) delete mode 100644 src/dispatch.rs delete mode 100644 src/dispatch/task.rs create mode 100644 src/idle.rs create mode 100644 src/sched/dispch.rs create mode 100644 src/sched/task.rs diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index 36860bc..4480adb 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -3,6 +3,7 @@ use core::{fmt::Display, ops::Range}; pub mod mem; +pub mod stack; #[derive(Default, Debug, PartialEq, Eq, Clone)] pub enum Error { @@ -10,6 +11,7 @@ pub enum Error { Generic, OutOfMemory(usize), OutOfBoundsPtr(usize, Range), + InvalidAddress(usize), } pub enum Fault { @@ -30,7 +32,8 @@ impl Display for Error { "Pointer {:p} out of bounds (expected in {:p}..{:p})", *ptr as *const u8, range.start as *const u8, range.end as *const u8 ) - } + }, + Error::InvalidAddress(addr) => write!(f, "Invalid address {:p}", *addr as *const u8), } } } diff --git a/machine/api/src/mem.rs b/machine/api/src/mem.rs index 10c8b07..b919bfe 100644 --- a/machine/api/src/mem.rs +++ b/machine/api/src/mem.rs @@ -1,11 +1,12 @@ - -pub mod stack; +use core::ops::{Add, Sub, Div, Rem}; #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct PhysAddr(usize); impl PhysAddr { + pub const MAX: Self = Self(usize::MAX); + #[inline] pub fn new(addr: usize) -> Self { Self(addr) @@ -15,6 +16,58 @@ impl PhysAddr { pub fn as_usize(&self) -> usize { self.0 } + + pub fn as_mut_ptr(&self) -> *mut T { + self.0 as *mut T + } + + pub fn checked_add(&self, other: usize) -> Option { + self.0.checked_add(other).map(Self) + } + + pub fn checked_sub(&self, other: usize) -> Option { + self.0.checked_sub(other).map(Self) + } + + pub fn is_multiple_of(&self, align: usize) -> bool { + self.0.is_multiple_of(align) + } +} + +impl Add for PhysAddr { + type Output = Self; + + #[inline] + fn add(self, rhs: usize) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl Sub for PhysAddr { + type Output = Self; + + #[inline] + fn sub(self, rhs: usize) -> Self::Output { + Self(self.0 - rhs) + } +} + +impl Div for PhysAddr { + type Output = Self; + + #[inline] + fn div(self, rhs: usize) -> Self::Output { + Self(self.0 / rhs) + } +} + +impl Rem for PhysAddr { + type Output = Self; + + #[inline] + fn rem(self, rhs: usize) -> Self::Output { + Self(self.0 % rhs) + } } impl From for usize { @@ -38,6 +91,16 @@ impl VirtAddr { pub fn as_usize(&self) -> usize { self.0 } + + #[inline] + pub fn saturating_add(&self, other: usize) -> Self { + Self(self.0.saturating_add(other)) + } + + #[inline] + pub fn saturating_sub(&self, other: usize) -> Self { + Self(self.0.saturating_sub(other)) + } } impl From for usize { diff --git a/machine/api/src/mem/stack.rs b/machine/api/src/stack.rs similarity index 89% rename from machine/api/src/mem/stack.rs rename to machine/api/src/stack.rs index 4291378..11604f1 100644 --- a/machine/api/src/mem/stack.rs +++ b/machine/api/src/stack.rs @@ -1,18 +1,21 @@ use core::{ffi::c_void, num::NonZero}; use crate::{Result, mem::PhysAddr}; -pub struct StackDescriptor { +pub type EntryFn = extern "C" fn(); +pub type FinFn = extern "C" fn() -> !; + +pub struct Descriptor { pub top: PhysAddr, pub size: NonZero, - pub entry: extern "C" fn(), - pub fin: Option !>, + pub entry: EntryFn, + pub fin: Option, } pub trait Stacklike { type ElemSize: Copy; type StackPtr; - unsafe fn new(desc: StackDescriptor) -> Result + unsafe fn new(desc: Descriptor) -> Result where Self: Sized; diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index 19578fa..eb3f274 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -7,7 +7,7 @@ use core::{ ptr::NonNull, }; -use hal_api::{Result, stack::StackDescriptor}; +use hal_api::{Result, stack::Descriptor}; use crate::print::println; @@ -191,11 +191,11 @@ impl hal_api::stack::Stacklike for ArmStack { type ElemSize = u32; type StackPtr = StackPtr; - unsafe fn new(desc: StackDescriptor) -> Result + unsafe fn new(desc: Descriptor) -> Result where Self: Sized, { - let StackDescriptor { + let Descriptor { top, size, entry, @@ -203,7 +203,7 @@ impl hal_api::stack::Stacklike for ArmStack { } = desc; // We expect a PhysAddr, which can be converted to a ptr on nommu. - let top = NonNull::new(top as *mut u32).ok_or(hal_api::Error::InvalidAddress)?; + let top = NonNull::new(top.as_mut_ptr::()).ok_or(hal_api::Error::InvalidAddress(top.as_usize()))?; let mut stack = Self { top, diff --git a/machine/testing/src/sched.rs b/machine/testing/src/sched.rs index 9715b16..ced7100 100644 --- a/machine/testing/src/sched.rs +++ b/machine/testing/src/sched.rs @@ -2,7 +2,7 @@ use std::ffi::c_void; use hal_api::{ Result, - stack::{StackDescriptor, Stacklike}, + stack::{Descriptor, Stacklike}, }; #[derive(Debug, Clone, Copy)] @@ -12,7 +12,7 @@ impl Stacklike for TestingStack { type ElemSize = usize; type StackPtr = *mut c_void; - unsafe fn new(_desc: StackDescriptor) -> Result + unsafe fn new(_desc: Descriptor) -> Result where Self: Sized, { diff --git a/options.toml b/options.toml index d7bf1ce..7427cea 100644 --- a/options.toml +++ b/options.toml @@ -26,6 +26,12 @@ description = "Enables the Floating Point Unit (FPU). This is required for appli type = "Boolean" default = false +[stackpages] +name = "Stack Pages" +description = "Number of pages to allocate for the kernel stack." +type = { type = "Integer", min = 1 } +default = 4 + [tuning.appmemsize] name = "Application Memory Size" description = "Sets the size of the initial memory region for the init application. This memory is used for the heap and stack." diff --git a/src/dispatch.rs b/src/dispatch.rs deleted file mode 100644 index a985fef..0000000 --- a/src/dispatch.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! This is the owner of all Tasks. It takes care of context switching between them. -//! The idea is that the Schedulers selects one of its threads to run, and then the Dipatcher takes care of context-switching to the associated Task. (e.g. setting up the address space) -//! If the thread is part of the same task as the currently running one, the Dispatcher does effectively nothing. -//! -//! - -mod task; - -use crate::types::array::IndexMap; - -/* -struct Dispatcher { - tasks: IndexMap, -}*/ \ No newline at end of file diff --git a/src/dispatch/task.rs b/src/dispatch/task.rs deleted file mode 100644 index b3f3d2c..0000000 --- a/src/dispatch/task.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! This module provides the basic task and thread structures for the scheduler. -use core::num::NonZero; -use core::ops::Range; -use core::ptr::NonNull; -use std::borrow::Borrow; - -use hal::{Stack, stack}; - -use hal::stack::Stacklike; - -use crate::{mem, sched}; - -use crate::mem::alloc::{Allocator, BestFitAllocator}; -use crate::mem::vmm::{AddressSpace, AddressSpacelike, Region}; -use crate::types::traits::ToIndex; -use crate::utils::KernelError; - -/// Id of a task. This is unique across all tasks. -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] -pub struct UId { - uid: usize, -} - -impl ToIndex for UId { - fn to_index>(idx: Option) -> usize { - idx.as_ref().map_or(0, |uid| uid.borrow().uid) - } -} - -pub struct Attributes { - reserved: Option>, -} - -/// The struct representing a task. -pub struct Task { - /// The unique identifier of the task. - pub id: UId, - /// The counter for the thread ids. - tid_cntr: usize, - /// Sets up the memory for the task. - address_space: mem::vmm::AddressSpace, -} - -impl Task { - /// Create a new task. - /// - /// `memory_size` - The size of the memory that the task requires. - /// - /// Returns a new task if the task was created successfully, or an error if the task could not be created. - pub fn new(id: UId, attrs: &Attributes) -> Result { - Ok(Self { - id, - address_space: AddressSpace::new(), - tid_cntr: 0, - }) - } - - fn allocate_tid(&mut self) -> sched::thread::Id { - let tid = self.tid_cntr; - self.tid_cntr += 1; - - sched::thread::Id::new(tid, self.id) - } - - pub fn allocate(&mut self, size: usize, align: usize) -> Result { - self.address_space.map(size, align) - } - - pub fn create_thread( - &mut self, - entry: extern "C" fn(), - fin: Option !>, - ) -> Result { - - // Create the stack for the thread. - let size = 1 * mem::pfa::PAGE_SIZE; // TODO: Make this configurable - let start = self.address_space.end() - size; - let region = mem::vmm::Region::new( - start, - size, - mem::vmm::Backing::Uninit, - mem::vmm::Perms::Read | mem::vmm::Perms::Write, - ); - let stack_pa = self.address_space.map(region)?; - - let stack = hal::stack::StackDescriptor { - top: stack_pa, - // Safe unwrap because stack size is non zero. - size: NonZero::new(size).unwrap(), - entry, - fin, - }; - - let stack = unsafe { Stack::new(stack) }?; - let tid = self.allocate_tid(); - - // TODO: Revert if error occurs - self.register_thread(tid)?; - - Ok(ThreadDescriptor { tid, stack, timing }) - } - - /// Register a thread with the task. - /// - /// `thread_id` - The id of the thread to register. - /// - /// Returns `Ok(())` if the thread was registered successfully, or an error if the thread could not be registered. TODO: Check if the thread is using the same memory as the task. - fn register_thread(&mut self, thread_id: ThreadId) -> Result<(), KernelError> { - self.threads.push(thread_id) - } -} - -/// The memory of a task. -#[derive(Debug)] -pub struct TaskMemory { - /// The beginning of the memory. - begin: NonNull, - /// The size of the memory. - size: usize, - - /// The allocator for the task's memory. - alloc: BestFitAllocator, -} - -#[allow(dead_code)] -impl TaskMemory { - /// Create a new task memory. - /// - /// `size` - The size of the memory. - /// - /// Returns a new task memory if the memory was created successfully, or an error if the memory could not be created. - pub fn new(size: usize) -> Result { - let begin = mem::malloc(size, align_of::()).ok_or(KernelError::OutOfMemory)?; - - let mut alloc = BestFitAllocator::new(); - let range = Range { - start: begin.as_ptr() as usize, - end: begin.as_ptr() as usize + size, - }; - - if let Err(e) = unsafe { alloc.add_range(range) } { - unsafe { mem::free(begin, size) }; - return Err(e); - } - - Ok(Self { begin, size, alloc }) - } - - pub fn malloc(&mut self, size: usize, align: usize) -> Result, KernelError> { - self.alloc.malloc(size, align) - } - - pub fn free(&mut self, ptr: NonNull, size: usize) { - unsafe { self.alloc.free(ptr, size) } - } -} - -impl Drop for TaskMemory { - fn drop(&mut self) { - unsafe { mem::free(self.begin, self.size) }; - } -} diff --git a/src/idle.rs b/src/idle.rs new file mode 100644 index 0000000..c3b4060 --- /dev/null +++ b/src/idle.rs @@ -0,0 +1,17 @@ +use crate::sched; + +extern "C" fn entry() { + loop { + hal::asm::wfi!(); + } +} + +pub fn init() { + let attrs = sched::thread::Attributes { + entry: entry, + fin: None, + }; + if let Err(e) = sched::create_thread(sched::task::KERNEL_TASK, &attrs) { + panic!("[Idle] Error: failed to create idle thread. Error: {e:?}"); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2c023a0..f273848 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,13 @@ mod utils; mod faults; mod mem; mod types; +mod idle; + pub mod print; -//pub mod sched; -mod dispatch; +pub mod sched; pub mod sync; pub mod syscalls; -//pub mod time; +pub mod time; pub mod uspace; use hal::Machinelike; @@ -33,6 +34,7 @@ include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); /// The `boot_info` pointer must be valid and point to a properly initialized `BootInfo` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { + hal::asm::disable_interrupts!(); // Initialize basic hardware and the logging system. hal::Machine::init(); hal::Machine::bench_start(); @@ -47,9 +49,10 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { print::print_header(); // Initialize the memory allocator. - if let Err(e) = mem::init_memory(&device_tree::memory::REGIONS) { - panic!("[Kernel] Error: failed to initialize memory allocator. Error: {e:?}"); - } + let kaddr_space = mem::init_memory(boot_info); + + sched::init(kaddr_space); + idle::init(); let (cyc, ns) = hal::Machine::bench_end(); kprintln!( @@ -63,6 +66,8 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { panic!("[Kernel] Error: failed to start init application. Error: {e:?}"); } + hal::asm::enable_interrupts!(); + loop { hal::asm::nop!(); } diff --git a/src/mem.rs b/src/mem.rs index f6c98b1..f45db97 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,14 +1,18 @@ //! This module provides access to the global memory allocator. +use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region}; use crate::sync::spinlock::SpinLocked; -use crate::{BootInfo, utils}; +use crate::{BootInfo, sched, utils}; use alloc::Allocator; +use hal::mem::{PhysAddr, VirtAddr}; use core::ptr::NonNull; pub mod alloc; pub mod vmm; pub mod pfa; +pub const BITS_PER_PTR: usize = core::mem::size_of::() * 8; + /// The possible types of memory. Which is compatible with the multiboot2 memory map. /// Link: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html #[repr(C)] @@ -35,19 +39,31 @@ static GLOBAL_ALLOCATOR: SpinLocked = /// `regions` - The memory node module of device tree codegen file. /// /// Returns an error if the memory allocator could not be initialized. -pub fn init_memory(boot_info: &BootInfo) -> Result<(), utils::KernelError> { - pfa::init_pfa(0x20000000)?; // TODO: Get this from the DeviceTree. +pub fn init_memory(boot_info: &BootInfo) -> vmm::AddressSpace { + if let Err(e) = pfa::init_pfa(PhysAddr::new(0x20000000)) { // TODO: Get this from the DeviceTree. + panic!("[Kernel] Error: failed to initialize PFA. Error: {e:?}"); + } + + // TODO: Configure. + let pgs = 4; + + let kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { + panic!("[Kernel] Error: failed to create kernel address space."); + }); + + let begin = kaddr_space.map(Region::new(VirtAddr::new(0), len, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { + panic!("[Kernel] Error: failed to map kernel address space."); + }); let mut allocator = GLOBAL_ALLOCATOR.lock(); - for &(_, base, size) in regions { - let range = base..base + size; - unsafe { - allocator.add_range(range)?; - } + let range = begin.as_usize()..(begin.as_usize() + pgs * vmm::PAGE_SIZE); + + if let Err(e) = unsafe { allocator.add_range(range) } { + panic!("[Kernel] Error: failed to add range to allocator."); } - Ok(()) + kaddr_space } /// Allocate a memory block. Normally Box or SizedPool should be used instead of this function. diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs index 9c24df8..c62b8c1 100644 --- a/src/mem/pfa.rs +++ b/src/mem/pfa.rs @@ -1,10 +1,11 @@ // The top level page frame allocator. +use hal::mem::PhysAddr; + use crate::sync::spinlock::SpinLocked; use crate::types::boxed::Box; use crate::utils::KernelError; -use interface::PhysAddr; use core::pin::Pin; mod bitset; @@ -12,7 +13,7 @@ mod bitset; /// Page size constant (typically 4KB) pub const PAGE_SIZE: usize = 4096; -const PAGE_CNT: usize = 1024; // TODO: This should be determined by the DeviceTree. +const PAGE_CNT: usize = 100; // TODO: This should be determined by the DeviceTree. type AllocatorType = bitset::Allocator; diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index d9ab9e3..23ef7f7 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -1,13 +1,13 @@ use core::pin::Pin; use core::ptr::NonNull; +use hal::mem::PhysAddr; + use crate::{ types::boxed::{self, Box}, utils::KernelError, }; -use interface::PhysAddr; - pub struct Allocator { begin: PhysAddr, l1: [usize; N], @@ -43,7 +43,7 @@ impl super::Allocator for Allocator { return Err(KernelError::InvalidAlign); } - let ptr = NonNull::new(addr as *mut Self).ok_or(KernelError::InvalidAddress)?; + let ptr = NonNull::new(addr.as_mut_ptr::()).ok_or(KernelError::InvalidAddress)?; // Safety: Ptr is properly aligned and non-null. The validity of the memory at that address is valid by the call contract. Ok(Pin::new(unsafe { boxed::Box::from_raw(ptr) })) @@ -119,7 +119,7 @@ impl super::Allocator for Allocator { self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); if len <= rem { - return Some(start); + return Some(PhysAddr::new(start)); } len -= rem; @@ -151,8 +151,8 @@ impl super::Allocator for Allocator { panic!("Address must be page aligned"); } - let mut idx = (addr - self.begin) / super::PAGE_SIZE / Self::BITS_PER_WORD; - let mut bit_idx = ((addr - self.begin) / super::PAGE_SIZE) % Self::BITS_PER_WORD; + let mut idx = (addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE / Self::BITS_PER_WORD; + let mut bit_idx = ((addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE) % Self::BITS_PER_WORD; // TODO: slow for _ in 0..page_count { @@ -181,7 +181,7 @@ mod tests { const BITS: usize = Allocator::::BITS_PER_WORD; const ALLOC_SIZE: usize = 100; - let mut allocator = Allocator::::new(0x0).unwrap(); + let mut allocator = Allocator::::new(PhysAddr::new(0x0)).unwrap(); // Generate a random bit pattern. for i in 0..N { diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs index 7565658..1f8aba9 100644 --- a/src/mem/vmm.rs +++ b/src/mem/vmm.rs @@ -1,8 +1,8 @@ use core::ops::Range; -use crate::{utils::KernelError}; +use hal::mem::{PhysAddr, VirtAddr}; -use interface::{PhysAddr, VirtAddr}; +use crate::{utils::KernelError}; mod nommu; @@ -45,7 +45,7 @@ impl Region { } pub fn len(&self) -> usize { - self.range.end.saturating_sub(self.range.start) + self.range.end.saturating_sub(self.range.start.into()).into() } pub fn contains(&self, addr: VirtAddr) -> bool { diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs index 69603cd..ff561b2 100644 --- a/src/mem/vmm/nommu.rs +++ b/src/mem/vmm/nommu.rs @@ -1,5 +1,6 @@ use core::ptr::copy_nonoverlapping; -use std::num::NonZero; + +use hal::mem::{PhysAddr, VirtAddr}; use crate::{ mem::{ @@ -8,57 +9,56 @@ use crate::{ utils::KernelError, }; -use interface::{PhysAddr, VirtAddr}; - pub struct AddressSpace { - begin: VirtAddr, - size: usize, + begin: PhysAddr, + end: PhysAddr, } impl vmm::AddressSpacelike for AddressSpace { fn new(size: usize) -> Result { let pg_cnt = size.div_ceil(pfa::PAGE_SIZE); let begin = pfa::alloc_page(pg_cnt).ok_or(KernelError::OutOfMemory)?; + let end = begin.checked_add(pg_cnt * pfa::PAGE_SIZE).ok_or(KernelError::OutOfMemory)?; Ok(Self { begin, - size: pg_cnt * pfa::PAGE_SIZE, + end, }) } fn map(&mut self, region: vmm::Region) -> Result { - if region.start() + region.len() > self.size { - return Err(KernelError::OutOfMemory); - } - - if let Some(test) = NonZero::new(region.start()) { - test. - } - + // Do both checks in one statement. + let phys = self.virt_to_phys(region.start()).and_then(|phys| { + if phys > self.end { + None + } else { + Some(phys) + } + }).ok_or(KernelError::InvalidArgument)?; match region.backing { vmm::Backing::Anon(phys) => { unsafe { copy_nonoverlapping( - phys as *const u8, - (self.begin + region.start()) as *mut u8, + phys.as_mut_ptr::(), + phys.as_mut_ptr::(), region.len(), ) }; - Ok(self.begin + region.start()) }, vmm::Backing::Zeroed => { unsafe { core::ptr::write_bytes( - (self.begin + region.start()) as *mut u8, + phys.as_mut_ptr::(), 0, region.len(), ) }; - Ok(self.begin + region.start()) }, - vmm::Backing::Uninit => Ok(self.begin + region.start()), + vmm::Backing::Uninit => {}, } + + Ok(phys) } fn unmap(&mut self, _region: &vmm::Region) -> Result<(), KernelError> { @@ -70,15 +70,16 @@ impl vmm::AddressSpacelike for AddressSpace { } fn phys_to_virt(&self, addr: PhysAddr) -> Option { - addr.checked_sub(self.begin) + addr.checked_sub(self.begin.as_usize()).map(|phys| VirtAddr::new(phys.as_usize())) } fn virt_to_phys(&self, addr: VirtAddr) -> Option { - self.begin.checked_add(addr) + self.begin.checked_add(addr.as_usize()) } fn end(&self) -> VirtAddr { - self.size + // This should always succeed. + self.phys_to_virt(self.end).unwrap() } fn activate(&self) -> Result<(), KernelError> { diff --git a/src/sched.rs b/src/sched.rs index 9edca37..076b107 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,47 +1,80 @@ //! This module provides access to the scheduler. +mod dispch; pub mod rt; -//pub mod thread; +pub mod task; +pub mod thread; + +use core::ffi::c_void; use hal::Schedable; -use crate::types::{array::IndexMap, rbtree::RbTree, view::ViewMut}; +use crate::{ + sync::spinlock::SpinLocked, types::{ + array::IndexMap, + rbtree::RbTree, + traits::{Get, GetMut}, + view::ViewMut, + }, utils::KernelError +}; type ThreadMap = IndexMap; +type TaskMap = IndexMap; + +static SCHED: SpinLocked> = SpinLocked::new(Scheduler::new()); pub struct Scheduler { threads: ThreadMap, + tasks: TaskMap, + id_gen: usize, + rt_scheduler: rt::Scheduler, wakeup: RbTree, + current: thread::UId, last_tick: u64, + next_tick: u64, } impl Scheduler { - pub fn new() -> Self { + pub const fn new() -> Self { Self { threads: IndexMap::new(), + tasks: IndexMap::new(), + id_gen: 1, rt_scheduler: rt::Scheduler::new(), wakeup: RbTree::new(), + current: thread::IDLE_THREAD, last_tick: 0, + next_tick: 0, } } - pub fn enqueue(&mut self, thread: thread::Thread) { - let uid = thread.uid(); - let rt = thread.rt_server().is_some(); - self.threads.insert(&thread.uid(), thread); + fn land(&mut self, ctx: *mut c_void) -> Result<(), KernelError> { + // A thread must not disappear while it is running. + let current = self.threads.get_mut(self.current).ok_or(KernelError::InvalidArgument)?; + // The context pointer must not be bogus after a sched_enter. + current.save_ctx(ctx) + } + + pub fn enqueue(&mut self, uid: thread::UId) -> Result<(), KernelError> { + let thread = self.threads.get(uid).ok_or(KernelError::InvalidArgument)?; - if rt { - let mut view = ViewMut::>::new( - &mut self.threads, - ); + if thread.rt_server().is_some() { + let mut view = + ViewMut::>::new(&mut self.threads); self.rt_scheduler.enqueue(uid, &mut view); } + + Ok(()) } - pub fn do_sched(&mut self, now: u64, old: Option) -> Option { + pub fn do_sched( + &mut self, + now: u64, + old: Option, + ) -> Option<(*mut c_void, &mut task::Task)> { let dt = now - self.last_tick; self.last_tick = now; @@ -55,7 +88,14 @@ impl Scheduler { } let mut view = rt::ServerView::::new(&mut self.threads); - self.rt_scheduler.pick(now, &mut view) + let (new, budget) = self.rt_scheduler.pick(now, &mut view)?; + + let ctx = self.threads.get(new)?.ctx(); + let task = self.tasks.get_mut(self.threads.get(new)?.task_id())?; + + self.current = new; + self.next_tick = now + budget; + Some((ctx, task)) } pub fn dequeue(&mut self, uid: thread::UId) -> Option { @@ -65,57 +105,78 @@ impl Scheduler { self.threads.remove(&uid) } -} -/// Reschedule the tasks. -pub fn reschedule() { - hal::Machine::trigger_reschedule(); -} + pub fn create_task(&mut self, task: &task::Attributes) -> Result { + let uid = task::UId::new(self.id_gen).ok_or(KernelError::InvalidArgument)?; + self.id_gen += 1; -/* + self.tasks.insert(&uid, task::Task::new(uid, task)?); + Ok(uid) + } + pub fn create_thread(&mut self, task: task::UId, attrs: &thread::Attributes) -> Result { + let task = self.tasks.get_mut(task).ok_or(KernelError::InvalidArgument)?; + let thread = task.create_thread(self.id_gen, attrs)?; + let uid = thread.uid(); + self.id_gen += 1; + Ok(uid) + } +} +pub fn init(kaddr_space: mem::vmm::AddressSpace) { + let mut sched = SCHED.lock(); + let uid = task::KERNEL_TASK; + sched.tasks.insert(&uid, task::Task::from_addr_space(uid, kaddr_space)); +} -/// Create a new task. -/// -/// `desc` - The task descriptor. -/// `main_desc` - The main thread descriptor. -/// `main_timing` - The timing information for the main thread. -/// -/// Returns the task ID if the task was created successfully, or an error if the task could not be created. -pub fn create_task(desc: task::TaskDescriptor) -> Result { - enable_scheduler(false); - let res = scheduler::SCHEDULER.lock().create_task(desc); - enable_scheduler(true); +pub fn needs_reschedule(now: u64) -> bool { + let sched = SCHED.lock(); + now >= sched.next_tick +} - res +pub fn create_task(attrs: &task::Attributes) -> Result { + SCHED.lock().create_task(attrs) } -pub fn create_thread( - task_id: task::TaskId, - entry: extern "C" fn(), - fin: Option !>, - timing: thread::Timing, -) -> Result { - enable_scheduler(false); - let res = scheduler::SCHEDULER - .lock() - .create_thread(entry, fin, timing, task_id); - enable_scheduler(true); - - res +pub fn create_thread(task: task::UId, attrs: &thread::Attributes) -> Result { + SCHED.lock().create_thread(task, attrs) } -pub fn enable_scheduler(enable: bool) { - scheduler::set_enabled(enable); +/// Reschedule the tasks. +pub fn reschedule() { + hal::Machine::trigger_reschedule(); } -pub fn tick_scheduler() -> bool { - if !scheduler::enabled() { - return false; +/// cbindgen:ignore +/// cbindgen:no-export +#[unsafe(no_mangle)] +pub extern "C" fn sched_enter(ctx: *mut c_void) -> *mut c_void { + let mut sched = SCHED.lock(); + let mut broken = false; + let old = sched.current; + + if sched.land(ctx).is_err() { + if sched.current == thread::IDLE_THREAD { + BUG!("failed to land the idle thread. something is horribly broken."); + } + + // If we cannot reasonably land. We dequeue the thread. + sched.dequeue(old); + // TODO: Warn + sched.current = thread::IDLE_THREAD; + broken = true; } - scheduler::SCHEDULER.lock().tick() -} + let now = 0; - */ + if let Some((ctx, task)) = sched.do_sched(now, Some(old)) { + if task.id != old.owner() { + dispch::prepare(task); + } + ctx + } else if broken { + BUG!("failed to reschedule after a failed landing. something is horribly broken."); + } else { + ctx + } +} diff --git a/src/sched/dispch.rs b/src/sched/dispch.rs new file mode 100644 index 0000000..0f781b6 --- /dev/null +++ b/src/sched/dispch.rs @@ -0,0 +1,7 @@ +use super::task::Task; + +pub fn prepare(task: &mut Task) { + if task.id.is_kernel() { + // Change task priv. level in HAL. + } +} \ No newline at end of file diff --git a/src/sched/rt.rs b/src/sched/rt.rs index 95dfb62..b5a66b9 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -7,7 +7,7 @@ pub struct Scheduler { pub type ServerView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::RtServer, ThreadMap>; impl Scheduler { - pub fn new() -> Self { + pub const fn new() -> Self { Self { edf: RbTree::new(), } @@ -23,7 +23,7 @@ impl Scheduler { } } - pub fn pick(&mut self, now: u64, storage: &mut ServerView) -> Option { + pub fn pick(&mut self, now: u64, storage: &mut ServerView) -> Option<(thread::UId, u64)> { let id = self.edf.min()?; if storage.get(id)?.budget() == 0 { @@ -33,7 +33,7 @@ impl Scheduler { } // Insert updated the min cache. - self.edf.min() + self.edf.min().and_then(|id| storage.get(id).map(|s| (id, s.budget()))) } pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) { diff --git a/src/sched/task.rs b/src/sched/task.rs new file mode 100644 index 0000000..06bb8d9 --- /dev/null +++ b/src/sched/task.rs @@ -0,0 +1,129 @@ +//! This module provides the basic task and thread structures for the scheduler. +use core::num::NonZero; +use core::borrow::Borrow; + +use envparse::parse_env; +use hal::{Stack}; + +use hal::stack::{Stacklike}; + +use crate::sched::thread; +use crate::{mem, sched}; + +use crate::mem::vmm::{AddressSpacelike}; +use crate::types::traits::ToIndex; +use crate::utils::KernelError; + +pub struct Defaults { + pub stack_pages: usize, +} + +const DEFAULTS: Defaults = Defaults { + stack_pages: parse_env!("OSIRIS_STACKPAGES" as usize), +}; + +pub const KERNEL_TASK: UId = UId { uid: 0 }; + +/// Id of a task. This is unique across all tasks. +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] +pub struct UId { + uid: usize, +} + +impl UId { + pub fn new(uid: usize) -> Option { + if uid == 0 { + None + } else { + Some(Self { uid }) + } + } + + pub fn is_kernel(&self) -> bool { + self.uid == 0 + } +} + +impl ToIndex for UId { + fn to_index>(idx: Option) -> usize { + idx.as_ref().map_or(0, |uid| uid.borrow().uid) + } +} + +pub struct Attributes { + pub resrv_pgs: Option>, +} + +/// The struct representing a task. +pub struct Task { + /// The unique identifier of the task. + pub id: UId, + /// The counter for the thread ids. + tid_cntr: usize, + /// Sets up the memory for the task. + address_space: mem::vmm::AddressSpace, +} + +impl Task { + pub fn new(id: UId, attrs: &Attributes) -> Result { + // TODO: On MMU systems, the resrv_pgs attribute will be ignored, as memory will not be reserved. + let resrv_pgs = attrs.resrv_pgs.ok_or(KernelError::OutOfMemory)?; + let address_space = mem::vmm::AddressSpace::new(resrv_pgs.get())?; + + Ok(Self { + id, + address_space, + tid_cntr: 0, + }) + } + + pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Self { + Self { + id, + address_space, + tid_cntr: 0, + } + } + + fn allocate_tid(&mut self) -> sched::thread::Id { + let tid = self.tid_cntr; + self.tid_cntr += 1; + + sched::thread::Id::new(tid, self.id) + } + + fn allocate_stack( + &mut self, + attrs: &thread::Attributes, + ) -> Result { + let size = DEFAULTS.stack_pages * mem::pfa::PAGE_SIZE; + let start = self.address_space.end().saturating_sub(size); + let region = mem::vmm::Region::new( + start, + size, + mem::vmm::Backing::Uninit, + mem::vmm::Perms::Read | mem::vmm::Perms::Write, + ); + let pa = self.address_space.map(region)?; + + Ok(hal::stack::Descriptor { + top: pa + size, + size: NonZero::new(size).unwrap(), + entry: attrs.entry, + fin: attrs.fin, + }) + } + + pub fn create_thread( + &mut self, + uid: usize, + attrs: &thread::Attributes, + ) -> Result { + let stack = self.allocate_stack(attrs)?; + + let stack = unsafe { Stack::new(stack) }?; + let tid = self.allocate_tid(); + + Ok(sched::thread::Thread::new(tid.get_uid(uid), stack)) + } +} diff --git a/src/sched/thread.rs b/src/sched/thread.rs index f988b97..63c608a 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -2,22 +2,28 @@ use core::{borrow::Borrow, ffi::c_void}; -use hal::Stack; -use hal::stack::Stacklike; +use hal::{Stack, stack::EntryFn}; +use hal::stack::{FinFn, Stacklike}; use macros::TaggedLinks; -use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, sched::task::TaskId, utils::KernelError}; +use crate::sched::task::{self, KERNEL_TASK}; +use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, utils::KernelError}; + +pub const IDLE_THREAD: UId = UId { + uid: 0, + tid: Id { id: 0, owner: KERNEL_TASK }, +}; /// Id of a task. This is only unique within a Task. #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] pub struct Id { id: usize, - owner: TaskId, + owner: task::UId, } #[allow(dead_code)] impl Id { - pub fn new(id: usize, owner: TaskId) -> Self { + pub fn new(id: usize, owner: task::UId) -> Self { Self { id, owner } } @@ -25,7 +31,7 @@ impl Id { self.id } - pub fn owner(&self) -> TaskId { + pub fn owner(&self) -> task::UId { self.owner } @@ -38,7 +44,9 @@ impl Id { #[derive(Clone, Copy, Debug)] #[allow(dead_code)] pub struct UId { + /// A globally unique identifier for the thread. uid: usize, + /// The task-local identifier for the thread. tid: Id, } @@ -47,6 +55,10 @@ impl UId { pub fn tid(&self) -> Id { self.tid } + + pub fn owner(&self) -> task::UId { + self.tid.owner + } } impl PartialEq for UId { @@ -63,15 +75,6 @@ impl Into for UId { } } -impl Default for UId { - fn default() -> Self { - Self { - uid: 0, - tid: Id::new(0, TaskId::User(0)), - } - } -} - impl PartialOrd for UId { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -92,11 +95,6 @@ impl ToIndex for UId { // ------------------------------------------------------------------------- -pub struct Descriptor { - pub tid: Id, - pub stack: Stack, -} - /// The state of a thread. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(dead_code)] @@ -188,6 +186,11 @@ pub struct WakupTree; #[derive(Debug, Clone, Copy)] pub struct RtTree; +pub struct Attributes { + pub entry: EntryFn, + pub fin: Option, +} + /// The struct representing a thread. #[derive(Debug, Clone, Copy)] #[derive(TaggedLinks)] @@ -210,7 +213,7 @@ impl Thread { /// `stack` - The stack of the thread. /// /// Returns a new thread. - fn new(uid: UId, stack: Stack) -> Self { + pub fn new(uid: UId, stack: Stack) -> Self { Self { state: State { run_state: RunState::Ready, @@ -243,6 +246,10 @@ impl Thread { pub fn uid(&self) -> UId { self.uid } + + pub fn task_id(&self) -> task::UId { + self.uid.tid().owner + } } impl Compare for Thread { diff --git a/src/sync/atomic.rs b/src/sync/atomic.rs index 487125c..dec9f98 100644 --- a/src/sync/atomic.rs +++ b/src/sync/atomic.rs @@ -11,10 +11,10 @@ compile_error!( ); // ----------------------------AtomicU8---------------------------- -#[cfg(all(feature = "no-atomic-cas"))] +#[cfg(any(feature = "no-atomic-cas", not(target_has_atomic = "64")))] pub use core::sync::atomic::Ordering; -#[cfg(all(feature = "no-atomic-cas"))] +#[cfg(any(feature = "no-atomic-cas", not(target_has_atomic = "64")))] use core::cell::UnsafeCell; #[cfg(all(feature = "no-atomic-cas"))] @@ -106,3 +106,116 @@ impl AtomicBool { todo!("Implement atomic compare_exchange for bool"); } } + +// ----------------------------AtomicU64---------------------------- +#[allow(unused_imports)] +#[cfg(target_has_atomic = "64")] +pub use core::sync::atomic::AtomicU64; + +#[cfg(not(target_has_atomic = "64"))] +/// An atomic `u64` implemented by disabling interrupts around each operation. +pub struct AtomicU64 { + value: UnsafeCell, +} + +#[cfg(not(target_has_atomic = "64"))] +unsafe impl Sync for AtomicU64 {} + +#[cfg(not(target_has_atomic = "64"))] +impl AtomicU64 { + /// Creates a new atomic u64. + pub const fn new(value: u64) -> Self { + Self { + value: UnsafeCell::new(value), + } + } + + #[inline(always)] + fn with_interrupts_disabled(f: impl FnOnce() -> T) -> T { + let were_enabled = hal::asm::are_interrupts_enabled(); + if were_enabled { + hal::asm::disable_interrupts(); + } + + let result = f(); + + if were_enabled { + hal::asm::enable_interrupts(); + } + + result + } + + /// Loads the value. + pub fn load(&self, _: Ordering) -> u64 { + Self::with_interrupts_disabled(|| { + // SAFETY: Interrupts are disabled, so this read is exclusive with writes. + unsafe { *self.value.get() } + }) + } + + /// Stores a value. + pub fn store(&self, value: u64, _: Ordering) { + Self::with_interrupts_disabled(|| { + // SAFETY: Interrupts are disabled, so this write is exclusive with other access. + unsafe { + *self.value.get() = value; + } + }); + } + + /// Compares the value and exchanges it. + pub fn compare_exchange( + &self, + current: u64, + new: u64, + _: Ordering, + _: Ordering, + ) -> Result { + Self::with_interrupts_disabled(|| { + // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. + unsafe { + let value = self.value.get(); + if *value == current { + *value = new; + Ok(current) + } else { + Err(*value) + } + } + }) + } + + /// Fetches and adds, returning the previous value. + pub fn fetch_add(&self, value: u64, _: Ordering) -> u64 { + Self::with_interrupts_disabled(|| { + // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. + unsafe { + let ptr = self.value.get(); + let old = *ptr; + *ptr = old.wrapping_add(value); + old + } + }) + } + + /// Fetches a value, applies the function and writes it back atomically. + pub fn fetch_update(&self, _: Ordering, _: Ordering, mut f: F) -> Result + where + F: FnMut(u64) -> Option, + { + Self::with_interrupts_disabled(|| { + // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. + unsafe { + let ptr = self.value.get(); + let old = *ptr; + if let Some(new) = f(old) { + *ptr = new; + Ok(old) + } else { + Err(old) + } + } + }) + } +} diff --git a/src/time.rs b/src/time.rs index f8a2fc4..25c73eb 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,15 +1,13 @@ -use crate::{sched, sync::spinlock::SpinLocked}; -use hal::Schedable; +use core::sync::atomic::Ordering; + +use crate::sched; +use crate::sync::atomic::AtomicU64; // This variable is only allowed to be modified by the systick handler. -static TIME: SpinLocked = SpinLocked::new(0); +static TIME: AtomicU64 = AtomicU64::new(0); fn tick() { - // Increment the global time counter. - { - let mut time = TIME.lock(); - *time += 1; - } + TIME.fetch_add(1, Ordering::Release); } /* @@ -18,32 +16,16 @@ fn tick() { */ #[allow(dead_code)] pub fn time() -> u64 { - if !hal::asm::are_interrupts_enabled() { - // If interrupts are disabled, we can just read the time. - return *TIME.lock(); - } else { - let time; - // We need to disable interrupts to ensure that systick is always able to lock the time. - hal::asm::disable_interrupts(); - // Return the current time. - { - time = *TIME.lock(); - } - hal::asm::enable_interrupts(); - // Now systick can be called again. - time - } + TIME.load(Ordering::Acquire) } /// cbindgen:ignore /// cbindgen:no-export #[unsafe(no_mangle)] pub extern "C" fn systick_hndlr() { - tick(); - - let resched = { sched::tick_scheduler() }; + let time = TIME.fetch_add(1, Ordering::Release) + 1; - if resched { - hal::Machine::trigger_reschedule(); + if sched::needs_reschedule(time) { + sched::reschedule(); } } diff --git a/src/types/rbtree.rs b/src/types/rbtree.rs index ac64a17..969df19 100644 --- a/src/types/rbtree.rs +++ b/src/types/rbtree.rs @@ -51,7 +51,7 @@ enum Color { #[allow(dead_code)] impl RbTree { - pub fn new() -> Self { + pub const fn new() -> Self { Self { root: None, min: None, From f06e9c2df723f2c00e07323fdecdb47fcad6e003 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:25:02 +0000 Subject: [PATCH 04/28] more scheduler rewrite --- presets/stm32l4r5zi_def.toml | 1 + src/idle.rs | 4 ++-- src/lib.rs | 11 +++++++++-- src/mem.rs | 7 ++++--- src/sched.rs | 2 +- src/uspace.rs | 10 +++++++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/presets/stm32l4r5zi_def.toml b/presets/stm32l4r5zi_def.toml index c5f9166..9edf803 100644 --- a/presets/stm32l4r5zi_def.toml +++ b/presets/stm32l4r5zi_def.toml @@ -11,6 +11,7 @@ OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" # Tuning parameters OSIRIS_TUNING_ENABLEFPU = "false" +OSIRIS_STACKPAGES = "4" OSIRIS_TUNING_APPSTACKSIZE = "2048" OSIRIS_TUNING_APPMEMSIZE = "8192" diff --git a/src/idle.rs b/src/idle.rs index c3b4060..aa62b17 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -2,13 +2,13 @@ use crate::sched; extern "C" fn entry() { loop { - hal::asm::wfi!(); + hal::asm::nop!(); } } pub fn init() { let attrs = sched::thread::Attributes { - entry: entry, + entry, fin: None, }; if let Err(e) = sched::create_thread(sched::task::KERNEL_TASK, &attrs) { diff --git a/src/lib.rs b/src/lib.rs index f273848..ac046f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); /// The `boot_info` pointer must be valid and point to a properly initialized `BootInfo` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { - hal::asm::disable_interrupts!(); + hal::asm::disable_interrupts(); // Initialize basic hardware and the logging system. hal::Machine::init(); hal::Machine::bench_start(); @@ -51,9 +51,16 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { // Initialize the memory allocator. let kaddr_space = mem::init_memory(boot_info); + kprintln!("[Kernel] Memory initialized."); + sched::init(kaddr_space); + + kprintln!("[Kernel] Scheduler initialized."); + idle::init(); + kprintln!("[Kernel] Idle thread initialized."); + let (cyc, ns) = hal::Machine::bench_end(); kprintln!( "[Osiris] Kernel init took {} cycles taking {} ms", @@ -66,7 +73,7 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { panic!("[Kernel] Error: failed to start init application. Error: {e:?}"); } - hal::asm::enable_interrupts!(); + hal::asm::enable_interrupts(); loop { hal::asm::nop!(); diff --git a/src/mem.rs b/src/mem.rs index f45db97..530f6e5 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,5 +1,6 @@ //! This module provides access to the global memory allocator. +use crate::mem::pfa::PAGE_SIZE; use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region}; use crate::sync::spinlock::SpinLocked; use crate::{BootInfo, sched, utils}; @@ -47,17 +48,17 @@ pub fn init_memory(boot_info: &BootInfo) -> vmm::AddressSpace { // TODO: Configure. let pgs = 4; - let kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { + let mut kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { panic!("[Kernel] Error: failed to create kernel address space."); }); - let begin = kaddr_space.map(Region::new(VirtAddr::new(0), len, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { + let begin = kaddr_space.map(Region::new(VirtAddr::new(0), pgs * PAGE_SIZE, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { panic!("[Kernel] Error: failed to map kernel address space."); }); let mut allocator = GLOBAL_ALLOCATOR.lock(); - let range = begin.as_usize()..(begin.as_usize() + pgs * vmm::PAGE_SIZE); + let range = begin.as_usize()..(begin.as_usize() + pgs * PAGE_SIZE); if let Err(e) = unsafe { allocator.add_range(range) } { panic!("[Kernel] Error: failed to add range to allocator."); diff --git a/src/sched.rs b/src/sched.rs index 076b107..c9acae7 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -10,7 +10,7 @@ use core::ffi::c_void; use hal::Schedable; use crate::{ - sync::spinlock::SpinLocked, types::{ + mem, sync::spinlock::SpinLocked, types::{ array::IndexMap, rbtree::RbTree, traits::{Get, GetMut}, diff --git a/src/uspace.rs b/src/uspace.rs index c0d1d8e..14dcfd9 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -2,6 +2,8 @@ use ::core::mem::transmute; +use crate::sched; + pub fn init_app(boot_info: &crate::BootInfo) -> Result<(), crate::utils::KernelError> { let len = boot_info.args.init.len; @@ -15,8 +17,10 @@ pub fn init_app(boot_info: &crate::BootInfo) -> Result<(), crate::utils::KernelE ) }; - // We don't expect coming back from the init program. - // But for future user mode support the init program will be run by the scheduler, thus we leave a result as a return value here. - entry(); + let attrs = sched::thread::Attributes { + entry, + fin: None, + }; + sched::create_thread(sched::task::KERNEL_TASK, &attrs)?; Ok(()) } From 3f44481acbb7bd3ec8964b061a43a5800c0332f8 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:19:09 +0000 Subject: [PATCH 05/28] should work --- Cargo.toml | 1 + machine/api/src/lib.rs | 5 + machine/api/src/mem.rs | 27 +- machine/arm/common/CMakeLists.txt | 6 - machine/arm/common/crt0.S | 7 - machine/arm/src/lib.rs | 12 + machine/arm/stm32l4xx/CMakeLists.txt | 5 - machine/arm/stm32l4xx/interface/clock.c | 130 +++++ machine/arm/stm32l4xx/interface/export.h | 7 + machine/arm/stm32l4xx/interface/lib.c | 8 +- machine/arm/stm32l4xx/interface/sched.c | 3 +- machine/arm/stm32l4xx/link.ld | 18 +- machine/testing/src/lib.rs | 12 + macros/src/lib.rs | 2 +- macros/src/tree.rs | 43 +- options.toml | 2 +- presets/stm32l4r5zi_def.toml | 7 +- presets/testing_def.toml | 1 + src/idle.rs | 2 +- src/lib.rs | 27 +- src/macros.rs | 8 + src/mem.rs | 36 +- src/mem/alloc.rs | 583 +------------------ src/mem/alloc/bestfit.rs | 705 +++++++++++++++++++++++ src/mem/pfa/bitset.rs | 15 +- src/mem/vmm.rs | 23 +- src/mem/vmm/nommu.rs | 54 +- src/sched.rs | 128 ++-- src/sched/rr.rs | 48 ++ src/sched/task.rs | 14 +- src/sched/thread.rs | 8 + src/sync/atomic.rs | 46 +- src/time.rs | 30 +- src/types.rs | 1 + src/types/list.rs | 302 ++++++++++ src/uspace.rs | 5 +- src/utils.rs | 6 +- 37 files changed, 1552 insertions(+), 785 deletions(-) create mode 100644 machine/arm/stm32l4xx/interface/clock.c create mode 100644 src/mem/alloc/bestfit.rs create mode 100644 src/sched/rr.rs create mode 100644 src/types/list.rs diff --git a/Cargo.toml b/Cargo.toml index b6d40be..0cf5255 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["examples/*", "xtasks", "xtasks/crates/*"] +default-members = ["."] [workspace.dependencies] interface = { path = "interface" } diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index 4480adb..f4ebc52 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -47,6 +47,11 @@ pub trait Machinelike { fn bench_start(); fn bench_end() -> (u32, f32); + fn monotonic_now() -> u64; + fn monotonic_freq() -> u64; + // Returns the frequency of the machine's systick timer in Hz. + fn systick_freq() -> u64; + type ExcepBacktrace: Display; type ExcepStackFrame: Display; fn backtrace(initial_fp: *const usize, stack_ptr: *const usize) -> Self::ExcepBacktrace; diff --git a/machine/api/src/mem.rs b/machine/api/src/mem.rs index b919bfe..4aa98d6 100644 --- a/machine/api/src/mem.rs +++ b/machine/api/src/mem.rs @@ -1,7 +1,7 @@ -use core::ops::{Add, Sub, Div, Rem}; +use core::{fmt::Display, ops::{Add, Div, Rem, Sub}, ptr::NonNull}; #[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct PhysAddr(usize); impl PhysAddr { @@ -32,6 +32,29 @@ impl PhysAddr { pub fn is_multiple_of(&self, align: usize) -> bool { self.0.is_multiple_of(align) } + + pub fn diff(&self, other: Self) -> usize { + if self.0 >= other.0 { + // Cannot underflow because of the check above. + self.0.checked_sub(other.0).unwrap() + } else { + // Cannot underflow because of the check above. + other.0.checked_sub(self.0).unwrap() + } + } +} + +impl From> for PhysAddr { + #[inline] + fn from(ptr: NonNull) -> Self { + Self(ptr.as_ptr() as usize) + } +} + +impl Display for PhysAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "0x{:x}", self.0) + } } impl Add for PhysAddr { diff --git a/machine/arm/common/CMakeLists.txt b/machine/arm/common/CMakeLists.txt index c9111b7..f426b3a 100644 --- a/machine/arm/common/CMakeLists.txt +++ b/machine/arm/common/CMakeLists.txt @@ -28,14 +28,8 @@ foreach(var ${_cache_vars}) endif() endforeach() -# This sets up PIC for .data/.bss access by making all accesses relative to r9. -# r9 is initialized in crt0.S to point to the base of the .data section. -# We need this because the offset between .text and .data is not known at compile time. (relocatable) -add_compile_options(-msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative) - set_property(SOURCE ivt.S APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp") set_property(SOURCE irq.S APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp") -set_property(SOURCE crt0.S APPEND PROPERTY COMPILE_OPTIONS "-fno-pic") add_library(common STATIC ivt.S diff --git a/machine/arm/common/crt0.S b/machine/arm/common/crt0.S index 215c85e..e26deb6 100644 --- a/machine/arm/common/crt0.S +++ b/machine/arm/common/crt0.S @@ -10,13 +10,6 @@ .thumb_func .global bootstrap bootstrap: - /* - * Initialize r9 to point to the start of the .data section. - * This is needed because all .data/.bss accesses are relative to r9. - * We need this because the offset between .text and .data is not known at compile time (relocatable). - */ - ldr r9, =__data_start - @ Copy initialized data from flash to RAM. ldr r0, =__data ldr r1, =__data_start diff --git a/machine/arm/src/lib.rs b/machine/arm/src/lib.rs index 389d14b..31666f5 100644 --- a/machine/arm/src/lib.rs +++ b/machine/arm/src/lib.rs @@ -65,6 +65,18 @@ impl hal_api::Machinelike for ArmMachine { (cycles as u32, ns) } + fn monotonic_now() -> u64 { + unsafe { bindings::monotonic_now() } + } + + fn monotonic_freq() -> u64 { + unsafe { bindings::monotonic_freq() } + } + + fn systick_freq() -> u64 { + unsafe { bindings::systick_freq() } + } + type ExcepBacktrace = excep::ExcepBacktrace; type ExcepStackFrame = excep::ExcepStackFrame; diff --git a/machine/arm/stm32l4xx/CMakeLists.txt b/machine/arm/stm32l4xx/CMakeLists.txt index 569d51b..3e11634 100644 --- a/machine/arm/stm32l4xx/CMakeLists.txt +++ b/machine/arm/stm32l4xx/CMakeLists.txt @@ -30,11 +30,6 @@ foreach(var ${_cache_vars}) endif() endforeach() -# This sets up PIC for .data/.bss access by making all accesses relative to r9. -# r9 is initialized in crt0.S to point to the base of the .data section. -# We need this because the offset between .text and .data is not known at compile time. (relocatable) -add_compile_options(-msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative) - # this will compile our variant_stm32l4xx library add_subdirectory(${OSIRIS_ARM_STM32L4XX_VARIANT}) diff --git a/machine/arm/stm32l4xx/interface/clock.c b/machine/arm/stm32l4xx/interface/clock.c new file mode 100644 index 0000000..ed3acac --- /dev/null +++ b/machine/arm/stm32l4xx/interface/clock.c @@ -0,0 +1,130 @@ +#include "lib.h" +#include + +static volatile uint64_t monotonic_hi = 0; + +static void init_monotonic_timer(void) +{ + const uint32_t target_hz = 1000000U; + uint32_t tim_clk = HAL_RCC_GetPCLK1Freq(); + + monotonic_hi = 0; + + // If APB1 prescaler is not 1, timer clocks run at 2x PCLK1. + if ((RCC->CFGR & RCC_CFGR_PPRE1) != RCC_CFGR_PPRE1_DIV1) { + tim_clk *= 2U; + } + + const uint32_t prescaler = (tim_clk / target_hz) - 1U; + + __HAL_RCC_TIM2_CLK_ENABLE(); + __HAL_RCC_TIM2_FORCE_RESET(); + __HAL_RCC_TIM2_RELEASE_RESET(); + + HAL_NVIC_DisableIRQ(TIM2_IRQn); + NVIC_ClearPendingIRQ(TIM2_IRQn); + + // URS ensures update flags/interrupts are only from real overflows. + TIM2->CR1 = TIM_CR1_URS; + TIM2->PSC = prescaler; + TIM2->ARR = 0xFFFFFFFFU; + TIM2->CNT = 0; + TIM2->EGR = TIM_EGR_UG; + + // Clear pending flags and enable update interrupt for wrap extension. + TIM2->SR = 0; + TIM2->DIER = TIM_DIER_UIE; + + HAL_NVIC_SetPriority(TIM2_IRQn, 15, 0); + HAL_NVIC_EnableIRQ(TIM2_IRQn); + + TIM2->CR1 |= TIM_CR1_CEN; + + // Clear any latent startup update state before first read. + TIM2->SR = 0; + NVIC_ClearPendingIRQ(TIM2_IRQn); +} + +void tim2_hndlr(void) +{ + if ((TIM2->SR & TIM_SR_UIF) != 0U) { + TIM2->SR &= ~TIM_SR_UIF; + monotonic_hi += (1ULL << 32); + } +} + +void SystemClock_Config(void) +{ + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + + /* 80 MHz on STM32L4+ => Range 1 normal mode, not boost */ + __HAL_RCC_PWR_CLK_ENABLE(); + + if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) { + while (1) {} + } + + /* HSI16 -> PLL -> 80 MHz SYSCLK */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; + RCC_OscInitStruct.PLL.PLLM = 1; + RCC_OscInitStruct.PLL.PLLN = 10; + RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // arbitrary unless you use PLLP + RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // arbitrary unless you use PLLQ + + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + while (1) {} + } + + RCC_ClkInitStruct.ClockType = + RCC_CLOCKTYPE_SYSCLK | + RCC_CLOCKTYPE_HCLK | + RCC_CLOCKTYPE_PCLK1 | + RCC_CLOCKTYPE_PCLK2; + + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { + while (1) {} + } + + SystemCoreClockUpdate(); + init_monotonic_timer(); +} + +unsigned long long monotonic_now(void) +{ + uint64_t hi_1; + uint64_t hi_2; + uint32_t lo; + uint32_t sr; + + // Retry if the overflow IRQ updates the high word while sampling. + do { + hi_1 = monotonic_hi; + lo = TIM2->CNT; + sr = TIM2->SR; + hi_2 = monotonic_hi; + } while (hi_1 != hi_2); + + // If overflow is pending but IRQ has not run yet, include that wrap. + if ((sr & TIM_SR_UIF) != 0U && lo < 0x80000000U) { + hi_1 += (1ULL << 32); + } + + return hi_1 | (uint64_t)lo; +} + +unsigned long long monotonic_freq(void) +{ + return 1000000ULL; +} \ No newline at end of file diff --git a/machine/arm/stm32l4xx/interface/export.h b/machine/arm/stm32l4xx/interface/export.h index ce3e398..9d5910b 100644 --- a/machine/arm/stm32l4xx/interface/export.h +++ b/machine/arm/stm32l4xx/interface/export.h @@ -1,6 +1,7 @@ #pragma once // lib.c +unsigned long long systick_freq(void); void init_hal(void); // uart.c @@ -16,3 +17,9 @@ void dwt_reset(void); long dwt_read(void); float dwt_read_ns(void); float dwt_cycles_to_ns(long cycles); + +// clock.c +void SystemClock_Config(void); + +unsigned long long monotonic_now(void); +unsigned long long monotonic_freq(void); diff --git a/machine/arm/stm32l4xx/interface/lib.c b/machine/arm/stm32l4xx/interface/lib.c index 188119c..3c7d723 100644 --- a/machine/arm/stm32l4xx/interface/lib.c +++ b/machine/arm/stm32l4xx/interface/lib.c @@ -15,11 +15,14 @@ static void enable_faults(void) { } static void init_systick(void) { - HAL_SYSTICK_Config(SystemCoreClock / - 10); // Configure SysTick to interrupt every 1 ms + HAL_SYSTICK_Config(SystemCoreClock / 1000); // Configure SysTick to interrupt every 1 ms HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); } +unsigned long long systick_freq(void) { + return 1000; +} + void init_hal(void) { #if OSIRIS_TUNING_ENABLEFPU init_fpu(); @@ -28,6 +31,7 @@ void init_hal(void) { enable_faults(); + SystemClock_Config(); init_systick(); } diff --git a/machine/arm/stm32l4xx/interface/sched.c b/machine/arm/stm32l4xx/interface/sched.c index a301d50..8e90155 100644 --- a/machine/arm/stm32l4xx/interface/sched.c +++ b/machine/arm/stm32l4xx/interface/sched.c @@ -6,4 +6,5 @@ void reschedule(void) { SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; // Trigger PendSV exception __ISB(); __DSB(); -} \ No newline at end of file +} + diff --git a/machine/arm/stm32l4xx/link.ld b/machine/arm/stm32l4xx/link.ld index c964b11..b3e0ee8 100644 --- a/machine/arm/stm32l4xx/link.ld +++ b/machine/arm/stm32l4xx/link.ld @@ -39,15 +39,6 @@ SECTIONS KEEP(*(.ivt.ext)); } > FLASH :text - .stack (NOLOAD) : - { - . = ALIGN(4); - __stack_start = .; - . = . + __stack_size; - . = ALIGN(4); - __stack_top = .; - } > RAM - .text : { *(.text .text.* .gnu.linkonce.t*) @@ -130,6 +121,15 @@ SECTIONS __bss_end = .; } > RAM :data + .stack (NOLOAD) : + { + . = ALIGN(4); + __stack_start = .; + . = . + __stack_size; + . = ALIGN(4); + __stack_top = .; + } > RAM + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } } diff --git a/machine/testing/src/lib.rs b/machine/testing/src/lib.rs index 4236f4f..7c8dc6a 100644 --- a/machine/testing/src/lib.rs +++ b/machine/testing/src/lib.rs @@ -26,6 +26,18 @@ impl hal_api::Machinelike for TestingMachine { (0, 0.0) } + fn monotonic_now() -> u64 { + 0 + } + + fn monotonic_freq() -> u64 { + 0 + } + + fn systick_freq() -> u64 { + 0 + } + type ExcepBacktrace = String; type ExcepStackFrame = String; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c871c86..178322c 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream; mod tree; -#[proc_macro_derive(TaggedLinks, attributes(rbtree))] +#[proc_macro_derive(TaggedLinks, attributes(rbtree, list))] pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(input as syn::DeriveInput); diff --git a/macros/src/tree.rs b/macros/src/tree.rs index d62d8f6..9a37950 100644 --- a/macros/src/tree.rs +++ b/macros/src/tree.rs @@ -23,9 +23,11 @@ pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result) -> syn::Result { + let struct_ident = &input.ident; + let generics = &input.generics; + + let mut impls = Vec::new(); + + for field in fields { + let Some(field_ident) = field.ident.clone() else { continue }; + + if let (Some(tag_path), Some(idx_path)) = find_tagged(&field.attrs, "list")? { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let impl_block = quote! { + impl #impl_generics crate::types::list::Linkable<#tag_path, #idx_path> for #struct_ident #ty_generics #where_clause { + #[inline] + fn links(&self) -> &crate::types::list::Links<#tag_path, #idx_path> { + &self.#field_ident + } + #[inline] + fn links_mut(&mut self) -> &mut crate::types::list::Links<#tag_path, #idx_path> { + &mut self.#field_ident + } + } + }; + + impls.push(impl_block); + } } Ok(quote! { #(#impls)* }) } -fn find_rbtree(attrs: &[syn::Attribute]) -> syn::Result<(Option, Option)> { +fn find_tagged(attrs: &[syn::Attribute], attr_name: &str) -> syn::Result<(Option, Option)> { for attr in attrs { - if !attr.path().is_ident("rbtree") { + if !attr.path().is_ident(attr_name) { continue; } diff --git a/options.toml b/options.toml index 7427cea..eb66692 100644 --- a/options.toml +++ b/options.toml @@ -30,7 +30,7 @@ default = false name = "Stack Pages" description = "Number of pages to allocate for the kernel stack." type = { type = "Integer", min = 1 } -default = 4 +default = 1 [tuning.appmemsize] name = "Application Memory Size" diff --git a/presets/stm32l4r5zi_def.toml b/presets/stm32l4r5zi_def.toml index 9edf803..e637fc4 100644 --- a/presets/stm32l4r5zi_def.toml +++ b/presets/stm32l4r5zi_def.toml @@ -11,7 +11,7 @@ OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" # Tuning parameters OSIRIS_TUNING_ENABLEFPU = "false" -OSIRIS_STACKPAGES = "4" +OSIRIS_STACKPAGES = "1" OSIRIS_TUNING_APPSTACKSIZE = "2048" OSIRIS_TUNING_APPMEMSIZE = "8192" @@ -22,7 +22,4 @@ target = "thumbv7em-none-eabi" [target.'cfg(target_os = "none")'] rustflags = [ "-C", "link-arg=--entry=main", -] - -[target.thumbv7em-none-eabi] -rustflags = ["-C", "relocation-model=ropi-rwpi"] \ No newline at end of file +] \ No newline at end of file diff --git a/presets/testing_def.toml b/presets/testing_def.toml index 1ed804d..59b9c04 100644 --- a/presets/testing_def.toml +++ b/presets/testing_def.toml @@ -1,6 +1,7 @@ # This is the default configuration for running tests and verification. [env] +OSIRIS_STACKPAGES = "1" [build] target = "host-tuple" \ No newline at end of file diff --git a/src/idle.rs b/src/idle.rs index aa62b17..7c79b37 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -12,6 +12,6 @@ pub fn init() { fin: None, }; if let Err(e) = sched::create_thread(sched::task::KERNEL_TASK, &attrs) { - panic!("[Idle] Error: failed to create idle thread. Error: {e:?}"); + panic!("failed to create idle thread. Error: {e:?}"); } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ac046f3..f033cad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,13 +34,12 @@ include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); /// The `boot_info` pointer must be valid and point to a properly initialized `BootInfo` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { - hal::asm::disable_interrupts(); // Initialize basic hardware and the logging system. hal::Machine::init(); hal::Machine::bench_start(); if boot_info.is_null() || !boot_info.is_aligned() { - panic!("[Kernel] Error: boot_info pointer is null or unaligned."); + panic!("boot_info pointer is null or unaligned."); } // Safety: We trust the bootloader to provide a valid boot_info structure. @@ -51,31 +50,29 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { // Initialize the memory allocator. let kaddr_space = mem::init_memory(boot_info); - kprintln!("[Kernel] Memory initialized."); + kprintln!("Memory initialized."); - sched::init(kaddr_space); + if let Err(e) = sched::init(kaddr_space) { + panic!("failed to initialize scheduler. Error: {e:?}"); + } - kprintln!("[Kernel] Scheduler initialized."); + kprintln!("Scheduler initialized."); idle::init(); - kprintln!("[Kernel] Idle thread initialized."); + kprintln!("Idle thread initialized."); let (cyc, ns) = hal::Machine::bench_end(); kprintln!( - "[Osiris] Kernel init took {} cycles taking {} ms", - cyc, - ns as u32 / 1000000 + "Kernel init took {} cycles.", cyc ); // Start the init application. if let Err(e) = uspace::init_app(boot_info) { - panic!("[Kernel] Error: failed to start init application. Error: {e:?}"); + panic!("failed to start init application. Error: {e:?}"); } + + sched::enable(); - hal::asm::enable_interrupts(); - - loop { - hal::asm::nop!(); - } + loop {} } diff --git a/src/macros.rs b/src/macros.rs index 7c3a2e0..9d9d06f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,15 @@ macro_rules! kprintln { ($($arg:tt)*) => ({ use core::fmt::Write; use $crate::print::Printer; + let mut printer = Printer; + const MICROS_PER_SEC: u64 = 1000000; + let hz = $crate::time::mono_freq(); + let secs = $crate::time::mono_now() / hz; + let rem = $crate::time::mono_now() % hz; + let frac = (rem * MICROS_PER_SEC) / hz; + + write!(&mut printer, "[{}.{:06}] ", secs, frac).unwrap(); printer.write_fmt(format_args!($($arg)*)).unwrap(); printer.write_str("\n").unwrap(); }); diff --git a/src/mem.rs b/src/mem.rs index 530f6e5..a01d32e 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -3,7 +3,7 @@ use crate::mem::pfa::PAGE_SIZE; use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region}; use crate::sync::spinlock::SpinLocked; -use crate::{BootInfo, sched, utils}; +use crate::BootInfo; use alloc::Allocator; use hal::mem::{PhysAddr, VirtAddr}; use core::ptr::NonNull; @@ -31,9 +31,13 @@ enum MemoryTypes { BadMemory = 5, } +unsafe extern "C" { + unsafe static __stack_top: u8; +} + /// The global memory allocator. -static GLOBAL_ALLOCATOR: SpinLocked = - SpinLocked::new(alloc::BestFitAllocator::new()); +static GLOBAL_ALLOCATOR: SpinLocked = + SpinLocked::new(alloc::bestfit::BestFitAllocator::new()); /// Initialize the memory allocator. /// @@ -41,27 +45,29 @@ static GLOBAL_ALLOCATOR: SpinLocked = /// /// Returns an error if the memory allocator could not be initialized. pub fn init_memory(boot_info: &BootInfo) -> vmm::AddressSpace { - if let Err(e) = pfa::init_pfa(PhysAddr::new(0x20000000)) { // TODO: Get this from the DeviceTree. - panic!("[Kernel] Error: failed to initialize PFA. Error: {e:?}"); + let stack_top = &raw const __stack_top as usize; + if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) { // TODO: Get this from the DeviceTree. + panic!("failed to initialize PFA. Error: {e:?}"); } // TODO: Configure. - let pgs = 4; + let pgs = 10; let mut kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { - panic!("[Kernel] Error: failed to create kernel address space."); + panic!("failed to create kernel address space. Error: {e:?}"); }); - let begin = kaddr_space.map(Region::new(VirtAddr::new(0), pgs * PAGE_SIZE, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { - panic!("[Kernel] Error: failed to map kernel address space."); + let begin = kaddr_space.map(Region::new(None, 2 * PAGE_SIZE, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { + panic!("failed to map kernel address space. Error: {e:?}"); }); - let mut allocator = GLOBAL_ALLOCATOR.lock(); - - let range = begin.as_usize()..(begin.as_usize() + pgs * PAGE_SIZE); + { + let mut allocator = GLOBAL_ALLOCATOR.lock(); - if let Err(e) = unsafe { allocator.add_range(range) } { - panic!("[Kernel] Error: failed to add range to allocator."); + let range = begin..(begin + pgs * PAGE_SIZE); + if let Err(e) = unsafe { allocator.add_range(&range) } { + panic!("failed to add range to allocator. Error: {e:?}"); + } } kaddr_space @@ -75,7 +81,7 @@ pub fn init_memory(boot_info: &BootInfo) -> vmm::AddressSpace { /// Returns a pointer to the allocated memory block if the allocation was successful, or `None` if the allocation failed. pub fn malloc(size: usize, align: usize) -> Option> { let mut allocator = GLOBAL_ALLOCATOR.lock(); - allocator.malloc(size, align).ok() + allocator.malloc(size, align, None).ok() } /// Free a memory block. diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index 415bccc..85d8830 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -1,9 +1,13 @@ //! This module provides a simple allocator. //! One implementation is the BestFitAllocator, which uses the best fit strategy. -use core::{ops::Range, ptr::NonNull}; +use core::ptr::NonNull; -use crate::{BUG_ON, utils}; +use hal::mem::PhysAddr; + +use crate::utils; + +pub mod bestfit; #[cfg(target_pointer_width = "64")] pub const MAX_ADDR: usize = 2_usize.pow(48); @@ -21,580 +25,7 @@ pub const MAX_ADDR: usize = usize::MAX; /// Each range added to the allocator must be valid for the whole lifetime of the allocator and must not overlap with any other range. /// The lifetime of any allocation is only valid as long as the allocator is valid. (A pointer must not be used after the allocator is dropped.) pub trait Allocator { - fn malloc(&mut self, size: usize, align: usize) -> Result, utils::KernelError>; + fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result, utils::KernelError>; unsafe fn free(&mut self, ptr: NonNull, size: usize); } -/// The metadata that is before any block in the BestFitAllocator. -struct BestFitMeta { - /// The size of the block in bytes. - size: usize, - /// The pointer to the next free block. This is `None` if the block is allocated. - next: Option>, -} - -/// This is an allocator implementation that uses the best fit strategy. -/// That does mean, when we allocate a block, we try to find the smallest block that fits the requested size. -/// Blocks are stored in a singly linked list. The important part is that the linked list is stored in-line with the memory blocks. -/// This means that every block has a header that contains the size of the block and a pointer to the next block. -#[derive(Debug)] -pub struct BestFitAllocator { - /// Head of the free block list. - head: Option>, -} - -/// Implementation of the BestFitAllocator. -impl BestFitAllocator { - pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; - - /// Creates a new BestFitAllocator. - /// - /// Returns the new BestFitAllocator. - pub const fn new() -> Self { - Self { head: None } - } - - /// Adds a range of memory to the allocator. - /// - /// `range` - The range of memory to add. - /// - /// Returns `Ok(())` if the range was added successfully, otherwise an error. - /// - /// # Safety - /// - /// The range must be valid, 128bit aligned and must not overlapping with any other current or future range. - /// The range must also be at least as large as `MIN_RANGE_SIZE`. - /// Also the range must stay valid, for the whole lifetime of the allocator. Also the lifetime of any allocation is only valid as long as the allocator is valid. - pub unsafe fn add_range(&mut self, range: Range) -> Result<(), utils::KernelError> { - let ptr = range.start; - - // Check if the pointer is 128bit aligned. - if !ptr.is_multiple_of(align_of::()) { - return Err(utils::KernelError::InvalidAlign); - } - - if ptr == 0 { - return Err(utils::KernelError::InvalidAddress); - } - - debug_assert!(range.end > range.start); - debug_assert!(range.end - range.start > size_of::() + Self::align_up()); - debug_assert!(range.end <= isize::MAX as usize); - - // The user pointer is the pointer to the user memory. So we need to add the size of the meta data and possibly add padding. - let user_pointer = ptr + size_of::() + Self::align_up(); - - // Set the current head as the next block, so we can add the new block to the head. - let meta = BestFitMeta { - size: range.end - user_pointer, - next: self.head, - }; - - // Write the header to the memory. - unsafe { core::ptr::write(ptr as *mut BestFitMeta, meta) }; - - // Set the head to the new block. - self.head = Some(unsafe { NonNull::new_unchecked(ptr as *mut u8) }); - Ok(()) - } - - /// Calculates the padding required to align the block. Note: We only align to 128bit. - /// - /// Returns the padding in bytes. - const fn align_up() -> usize { - let meta = size_of::(); - let align = align_of::(); - // Calculate the padding required to align the block. - (align - (meta % align)) % align - } - - /// Selects the best fit block for the given size. - /// - /// `size` - The size of the block. - /// - /// Returns the control pointer to the block and the control pointer to the previous block. - fn select_block( - &mut self, - size: usize, - ) -> Result<(NonNull, Option>), utils::KernelError> { - let mut best_fit = Err(utils::KernelError::OutOfMemory); - let mut best_fit_size = usize::MAX; - - let mut current = self.head; - let mut prev = None; - - // Iterate over all blocks and find the best fit. - while let Some(ptr) = current { - // Get the metadata of the block. - let meta = unsafe { ptr.cast::().as_ref() }; - - // Check if the block is big enough and smaller than the current best fit. - if meta.size >= size && meta.size <= best_fit_size { - best_fit = Ok((ptr, prev)); - best_fit_size = meta.size; - } - - // Move to the next block. - prev = current; - current = meta.next; - } - - best_fit - } - - /// Calculates the user pointer from the control pointer. - /// - /// `ptr` - The control pointer. - /// - /// Returns the user pointer. - /// - /// # Safety - /// - /// The ptr must be a valid control pointer. Note: After the allocator which allocated the pointer is dropped, the control pointer is always considered invalid. - unsafe fn user_ptr(ptr: NonNull) -> NonNull { - debug_assert!( - (ptr.as_ptr() as usize) - <= isize::MAX as usize - size_of::() - Self::align_up() - ); - unsafe { ptr.byte_add(size_of::() + Self::align_up()) } - } - - /// Calculates the control pointer from the user pointer. - /// - /// `ptr` - The user pointer. - /// - /// Returns the control pointer. - /// - /// # Safety - /// - /// The ptr must be a valid user pointer. Note: After the allocator which allocated the pointer is dropped, the user pointer is always considered invalid. - unsafe fn control_ptr(ptr: NonNull) -> NonNull { - debug_assert!((ptr.as_ptr() as usize) > size_of::() + Self::align_up()); - unsafe { ptr.byte_sub(size_of::() + Self::align_up()) } - } -} - -/// Implementation of the Allocator trait for BestFitAllocator. -impl Allocator for BestFitAllocator { - /// Allocates a block of memory with the given size and alignment. Note: This function will always yield an invalid align for align > 128bit. - /// - /// `size` - The size of the block. - /// `align` - The alignment of the block. - /// - /// Returns the user pointer to the block if successful, otherwise an error. - fn malloc(&mut self, size: usize, align: usize) -> Result, utils::KernelError> { - // Check if the alignment is valid. - if align > align_of::() { - return Err(utils::KernelError::InvalidAlign); - } - - // Check if the size is valid. - if size == 0 { - return Err(utils::KernelError::InvalidSize); - } - - // For some cfg this warning is correct. But for others its not. - #[allow(clippy::absurd_extreme_comparisons)] - if size >= MAX_ADDR { - return Err(utils::KernelError::InvalidSize); - } - - // Align the size. - let aligned_size = super::align_up(size); - debug_assert!(aligned_size >= size); - debug_assert!(aligned_size <= isize::MAX as usize); - - // Find the best fit block. - let (split, block, prev) = match self.select_block(aligned_size) { - Ok((block, prev)) => { - // Get the metadata of the block. - let meta = unsafe { block.cast::().as_mut() }; - - // Calculate the amount of bytes until the beginning of the possibly next metadata. - let min = aligned_size.saturating_add(size_of::() + Self::align_up()); - - debug_assert!( - (block.as_ptr() as usize) - <= isize::MAX as usize - - meta.size - - size_of::() - - Self::align_up() - ); - - debug_assert!( - meta.size < isize::MAX as usize - size_of::() - Self::align_up() - ); - - // If the block is big enough to split. Then it also needs to be big enough to store the metadata + align of the next block. - if meta.size > min { - // Calculate the remaining size of the block and thus the next metadata. - let remaining_meta = BestFitMeta { - size: meta.size - min, - next: meta.next, - }; - - // Shrink the current block to the requested aligned_size + padding (which is not available to the user). - meta.size = aligned_size; - - // Calculate the pointer to the next metadata. - let ptr = unsafe { Self::user_ptr(block).byte_add(aligned_size) }; - - unsafe { - // Write the new metadata to the memory. - ptr.cast::().write(remaining_meta); - } - - // If there is a previous block, we insert the new block after it. Otherwise we set it as the new head. - if let Some(prev) = prev { - let prev_meta = unsafe { prev.cast::().as_mut() }; - prev_meta.next = Some(ptr); - } else { - self.head = Some(ptr); - } - - // The next block of an allocated block is always None. - meta.next = None; - - (true, block, prev) - } else { - (false, block, prev) - } - } - Err(_) => { - let (block, prev) = self.select_block(size)?; - (false, block, prev) - } - }; - - if !split { - // Get the metadata of the block. - let meta = unsafe { block.cast::().as_mut() }; - - if let Some(prev) = prev { - let prev_meta = unsafe { prev.cast::().as_mut() }; - // If there is a previous block, we remove the current block from the list. Ie. we set the next block of the previous block to the next block of the current block. - prev_meta.next = meta.next; - } else { - // If there is no previous block, we set the next block as the new head. - self.head = meta.next; - } - - // The next block of an allocated block is always None. - meta.next = None; - } - - // Return the user pointer. - Ok(unsafe { Self::user_ptr(block).cast() }) - } - - /// Frees a block of memory. - /// - /// `ptr` - The pointer to the block. - /// `size` - The size of the block. (This is used to check if the size of the block is correct.) - unsafe fn free(&mut self, ptr: NonNull, size: usize) { - let block = unsafe { Self::control_ptr(ptr.cast()) }; - let meta = unsafe { block.cast::().as_mut() }; - - // The next block of a free block is always the current head. We essentially insert the block at the beginning of the list. - meta.next = self.head; - - // Check if the size of the block is correct. - BUG_ON!(meta.size != super::align_up(size), "Invalid size in free()"); - - // Set the size of the block. - meta.size = size; - - // Set the block as the new head. - self.head = Some(block); - } -} - -// TESTING ------------------------------------------------------------------------------------------------------------ - -#[cfg(test)] -mod tests { - use super::*; - - fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { - let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; - let meta = unsafe { control_ptr.cast::().as_ref() }; - - assert!(meta.size >= size); - assert_eq!(meta.next, next); - } - - fn verify_ptrs_not_overlaping(ptrs: &[(NonNull, usize)]) { - for (i, (ptr1, size1)) in ptrs.iter().enumerate() { - for (j, (ptr2, size2)) in ptrs.iter().enumerate() { - if i == j { - continue; - } - - let begin1 = ptr1.as_ptr() as usize; - let end1 = begin1 + size1; - let begin2 = ptr2.as_ptr() as usize; - let end2 = begin2 + size2; - - assert!(end1 <= begin2 || end2 <= begin1); - assert!(begin1 != begin2); - assert!(end1 != end2); - assert!(*size1 > 0); - assert!(*size2 > 0); - assert!(end1 > begin1); - assert!(end2 > begin2); - } - } - } - - fn alloc_range(length: usize) -> Range { - let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); - let ptr = unsafe { std::alloc::alloc(alloc_range) }; - ptr as usize..ptr as usize + length - } - - #[test] - fn allocate_one() { - let mut allocator = BestFitAllocator::new(); - - let range = alloc_range(4096); - unsafe { - allocator.add_range(range).unwrap(); - } - - let ptr = allocator.malloc(128, 1).unwrap(); - - verify_block(ptr, 128, None); - } - - #[test] - fn alloc_alot() { - let mut allocator = BestFitAllocator::new(); - const CNT: usize = 100; - const SIZE: usize = 128; - - let range = alloc_range(SIZE * CNT * 2); - unsafe { - allocator.add_range(range).unwrap(); - } - - let mut ptrs = Vec::new(); - for _ in 0..CNT { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push((ptr, SIZE)); - } - - verify_ptrs_not_overlaping(ptrs.as_slice()); - } - - #[test] - fn alloc_exact() { - let mut allocator = BestFitAllocator::new(); - const CNT: usize = 10; - const SIZE: usize = 128; - - let range = - alloc_range((SIZE + size_of::() + BestFitAllocator::align_up()) * CNT); - unsafe { - allocator.add_range(range).unwrap(); - } - - let mut ptrs = Vec::new(); - for _ in 0..CNT { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push((ptr, SIZE)); - } - - verify_ptrs_not_overlaping(ptrs.as_slice()); - } - - #[test] - fn alloc_oom() { - let mut allocator = BestFitAllocator::new(); - const CNT: usize = 10; - const SIZE: usize = 128; - - let range = - alloc_range((SIZE + size_of::() + BestFitAllocator::align_up()) * CNT - 1); - unsafe { - allocator.add_range(range).unwrap(); - } - - let mut ptrs = Vec::new(); - for _ in 0..CNT - 1 { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push(ptr); - } - - let ptr = allocator.malloc::(SIZE, 1); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); - } - - #[test] - fn alloc_no_oom_through_free() { - let mut allocator = BestFitAllocator::new(); - const SIZE: usize = 128; - - let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); - unsafe { - allocator.add_range(range).unwrap(); - } - - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - - unsafe { - allocator.free(ptr, SIZE); - } - - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - } - - #[test] - fn multi_range_alloc() { - let mut allocator = BestFitAllocator::new(); - const CNT: usize = 10; - const SIZE: usize = 128; - - let mut ranges = Vec::new(); - for _ in 0..CNT { - let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); - unsafe { - allocator.add_range(range.clone()).unwrap(); - } - ranges.push(range); - } - - let mut ptrs = Vec::new(); - for _ in 0..CNT { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push((ptr, SIZE)); - } - - verify_ptrs_not_overlaping(ptrs.as_slice()); - } - - #[test] - fn multi_range_no_oom_through_free() { - // This function allocates multiple ranges and then frees one of them randomly. And only then there is no oom. - let mut allocator = BestFitAllocator::new(); - - const CNT: usize = 10; - const SIZE: usize = 128; - - let mut ranges = Vec::new(); - for _ in 0..CNT { - let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); - unsafe { - allocator.add_range(range.clone()).unwrap(); - } - ranges.push(range); - } - - let mut ptrs = Vec::new(); - - let ptr = allocator.malloc::(SIZE, 1).unwrap(); - - for _ in 0..CNT - 1 { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push((ptr, SIZE)); - } - - unsafe { - allocator.free(ptr, SIZE); - } - - let ptr = allocator.malloc(SIZE, 1).unwrap(); - ptrs.push((ptr, SIZE)); - - verify_ptrs_not_overlaping(ptrs.as_slice()); - } - - #[test] - fn multi_range_oom() { - // This function allocates multiple ranges and then frees one of them randomly. And only then there is no oom. - let mut allocator = BestFitAllocator::new(); - - const CNT: usize = 10; - const SIZE: usize = 128; - - let mut ranges = Vec::new(); - for _ in 0..CNT { - let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); - unsafe { - allocator.add_range(range.clone()).unwrap(); - } - ranges.push(range); - } - - let mut ptrs = Vec::new(); - - for _ in 0..CNT { - let ptr = allocator.malloc(SIZE, 1).unwrap(); - verify_block(ptr, SIZE, None); - ptrs.push((ptr, SIZE)); - } - - let ptr = allocator.malloc::(SIZE, 1); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); - - verify_ptrs_not_overlaping(ptrs.as_slice()); - } -} - -// END TESTING -------------------------------------------------------------------------------------------------------- - -// VERIFICATION ------------------------------------------------------------------------------------------------------- -#[cfg(kani)] -mod verification { - use super::*; - use core::{alloc::Layout, ptr}; - - fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { - let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; - let meta = unsafe { control_ptr.cast::().as_ref() }; - - assert!(meta.size >= size); - assert_eq!(meta.next, next); - } - - fn alloc_range(length: usize) -> Option> { - let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); - let ptr = unsafe { std::alloc::alloc(alloc_range) }; - - if ptr.is_null() || ((ptr as usize) >= isize::MAX as usize - length) { - None - } else { - Some(ptr as usize..ptr as usize + length) - } - } - - #[kani::proof] - #[kani::unwind(2)] - fn allocate_one() { - let mut allocator = BestFitAllocator::new(); - - let size: usize = kani::any(); - kani::assume(size < MAX_ADDR - size_of::() - BestFitAllocator::align_up()); - kani::assume(size > 0); - let larger_size: usize = kani::any_where(|&x| { - x > size + size_of::() + BestFitAllocator::align_up() && x < MAX_ADDR - }); - - if let Some(range) = alloc_range(larger_size) { - unsafe { - assert_eq!(allocator.add_range(range), Ok(())); - } - - let ptr = allocator.malloc(size, 1).unwrap(); - - verify_block(ptr, size, None); - } - } -} -// END VERIFICATION --------------------------------------------------------------------------------------------------- diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs new file mode 100644 index 0000000..a14c251 --- /dev/null +++ b/src/mem/alloc/bestfit.rs @@ -0,0 +1,705 @@ +use core::{ops::Range, ptr::NonNull}; + +use hal::mem::PhysAddr; + +use crate::utils::{self, KernelError}; + +/// The metadata that is before any block in the BestFitAllocator. +struct BestFitMeta { + /// The size of the block in bytes. + size: usize, + /// The pointer to the next free block. This is `None` if the block is allocated. + next: Option>, +} + +/// This is an allocator implementation that uses the best fit strategy. +/// That does mean, when we allocate a block, we try to find the smallest block that fits the requested size. +/// Blocks are stored in a singly linked list. The important part is that the linked list is stored in-line with the memory blocks. +/// This means that every block has a header that contains the size of the block and a pointer to the next block. +#[derive(Debug)] +pub struct BestFitAllocator { + /// Head of the free block list. + head: Option>, +} + +/// Implementation of the BestFitAllocator. +impl BestFitAllocator { + pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; + + /// Creates a new BestFitAllocator. + /// + /// Returns the new BestFitAllocator. + pub const fn new() -> Self { + Self { head: None } + } + + /// Adds a range of memory to the allocator. + /// + /// `range` - The range of memory to add. + /// + /// Returns `Ok(())` if the range was added successfully, otherwise an error. + /// + /// # Safety + /// + /// The range must be valid, 128bit aligned and must not overlapping with any other current or future range. + /// The range must also be at least as large as `MIN_RANGE_SIZE`. + /// Also the range must stay valid, for the whole lifetime of the allocator. Also the lifetime of any allocation is only valid as long as the allocator is valid. + pub unsafe fn add_range(&mut self, range: &Range) -> Result<(), utils::KernelError> { + let ptr = range.start; + + // Check if the pointer is 128bit aligned. + if !ptr.is_multiple_of(align_of::()) { + return Err(utils::KernelError::InvalidAlign); + } + + debug_assert!(range.end > range.start); + debug_assert!(range.end.diff(range.start) > size_of::() + Self::align_up()); + debug_assert!(range.end.as_usize() <= isize::MAX as usize); + + // The user pointer is the pointer to the user memory. So we need to add the size of the meta data and possibly add padding. + let user_pointer = ptr + size_of::() + Self::align_up(); + + // Set the current head as the next block, so we can add the new block to the head. + let meta = BestFitMeta { + size: range.end.diff(user_pointer), + next: self.head, + }; + + // Write the header to the memory. + unsafe { core::ptr::write(ptr.as_mut_ptr::(), meta) }; + + // Set the head to the new block. + self.head = Some(unsafe { NonNull::new_unchecked(ptr.as_mut_ptr::()) }); + Ok(()) + } + + /// Calculates the padding required to align the block. Note: We only align to 128bit. + /// + /// Returns the padding in bytes. + const fn align_up() -> usize { + let meta = size_of::(); + let align = align_of::(); + // Calculate the padding required to align the block. + (align - (meta % align)) % align + } + + /// Selects the best fit block for the given size. + /// + /// `size` - The size of the block. + /// + /// Returns the control pointer to the block and the control pointer to the previous block. + fn select_block( + &mut self, + size: usize, + requested: Option, + ) -> Result<(NonNull, Option>), utils::KernelError> { + let mut best_fit = Err(utils::KernelError::OutOfMemory); + let mut best_fit_size = usize::MAX; + + let mut current = self.head; + let mut prev = None; + + if let Some(requested) = requested { + while let Some(ptr) = current { + // Get the metadata of the block. + let meta = unsafe { ptr.cast::().as_ref() }; + + if unsafe { Self::contains(meta, requested, size) } { + return Ok((ptr, prev)); + } + + // Move to the next block. + prev = current; + current = meta.next; + } + } + + // Iterate over all blocks and find the best fit. + while let Some(ptr) = current { + // Get the metadata of the block. + let meta = unsafe { ptr.cast::().as_ref() }; + + // Check if the block is big enough and smaller than the current best fit. + if meta.size >= size && meta.size <= best_fit_size { + best_fit = Ok((ptr, prev)); + best_fit_size = meta.size; + } + + // Move to the next block. + prev = current; + current = meta.next; + } + + best_fit + } + + /// Calculates the user pointer from the control pointer. + /// + /// `ptr` - The control pointer. + /// + /// Returns the user pointer. + /// + /// # Safety + /// + /// The ptr must be a valid control pointer. Note: After the allocator which allocated the pointer is dropped, the control pointer is always considered invalid. + unsafe fn user_ptr(ptr: NonNull) -> NonNull { + debug_assert!( + (ptr.as_ptr() as usize) + <= isize::MAX as usize - size_of::() - Self::align_up() + ); + unsafe { ptr.byte_add(size_of::() + Self::align_up()) } + } + + /// Calculates the control pointer from the user pointer. + /// + /// `ptr` - The user pointer. + /// + /// Returns the control pointer. + /// + /// # Safety + /// + /// The ptr must be a valid user pointer. Note: After the allocator which allocated the pointer is dropped, the user pointer is always considered invalid. + unsafe fn control_ptr(ptr: NonNull) -> NonNull { + debug_assert!((ptr.as_ptr() as usize) > size_of::() + Self::align_up()); + unsafe { ptr.byte_sub(size_of::() + Self::align_up()) } + } + + unsafe fn contains(meta: &BestFitMeta, target: PhysAddr, size: usize) -> bool { + let begin = unsafe { Self::user_ptr(NonNull::new_unchecked(meta as *const BestFitMeta as *mut u8)) }; + debug_assert!(size > 0); + + if target >= begin.into() { + if let Some(target) = target.checked_add(size) { + if target > (unsafe { begin.add(meta.size) }).into() { + return false; + } + } else { + return false; + } + return true; + } + false + } +} + +/// Implementation of the Allocator trait for BestFitAllocator. +impl super::Allocator for BestFitAllocator { + /// Allocates a block of memory with the given size and alignment. Note: This function will always yield an invalid align for align > 128bit. + /// + /// `size` - The size of the block. + /// `align` - The alignment of the block. + /// + /// Returns the user pointer to the block if successful, otherwise an error. + fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result, utils::KernelError> { + // Check if the alignment is valid. + if align > align_of::() { + return Err(utils::KernelError::InvalidAlign); + } + + if let Some(request) = request { + if !request.is_multiple_of(align) { + return Err(utils::KernelError::InvalidAlign); + } + } + + // Check if the size is valid. + if size == 0 { + return Err(utils::KernelError::InvalidSize); + } + + // For some cfg this warning is correct. But for others its not. + #[allow(clippy::absurd_extreme_comparisons)] + if size >= super::MAX_ADDR { + return Err(utils::KernelError::InvalidSize); + } + + // Align the size. + let aligned_size = super::super::align_up(size); + debug_assert!(aligned_size >= size); + debug_assert!(aligned_size <= isize::MAX as usize); + + // Find the best fit block. + let (split, block, prev) = match self.select_block(aligned_size, request) { + Ok((block, prev)) => { + // Get the metadata of the block. + let meta = unsafe { block.cast::().as_mut() }; + + // If we requested a specific address. The size must be extended by the offset from block start to the requested address. + let aligned_size = if let Some(request) = request { + aligned_size + request.diff(unsafe { Self::user_ptr(block) }.into()) + } else { + aligned_size + }; + + // Calculate the amount of bytes until the beginning of the possibly next metadata. + let min = aligned_size.saturating_add(size_of::() + Self::align_up()); + + debug_assert!( + (block.as_ptr() as usize) + <= isize::MAX as usize + - meta.size + - size_of::() + - Self::align_up() + ); + + debug_assert!( + meta.size < isize::MAX as usize - size_of::() - Self::align_up() + ); + + // If the block is big enough to split. Then it also needs to be big enough to store the metadata + align of the next block. + if meta.size > min { + // Calculate the remaining size of the block and thus the next metadata. + let remaining_meta = BestFitMeta { + size: meta.size - min, + next: meta.next, + }; + + // Shrink the current block to the requested aligned_size + padding (which is not available to the user). + meta.size = aligned_size; + + // Calculate the pointer to the next metadata. + let ptr = unsafe { Self::user_ptr(block).byte_add(aligned_size) }; + + unsafe { + // Write the new metadata to the memory. + ptr.cast::().write(remaining_meta); + } + + // If there is a previous block, we insert the new block after it. Otherwise we set it as the new head. + if let Some(prev) = prev { + let prev_meta = unsafe { prev.cast::().as_mut() }; + prev_meta.next = Some(ptr); + } else { + self.head = Some(ptr); + } + + // The next block of an allocated block is always None. + meta.next = None; + + (true, block, prev) + } else { + (false, block, prev) + } + } + Err(_) => { + let (block, prev) = self.select_block(size, request)?; + (false, block, prev) + } + }; + + if !split { + // Get the metadata of the block. + let meta = unsafe { block.cast::().as_mut() }; + + if let Some(prev) = prev { + let prev_meta = unsafe { prev.cast::().as_mut() }; + // If there is a previous block, we remove the current block from the list. Ie. we set the next block of the previous block to the next block of the current block. + prev_meta.next = meta.next; + } else { + // If there is no previous block, we set the next block as the new head. + self.head = meta.next; + } + + // The next block of an allocated block is always None. + meta.next = None; + } + + if let Some(request) = request { + debug_assert!(unsafe { Self::contains(block.cast::().as_ref(), request, size) }); + } + + // Return the user pointer. + Ok(unsafe { Self::user_ptr(block).cast() }) + } + + /// Frees a block of memory. + /// + /// `ptr` - The pointer to the block. + /// `size` - The size of the block. (This is used to check if the size of the block is correct.) + unsafe fn free(&mut self, ptr: NonNull, size: usize) { + let block = unsafe { Self::control_ptr(ptr.cast()) }; + let meta = unsafe { block.cast::().as_mut() }; + + // The next block of a free block is always the current head. We essentially insert the block at the beginning of the list. + meta.next = self.head; + + // Check if the size of the block is correct. + BUG_ON!(meta.size != super::super::align_up(size), "Invalid size in free()"); + + // Set the size of the block. + meta.size = size; + + // Set the block as the new head. + self.head = Some(block); + } +} + +// TESTING ------------------------------------------------------------------------------------------------------------ + +#[cfg(test)] +mod tests { + use super::*; + use super::super::*; + + fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { + let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; + let meta = unsafe { control_ptr.cast::().as_ref() }; + + assert!(meta.size >= size); + assert_eq!(meta.next, next); + } + + fn verify_ptrs_not_overlaping(ptrs: &[(NonNull, usize)]) { + for (i, (ptr1, size1)) in ptrs.iter().enumerate() { + for (j, (ptr2, size2)) in ptrs.iter().enumerate() { + if i == j { + continue; + } + + let begin1 = ptr1.as_ptr() as usize; + let end1 = begin1 + size1; + let begin2 = ptr2.as_ptr() as usize; + let end2 = begin2 + size2; + + assert!(end1 <= begin2 || end2 <= begin1); + assert!(begin1 != begin2); + assert!(end1 != end2); + assert!(*size1 > 0); + assert!(*size2 > 0); + assert!(end1 > begin1); + assert!(end2 > begin2); + } + } + } + + fn alloc_range(length: usize) -> Range { + let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); + let ptr = unsafe { std::alloc::alloc(alloc_range) }; + PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize + length) + } + + #[test] + fn allocate_one() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let ptr = allocator.malloc(128, 1, None).unwrap(); + + verify_block(ptr, 128, None); + } + + #[test] + fn alloc_request() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let request = range.start + 128; + let ptr = allocator.malloc::(128, 1, Some(request)).unwrap(); + + // Check that the returned pointer contains the requested address. + let meta = unsafe { BestFitAllocator::control_ptr(ptr).cast::().as_ref() }; + assert!(unsafe { BestFitAllocator::contains(meta, request, 128) }); + } + + #[test] + fn alloc_request_to_big() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let request = range.start + 4096; + let ptr = allocator.malloc::(128, 1, Some(request)); + + assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + } + + #[test] + fn alloc_request_not_aligned() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let request = range.start + 127; + let ptr = allocator.malloc::(128, 8, Some(request)); + + assert!(ptr.is_err_and(|e| e == utils::KernelError::InvalidAlign)); + } + + #[test] + fn alloc_request_not_available() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let request = range.start + 128; + let ptr = allocator.malloc::(128, 1, Some(request)).unwrap(); + verify_block(ptr, 128, None); + + let ptr = allocator.malloc::(128, 1, Some(request)); + assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + } + + #[test] + fn alloc_request_out_of_range() { + let mut allocator = BestFitAllocator::new(); + + let range = alloc_range(4096); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let request = range.end + 128; + let ptr = allocator.malloc::(128, 1, Some(request)); + + assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + } + + #[test] + fn alloc_alot() { + let mut allocator = BestFitAllocator::new(); + const CNT: usize = 100; + const SIZE: usize = 128; + + let range = alloc_range(SIZE * CNT * 2); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let mut ptrs = Vec::new(); + for _ in 0..CNT { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push((ptr, SIZE)); + } + + verify_ptrs_not_overlaping(ptrs.as_slice()); + } + + #[test] + fn alloc_exact() { + let mut allocator = BestFitAllocator::new(); + const CNT: usize = 10; + const SIZE: usize = 128; + + let range = + alloc_range((SIZE + size_of::() + BestFitAllocator::align_up()) * CNT); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let mut ptrs = Vec::new(); + for _ in 0..CNT { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push((ptr, SIZE)); + } + + verify_ptrs_not_overlaping(ptrs.as_slice()); + } + + #[test] + fn alloc_oom() { + let mut allocator = BestFitAllocator::new(); + const CNT: usize = 10; + const SIZE: usize = 128; + + let range = + alloc_range((SIZE + size_of::() + BestFitAllocator::align_up()) * CNT - 1); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let mut ptrs = Vec::new(); + for _ in 0..CNT - 1 { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push(ptr); + } + + let ptr = allocator.malloc::(SIZE, 1, None); + assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + } + + #[test] + fn alloc_no_oom_through_free() { + let mut allocator = BestFitAllocator::new(); + const SIZE: usize = 128; + + let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + + unsafe { + allocator.free(ptr, SIZE); + } + + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + } + + #[test] + fn multi_range_alloc() { + let mut allocator = BestFitAllocator::new(); + const CNT: usize = 10; + const SIZE: usize = 128; + + let mut ranges = Vec::new(); + for _ in 0..CNT { + let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); + unsafe { + allocator.add_range(&range).unwrap(); + } + ranges.push(range); + } + + let mut ptrs = Vec::new(); + for _ in 0..CNT { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push((ptr, SIZE)); + } + + verify_ptrs_not_overlaping(ptrs.as_slice()); + } + + #[test] + fn multi_range_no_oom_through_free() { + // This function allocates multiple ranges and then frees one of them randomly. And only then there is no oom. + let mut allocator = BestFitAllocator::new(); + + const CNT: usize = 10; + const SIZE: usize = 128; + + let mut ranges = Vec::new(); + for _ in 0..CNT { + let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); + unsafe { + allocator.add_range(&range).unwrap(); + } + ranges.push(range); + } + + let mut ptrs = Vec::new(); + + let ptr = allocator.malloc::(SIZE, 1, None).unwrap(); + + for _ in 0..CNT - 1 { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push((ptr, SIZE)); + } + + unsafe { + allocator.free(ptr, SIZE); + } + + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + ptrs.push((ptr, SIZE)); + + verify_ptrs_not_overlaping(ptrs.as_slice()); + } + + #[test] + fn multi_range_oom() { + // This function allocates multiple ranges and then frees one of them randomly. And only then there is no oom. + let mut allocator = BestFitAllocator::new(); + + const CNT: usize = 10; + const SIZE: usize = 128; + + let mut ranges = Vec::new(); + for _ in 0..CNT { + let range = alloc_range(SIZE + size_of::() + BestFitAllocator::align_up()); + unsafe { + allocator.add_range(&range).unwrap(); + } + ranges.push(range); + } + + let mut ptrs = Vec::new(); + + for _ in 0..CNT { + let ptr = allocator.malloc(SIZE, 1, None).unwrap(); + verify_block(ptr, SIZE, None); + ptrs.push((ptr, SIZE)); + } + + let ptr = allocator.malloc::(SIZE, 1, None); + assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + + verify_ptrs_not_overlaping(ptrs.as_slice()); + } +} + +// END TESTING -------------------------------------------------------------------------------------------------------- + +// VERIFICATION ------------------------------------------------------------------------------------------------------- +#[cfg(kani)] +mod verification { + use super::*; + use core::{alloc::Layout, ptr}; + + fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { + let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; + let meta = unsafe { control_ptr.cast::().as_ref() }; + + assert!(meta.size >= size); + assert_eq!(meta.next, next); + } + + fn alloc_range(length: usize) -> Option> { + let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); + let ptr = unsafe { std::alloc::alloc(alloc_range) }; + + if ptr.is_null() || ((ptr as usize) >= isize::MAX as usize - length) { + None + } else { + Some(ptr as usize..ptr as usize + length) + } + } + + #[kani::proof] + #[kani::unwind(2)] + fn allocate_one() { + let mut allocator = BestFitAllocator::new(); + + let size: usize = kani::any(); + kani::assume(size < MAX_ADDR - size_of::() - BestFitAllocator::align_up()); + kani::assume(size > 0); + let larger_size: usize = kani::any_where(|&x| { + x > size + size_of::() + BestFitAllocator::align_up() && x < MAX_ADDR + }); + + if let Some(range) = alloc_range(larger_size) { + unsafe { + assert_eq!(allocator.add_range(range), Ok(())); + } + + let ptr = allocator.malloc(size, 1, None).unwrap(); + + verify_block(ptr, size, None); + } + } +} +// END VERIFICATION --------------------------------------------------------------------------------------------------- diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 23ef7f7..391ab9a 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -43,7 +43,16 @@ impl super::Allocator for Allocator { return Err(KernelError::InvalidAlign); } - let ptr = NonNull::new(addr.as_mut_ptr::()).ok_or(KernelError::InvalidAddress)?; + let ptr = NonNull::new(addr.as_mut_ptr::()).ok_or(KernelError::InvalidAddress(addr))?; + // Align this up to PAGE_SIZE + let begin = addr + size_of::(); + let begin = if begin.is_multiple_of(super::PAGE_SIZE) { + begin + } else { + PhysAddr::new((begin.as_usize() + super::PAGE_SIZE - 1) & !(super::PAGE_SIZE - 1)) + }; + // TODO: Subtract the needed pages from the available + unsafe { core::ptr::write(ptr.as_ptr(), Self::new(begin).ok_or(KernelError::InvalidAddress(begin))?) }; // Safety: Ptr is properly aligned and non-null. The validity of the memory at that address is valid by the call contract. Ok(Pin::new(unsafe { boxed::Box::from_raw(ptr) })) @@ -119,7 +128,7 @@ impl super::Allocator for Allocator { self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); if len <= rem { - return Some(PhysAddr::new(start)); + return Some(self.begin + (start * super::PAGE_SIZE)); } len -= rem; @@ -201,7 +210,7 @@ mod tests { let pre = allocator.l1.clone(); let addr = super::super::Allocator::alloc(&mut allocator, ALLOC_SIZE).unwrap(); - let idx = addr / super::super::PAGE_SIZE; + let idx = addr.as_usize() / super::super::PAGE_SIZE; // Check that the bits in returned addresses is all ones in pre. for i in 0..ALLOC_SIZE { diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs index 1f8aba9..ee86228 100644 --- a/src/mem/vmm.rs +++ b/src/mem/vmm.rs @@ -1,5 +1,3 @@ -use core::ops::Range; - use hal::mem::{PhysAddr, VirtAddr}; use crate::{utils::KernelError}; @@ -26,30 +24,39 @@ pub enum Backing { #[derive(Clone)] pub struct Region { - range: Range, + start: Option, + len: usize, backing: Backing, perms: Perms, } impl Region { - pub fn new(start: VirtAddr, len: usize, backing: Backing, perms: Perms) -> Self { + /// Creates a new region. + /// + /// - `start` is the starting virtual address of the region. If `None`, the system will choose a suitable address. + /// - `len` is the length of the region in bytes. + /// - `backing` is the backing type of the region, which determines how the region is initialized and where its contents come from. + /// - `perms` is the permissions of the region, which determines how the region can be accessed. + /// + pub fn new(start: Option, len: usize, backing: Backing, perms: Perms) -> Self { Self { - range: start..start.saturating_add(len), + start, + len, backing, perms, } } pub fn start(&self) -> VirtAddr { - self.range.start + self.start.unwrap_or_else(|| VirtAddr::new(0)) } pub fn len(&self) -> usize { - self.range.end.saturating_sub(self.range.start.into()).into() + self.len } pub fn contains(&self, addr: VirtAddr) -> bool { - self.range.contains(&addr) + self.start().saturating_add(self.len()) > addr && addr >= self.start() } } diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs index ff561b2..8927471 100644 --- a/src/mem/vmm/nommu.rs +++ b/src/mem/vmm/nommu.rs @@ -4,6 +4,7 @@ use hal::mem::{PhysAddr, VirtAddr}; use crate::{ mem::{ + alloc::{Allocator, bestfit}, pfa, vmm, }, utils::KernelError, @@ -12,53 +13,45 @@ use crate::{ pub struct AddressSpace { begin: PhysAddr, end: PhysAddr, + allocator: bestfit::BestFitAllocator, } impl vmm::AddressSpacelike for AddressSpace { - fn new(size: usize) -> Result { - let pg_cnt = size.div_ceil(pfa::PAGE_SIZE); - let begin = pfa::alloc_page(pg_cnt).ok_or(KernelError::OutOfMemory)?; - let end = begin.checked_add(pg_cnt * pfa::PAGE_SIZE).ok_or(KernelError::OutOfMemory)?; + fn new(pgs: usize) -> Result { + let begin = pfa::alloc_page(pgs).ok_or(KernelError::OutOfMemory)?; + let end = begin + .checked_add(pgs * pfa::PAGE_SIZE) + .ok_or(KernelError::OutOfMemory)?; + + let mut allocator = bestfit::BestFitAllocator::new(); + unsafe { allocator.add_range(&(begin..end))? }; Ok(Self { begin, end, + allocator, }) } fn map(&mut self, region: vmm::Region) -> Result { - // Do both checks in one statement. - let phys = self.virt_to_phys(region.start()).and_then(|phys| { - if phys > self.end { - None - } else { - Some(phys) - } - }).ok_or(KernelError::InvalidArgument)?; + let req = region.start.and_then(|virt| self.virt_to_phys(virt)); + // TODO: per page align + let align = core::mem::align_of::(); + let start = self.allocator.malloc::(region.len(), align, req)?; match region.backing { vmm::Backing::Anon(phys) => { unsafe { - copy_nonoverlapping( - phys.as_mut_ptr::(), - phys.as_mut_ptr::(), - region.len(), - ) + copy_nonoverlapping(phys.as_mut_ptr::(), start.as_ptr(), region.len()) }; - }, + } vmm::Backing::Zeroed => { - unsafe { - core::ptr::write_bytes( - phys.as_mut_ptr::(), - 0, - region.len(), - ) - }; - }, - vmm::Backing::Uninit => {}, + unsafe { core::ptr::write_bytes(start.as_ptr(), 0, region.len()) }; + } + vmm::Backing::Uninit => {} } - Ok(phys) + Ok(start.into()) } fn unmap(&mut self, _region: &vmm::Region) -> Result<(), KernelError> { @@ -70,11 +63,12 @@ impl vmm::AddressSpacelike for AddressSpace { } fn phys_to_virt(&self, addr: PhysAddr) -> Option { - addr.checked_sub(self.begin.as_usize()).map(|phys| VirtAddr::new(phys.as_usize())) + addr.checked_sub(self.begin.as_usize()) + .map(|phys| VirtAddr::new(phys.as_usize())) } fn virt_to_phys(&self, addr: VirtAddr) -> Option { - self.begin.checked_add(addr.as_usize()) + self.begin.checked_add(addr.as_usize()) } fn end(&self) -> VirtAddr { diff --git a/src/sched.rs b/src/sched.rs index c9acae7..510ff80 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -2,15 +2,16 @@ mod dispch; pub mod rt; +pub mod rr; pub mod task; pub mod thread; -use core::ffi::c_void; +use core::{ffi::c_void, sync::atomic::{AtomicBool, Ordering}}; use hal::Schedable; use crate::{ - mem, sync::spinlock::SpinLocked, types::{ + mem, sync::{atomic::AtomicU64, spinlock::SpinLocked}, time::{self, tick}, types::{ array::IndexMap, rbtree::RbTree, traits::{Get, GetMut}, @@ -23,18 +24,21 @@ type TaskMap = IndexMap; static SCHED: SpinLocked> = SpinLocked::new(Scheduler::new()); +static DISABLED: AtomicBool = AtomicBool::new(true); +static NEXT_TICK: AtomicU64 = AtomicU64::new(0); + pub struct Scheduler { threads: ThreadMap, tasks: TaskMap, id_gen: usize, rt_scheduler: rt::Scheduler, + rr_scheduler: rr::Scheduler, wakeup: RbTree, - current: thread::UId, + current: Option, last_tick: u64, - next_tick: u64, } impl Scheduler { @@ -44,18 +48,20 @@ impl Scheduler { tasks: IndexMap::new(), id_gen: 1, rt_scheduler: rt::Scheduler::new(), + rr_scheduler: rr::Scheduler::new(), wakeup: RbTree::new(), - current: thread::IDLE_THREAD, + current: None, last_tick: 0, - next_tick: 0, } } fn land(&mut self, ctx: *mut c_void) -> Result<(), KernelError> { - // A thread must not disappear while it is running. - let current = self.threads.get_mut(self.current).ok_or(KernelError::InvalidArgument)?; - // The context pointer must not be bogus after a sched_enter. - current.save_ctx(ctx) + if let Some(current) = self.current { + let thread = self.threads.get_mut(current).ok_or(KernelError::InvalidArgument)?; + return thread.save_ctx(ctx); + } + + Ok(()) } pub fn enqueue(&mut self, uid: thread::UId) -> Result<(), KernelError> { @@ -65,8 +71,12 @@ impl Scheduler { let mut view = ViewMut::>::new(&mut self.threads); self.rt_scheduler.enqueue(uid, &mut view); + } else { + self.rr_scheduler.enqueue(uid, &mut self.threads)?; } + // A new thread was added -> Trigger a reschedule. + NEXT_TICK.store(tick(), Ordering::Release); Ok(()) } @@ -82,19 +92,40 @@ impl Scheduler { let mut view = rt::ServerView::::new(&mut self.threads); // If this is not a real-time thread, this will just do nothing. self.rt_scheduler.put(old, dt, &mut view); + // If this is not a round-robin thread, this will just do nothing. + self.rr_scheduler.put(old, dt); // TODO: thread is still enqueued. Dequeue if blocked or sleeping and put to the respective tree/list. // If it exited remove it completely. } let mut view = rt::ServerView::::new(&mut self.threads); - let (new, budget) = self.rt_scheduler.pick(now, &mut view)?; + + let (new, budget) = if let Some((new, budget)) = self.rt_scheduler.pick(now, &mut view) { + (new, budget) + } else if let Some((new, budget)) = self.rr_scheduler.pick(&mut self.threads) { + (new, budget) + } else { + // No thread to run. Run the idle thread. + (thread::IDLE_THREAD, u64::MAX) + }; let ctx = self.threads.get(new)?.ctx(); let task = self.tasks.get_mut(self.threads.get(new)?.task_id())?; - self.current = new; - self.next_tick = now + budget; + self.current = Some(new); + + // Only store next_tick if now + budget is smaller than the current next tick. + let next_tick = now + budget; + let mut old_tick = NEXT_TICK.load(Ordering::Acquire); + + while NEXT_TICK.compare_exchange(old_tick, next_tick, Ordering::Release, Ordering::Acquire).is_err() { + old_tick = NEXT_TICK.load(Ordering::Acquire); + if next_tick >= old_tick { + break; + } + } + Some((ctx, task)) } @@ -110,7 +141,7 @@ impl Scheduler { let uid = task::UId::new(self.id_gen).ok_or(KernelError::InvalidArgument)?; self.id_gen += 1; - self.tasks.insert(&uid, task::Task::new(uid, task)?); + self.tasks.insert(&uid, task::Task::new(uid, task)?)?; Ok(uid) } @@ -118,20 +149,18 @@ impl Scheduler { let task = self.tasks.get_mut(task).ok_or(KernelError::InvalidArgument)?; let thread = task.create_thread(self.id_gen, attrs)?; let uid = thread.uid(); + + self.threads.insert(&uid, thread)?; + self.id_gen += 1; Ok(uid) } } -pub fn init(kaddr_space: mem::vmm::AddressSpace) { +pub fn init(kaddr_space: mem::vmm::AddressSpace) -> Result<(), KernelError> { let mut sched = SCHED.lock(); let uid = task::KERNEL_TASK; - sched.tasks.insert(&uid, task::Task::from_addr_space(uid, kaddr_space)); -} - -pub fn needs_reschedule(now: u64) -> bool { - let sched = SCHED.lock(); - now >= sched.next_tick + sched.tasks.insert(&uid, task::Task::from_addr_space(uid, kaddr_space)?) } pub fn create_task(attrs: &task::Attributes) -> Result { @@ -139,7 +168,30 @@ pub fn create_task(attrs: &task::Attributes) -> Result { } pub fn create_thread(task: task::UId, attrs: &thread::Attributes) -> Result { - SCHED.lock().create_thread(task, attrs) + let mut sched = SCHED.lock(); + sched.create_thread(task, attrs) +} + +pub fn enqueue(uid: thread::UId) -> Result<(), KernelError> { + SCHED.lock().enqueue(uid) +} + +pub fn needs_reschedule(now: u64) -> bool { + if DISABLED.load(Ordering::Acquire) { + return false; + } + + now >= NEXT_TICK.load(Ordering::Acquire) +} + +#[inline] +pub fn disable() { + DISABLED.store(true, Ordering::Release); +} + +#[inline] +pub fn enable() { + DISABLED.store(false, Ordering::Release); } /// Reschedule the tasks. @@ -156,23 +208,25 @@ pub extern "C" fn sched_enter(ctx: *mut c_void) -> *mut c_void { let old = sched.current; if sched.land(ctx).is_err() { - if sched.current == thread::IDLE_THREAD { - BUG!("failed to land the idle thread. something is horribly broken."); - } - - // If we cannot reasonably land. We dequeue the thread. - sched.dequeue(old); - // TODO: Warn - sched.current = thread::IDLE_THREAD; - broken = true; + sched.current.inspect(|uid| { + if *uid == thread::IDLE_THREAD { + BUG!("failed to land the idle thread. something is horribly broken."); + } + + // If we cannot reasonably land. We dequeue the thread. + sched.dequeue(*uid); + // TODO: Warn + sched.current = None; + broken = true; + }); } - let now = 0; - - if let Some((ctx, task)) = sched.do_sched(now, Some(old)) { - if task.id != old.owner() { - dispch::prepare(task); - } + if let Some((ctx, task)) = sched.do_sched(time::tick(), old) { + if let Some(old) = old + && task.id != old.owner() { + dispch::prepare(task); + } + ctx } else if broken { BUG!("failed to reschedule after a failed landing. something is horribly broken."); diff --git a/src/sched/rr.rs b/src/sched/rr.rs new file mode 100644 index 0000000..c4613f2 --- /dev/null +++ b/src/sched/rr.rs @@ -0,0 +1,48 @@ +use crate::{ + sched::{ + thread::{self}, + }, + types::{ + list::List, + }, +}; + +pub struct Scheduler { + queue: List, + + current: Option, + current_left: u64, + quantum: u64, +} + +impl Scheduler { + pub const fn new() -> Self { + // TODO: Make quantum configurable. + Self { queue: List::new(), current: None, current_left: 0, quantum: 1000 } + } + + pub fn enqueue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) -> Result<(), crate::utils::KernelError> { + self.queue.push_back(uid, storage).map_err(|_| crate::utils::KernelError::InvalidArgument) + } + + pub fn put(&mut self, uid: thread::UId, dt: u64) { + if let Some(current) = self.current { + if current == uid { + self.current_left = self.current_left.saturating_sub(dt); + } + } + } + + pub fn pick(&mut self, storage: &mut super::ThreadMap) -> Option<(thread::UId, u64)> { + if self.current_left == 0 { + if let Some(current) = self.current { + self.queue.push_back(current, storage); + } + + self.current = self.queue.pop_front(storage).ok().flatten(); + self.current_left = self.quantum; + } + + self.current.map(|id| (id, self.current_left)) + } +} diff --git a/src/sched/task.rs b/src/sched/task.rs index 06bb8d9..9527f81 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -69,7 +69,10 @@ impl Task { // TODO: On MMU systems, the resrv_pgs attribute will be ignored, as memory will not be reserved. let resrv_pgs = attrs.resrv_pgs.ok_or(KernelError::OutOfMemory)?; let address_space = mem::vmm::AddressSpace::new(resrv_pgs.get())?; + Self::from_addr_space(id, address_space) + } + pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Result { Ok(Self { id, address_space, @@ -77,14 +80,6 @@ impl Task { }) } - pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Self { - Self { - id, - address_space, - tid_cntr: 0, - } - } - fn allocate_tid(&mut self) -> sched::thread::Id { let tid = self.tid_cntr; self.tid_cntr += 1; @@ -97,9 +92,8 @@ impl Task { attrs: &thread::Attributes, ) -> Result { let size = DEFAULTS.stack_pages * mem::pfa::PAGE_SIZE; - let start = self.address_space.end().saturating_sub(size); let region = mem::vmm::Region::new( - start, + None, size, mem::vmm::Backing::Uninit, mem::vmm::Perms::Read | mem::vmm::Perms::Write, diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 63c608a..66f4ec5 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -7,6 +7,7 @@ use hal::stack::{FinFn, Stacklike}; use macros::TaggedLinks; use crate::sched::task::{self, KERNEL_TASK}; +use crate::types::list; use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, utils::KernelError}; pub const IDLE_THREAD: UId = UId { @@ -186,6 +187,9 @@ pub struct WakupTree; #[derive(Debug, Clone, Copy)] pub struct RtTree; +#[derive(Debug, Clone, Copy)] +pub struct RRList; + pub struct Attributes { pub entry: EntryFn, pub fin: Option, @@ -204,6 +208,9 @@ pub struct Thread { /// Wakup tree links for the thread. #[rbtree(tag = WakupTree, idx = UId)] _wakeup_links: rbtree::Links, + + #[list(tag = RRList, idx = UId)] + rr_links: list::Links, } #[allow(dead_code)] @@ -222,6 +229,7 @@ impl Thread { uid, rt_server: None, _wakeup_links: rbtree::Links::new(), + rr_links: list::Links::new(), } } diff --git a/src/sync/atomic.rs b/src/sync/atomic.rs index dec9f98..e3a4bde 100644 --- a/src/sync/atomic.rs +++ b/src/sync/atomic.rs @@ -10,10 +10,26 @@ compile_error!( "The `atomic-cas` feature requires the target to have atomic operations on at least 8-bit integers." ); -// ----------------------------AtomicU8---------------------------- -#[cfg(any(feature = "no-atomic-cas", not(target_has_atomic = "64")))] +#[allow(unused_imports)] pub use core::sync::atomic::Ordering; +#[inline(always)] +pub fn irq_free(f: impl FnOnce() -> T) -> T { + let enabled = hal::asm::are_interrupts_enabled(); + if enabled { + hal::asm::disable_interrupts(); + } + + let result = f(); + + if enabled { + hal::asm::enable_interrupts(); + } + + result +} + +// ----------------------------AtomicU8---------------------------- #[cfg(any(feature = "no-atomic-cas", not(target_has_atomic = "64")))] use core::cell::UnsafeCell; @@ -130,25 +146,9 @@ impl AtomicU64 { } } - #[inline(always)] - fn with_interrupts_disabled(f: impl FnOnce() -> T) -> T { - let were_enabled = hal::asm::are_interrupts_enabled(); - if were_enabled { - hal::asm::disable_interrupts(); - } - - let result = f(); - - if were_enabled { - hal::asm::enable_interrupts(); - } - - result - } - /// Loads the value. pub fn load(&self, _: Ordering) -> u64 { - Self::with_interrupts_disabled(|| { + irq_free(|| { // SAFETY: Interrupts are disabled, so this read is exclusive with writes. unsafe { *self.value.get() } }) @@ -156,7 +156,7 @@ impl AtomicU64 { /// Stores a value. pub fn store(&self, value: u64, _: Ordering) { - Self::with_interrupts_disabled(|| { + irq_free(|| { // SAFETY: Interrupts are disabled, so this write is exclusive with other access. unsafe { *self.value.get() = value; @@ -172,7 +172,7 @@ impl AtomicU64 { _: Ordering, _: Ordering, ) -> Result { - Self::with_interrupts_disabled(|| { + irq_free(|| { // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. unsafe { let value = self.value.get(); @@ -188,7 +188,7 @@ impl AtomicU64 { /// Fetches and adds, returning the previous value. pub fn fetch_add(&self, value: u64, _: Ordering) -> u64 { - Self::with_interrupts_disabled(|| { + irq_free(|| { // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. unsafe { let ptr = self.value.get(); @@ -204,7 +204,7 @@ impl AtomicU64 { where F: FnMut(u64) -> Option, { - Self::with_interrupts_disabled(|| { + irq_free(|| { // SAFETY: Interrupts are disabled, so this read-modify-write is exclusive. unsafe { let ptr = self.value.get(); diff --git a/src/time.rs b/src/time.rs index 25c73eb..44cc3a5 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,31 +1,29 @@ -use core::sync::atomic::Ordering; +use hal::Machinelike; -use crate::sched; -use crate::sync::atomic::AtomicU64; +use crate::{sched, sync}; -// This variable is only allowed to be modified by the systick handler. -static TIME: AtomicU64 = AtomicU64::new(0); +static TICKS: sync::atomic::AtomicU64 = sync::atomic::AtomicU64::new(0); -fn tick() { - TIME.fetch_add(1, Ordering::Release); +pub fn tick() -> u64 { + TICKS.load(sync::atomic::Ordering::Acquire) } -/* - * Returns the current time in milliseconds after boot. - * - */ -#[allow(dead_code)] -pub fn time() -> u64 { - TIME.load(Ordering::Acquire) +pub fn mono_now() -> u64 { + // TODO: This will break on SMP systems without native u64 atomic store. + sync::atomic::irq_free(|| hal::Machine::monotonic_now() ) +} + +pub fn mono_freq() -> u64 { + hal::Machine::monotonic_freq() } /// cbindgen:ignore /// cbindgen:no-export #[unsafe(no_mangle)] pub extern "C" fn systick_hndlr() { - let time = TIME.fetch_add(1, Ordering::Release) + 1; + let tick = TICKS.fetch_add(1, sync::atomic::Ordering::Release) + 1; - if sched::needs_reschedule(time) { + if sched::needs_reschedule(tick) { sched::reschedule(); } } diff --git a/src/types.rs b/src/types.rs index 26df432..746bf6d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,5 +4,6 @@ pub mod array; pub mod heap; pub mod pool; pub mod rbtree; +pub mod list; pub mod traits; pub mod view; \ No newline at end of file diff --git a/src/types/list.rs b/src/types/list.rs new file mode 100644 index 0000000..d3c9779 --- /dev/null +++ b/src/types/list.rs @@ -0,0 +1,302 @@ +use core::marker::PhantomData; + +use super::traits::{Get, GetMut}; + +#[allow(dead_code)] +pub struct List { + head: Option, + tail: Option, + len: usize, + _tag: PhantomData, +} + +#[allow(dead_code)] +pub trait Linkable { + fn links(&self) -> &Links; + fn links_mut(&mut self) -> &mut Links; +} + +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Links { + prev: Option, + next: Option, + _tag: PhantomData, +} + +#[allow(dead_code)] +impl Links { + pub const fn new() -> Self { + Self { + prev: None, + next: None, + _tag: PhantomData, + } + } +} + +#[allow(dead_code)] +impl List { + pub const fn new() -> Self { + Self { + head: None, + tail: None, + len: 0, + _tag: PhantomData, + } + } + + pub fn head(&self) -> Option { + self.head + } + + pub fn tail(&self) -> Option { + self.tail + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + pub fn push_front + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where + >::Output: Linkable, + { + self.detach_links(id, storage)?; + + match self.head { + Some(old_head) => { + let (new_node, old_head_node) = storage.get2_mut(id, old_head); + let (new_node, old_head_node) = (new_node.ok_or(())?, old_head_node.ok_or(())?); + + new_node.links_mut().prev = None; + new_node.links_mut().next = Some(old_head); + + old_head_node.links_mut().prev = Some(id); + } + None => { + let new_node = storage.get_mut(id).ok_or(())?; + new_node.links_mut().prev = None; + new_node.links_mut().next = None; + self.tail = Some(id); + } + } + + self.head = Some(id); + self.len += 1; + Ok(()) + } + + pub fn push_back + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where + >::Output: Linkable, + { + self.detach_links(id, storage)?; + + match self.tail { + Some(old_tail) => { + let (new_node, old_tail_node) = storage.get2_mut(id, old_tail); + let (new_node, old_tail_node) = (new_node.ok_or(())?, old_tail_node.ok_or(())?); + + new_node.links_mut().next = None; + new_node.links_mut().prev = Some(old_tail); + + old_tail_node.links_mut().next = Some(id); + } + None => { + let new_node = storage.get_mut(id).ok_or(())?; + new_node.links_mut().next = None; + new_node.links_mut().prev = None; + self.head = Some(id); + } + } + + self.tail = Some(id); + self.len += 1; + Ok(()) + } + + pub fn pop_front + GetMut>(&mut self, storage: &mut S) -> Result, ()> + where + >::Output: Linkable, + { + let Some(id) = self.head else { + return Ok(None); + }; + + self.remove(id, storage)?; + Ok(Some(id)) + } + + pub fn pop_back + GetMut>(&mut self, storage: &mut S) -> Result, ()> + where + >::Output: Linkable, + { + let Some(id) = self.tail else { + return Ok(None); + }; + + self.remove(id, storage)?; + Ok(Some(id)) + } + + pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where + >::Output: Linkable, + { + let (prev, next, linked) = { + let node = storage.get(id).ok_or(())?; + let links = node.links(); + let linked = self.head == Some(id) + || self.tail == Some(id) + || links.prev.is_some() + || links.next.is_some(); + (links.prev, links.next, linked) + }; + + if !linked { + return Err(()); + } + + if let Some(prev_id) = prev { + let prev_node = storage.get_mut(prev_id).ok_or(())?; + prev_node.links_mut().next = next; + } else { + self.head = next; + } + + if let Some(next_id) = next { + let next_node = storage.get_mut(next_id).ok_or(())?; + next_node.links_mut().prev = prev; + } else { + self.tail = prev; + } + + let node = storage.get_mut(id).ok_or(())?; + node.links_mut().prev = None; + node.links_mut().next = None; + + self.len = self.len.saturating_sub(1); + Ok(()) + } + + fn detach_links + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + where + >::Output: Linkable, + { + let node = storage.get_mut(id).ok_or(())?; + node.links_mut().prev = None; + node.links_mut().next = None; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use core::borrow::Borrow; + + use super::{Linkable, Links, List}; + use crate::types::{array::IndexMap, traits::{Get, ToIndex}}; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct Id(usize); + + impl ToIndex for Id { + fn to_index>(idx: Option) -> usize { + idx.as_ref().map_or(0, |k| k.borrow().0) + } + } + + #[derive(Clone, Copy)] + struct TestTag; + + struct Node { + links: Links, + } + + impl Node { + fn new() -> Self { + Self { + links: Links::new(), + } + } + } + + impl Linkable for Node { + fn links(&self) -> &Links { + &self.links + } + + fn links_mut(&mut self) -> &mut Links { + &mut self.links + } + } + + fn storage() -> IndexMap { + let mut map = IndexMap::new(); + for i in 0..4 { + map.insert(&Id(i), Node::new()).unwrap(); + } + map + } + + #[test] + fn push_front_and_remove() { + let mut s = storage(); + let mut list = List::::new(); + + list.push_front(Id(1), &mut s).unwrap(); + list.push_front(Id(2), &mut s).unwrap(); + list.push_front(Id(3), &mut s).unwrap(); + + assert_eq!(list.head(), Some(Id(3))); + assert_eq!(list.tail(), Some(Id(1))); + assert_eq!(list.len(), 3); + + list.remove(Id(2), &mut s).unwrap(); + assert_eq!(list.head(), Some(Id(3))); + assert_eq!(list.tail(), Some(Id(1))); + assert_eq!(list.len(), 2); + + let n3 = s.get(Id(3)).unwrap(); + let n1 = s.get(Id(1)).unwrap(); + assert_eq!(n3.links().next, Some(Id(1))); + assert_eq!(n1.links().prev, Some(Id(3))); + } + + #[test] + fn pop_back_ordered() { + let mut s = storage(); + let mut list = List::::new(); + + list.push_back(Id(1), &mut s).unwrap(); + list.push_back(Id(2), &mut s).unwrap(); + list.push_back(Id(3), &mut s).unwrap(); + + assert_eq!(list.pop_back(&mut s).unwrap(), Some(Id(3))); + assert_eq!(list.pop_back(&mut s).unwrap(), Some(Id(2))); + assert_eq!(list.pop_back(&mut s).unwrap(), Some(Id(1))); + assert_eq!(list.pop_back(&mut s).unwrap(), None); + assert!(list.is_empty()); + } + + #[test] + fn pop_front_ordered() { + let mut s = storage(); + let mut list = List::::new(); + + list.push_back(Id(1), &mut s).unwrap(); + list.push_back(Id(2), &mut s).unwrap(); + list.push_back(Id(3), &mut s).unwrap(); + + assert_eq!(list.pop_front(&mut s).unwrap(), Some(Id(1))); + assert_eq!(list.pop_front(&mut s).unwrap(), Some(Id(2))); + assert_eq!(list.pop_front(&mut s).unwrap(), Some(Id(3))); + assert_eq!(list.pop_front(&mut s).unwrap(), None); + assert!(list.is_empty()); + } +} \ No newline at end of file diff --git a/src/uspace.rs b/src/uspace.rs index 14dcfd9..febcf01 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -21,6 +21,7 @@ pub fn init_app(boot_info: &crate::BootInfo) -> Result<(), crate::utils::KernelE entry, fin: None, }; - sched::create_thread(sched::task::KERNEL_TASK, &attrs)?; - Ok(()) + let uid = sched::create_thread(sched::task::KERNEL_TASK, &attrs)?; + + sched::enqueue(uid) } diff --git a/src/utils.rs b/src/utils.rs index c77239a..0f1bd71 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -13,6 +13,8 @@ pub(crate) use core::convert::{identity as likely, identity as unlikely}; #[cfg(feature = "nightly")] pub(crate) use core::hint::{likely, unlikely}; +use hal::mem::PhysAddr; + /// This is a macro that is used to panic when a bug is detected. @@ -55,7 +57,7 @@ pub enum KernelError { /// The kernel is out of memory. OutOfMemory, InvalidSize, - InvalidAddress, + InvalidAddress(PhysAddr), InvalidArgument, HalError(hal::Error), CustomError(&'static str), @@ -68,7 +70,7 @@ impl Debug for KernelError { KernelError::InvalidAlign => write!(f, "Invalid alignment"), KernelError::OutOfMemory => write!(f, "Out of memory"), KernelError::InvalidSize => write!(f, "Invalid size"), - KernelError::InvalidAddress => write!(f, "Invalid address"), + KernelError::InvalidAddress(addr) => write!(f, "Invalid address ({})", addr), KernelError::InvalidArgument => write!(f, "Invalid argument"), KernelError::HalError(e) => write!(f, "{e} (in HAL)"), KernelError::CustomError(msg) => write!(f, "{}", msg), From 0737bcc1f892d1c4ab3ca6a7da4703196c83bc0c Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:25:29 +0000 Subject: [PATCH 06/28] make things more sane --- .cargo/config.toml | 18 ++++++-------- .gitignore | 1 + build.rs | 9 ------- machine/arm/Cargo.toml | 3 --- machine/arm/build.rs | 5 ++-- machine/select/Cargo.toml | 1 - machine/select/build.rs | 7 ------ presets/stm32l4r5zi_def.toml | 7 +----- xtasks/crates/config/src/file.rs | 9 +++++++ xtasks/crates/config/src/lib.rs | 40 ++++-------------------------- xtasks/crates/config/src/main.rs | 15 +++++------ xtasks/crates/injector/src/main.rs | 2 +- 12 files changed, 35 insertions(+), 82 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 0a1e750..dfc05b1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,16 +1,12 @@ +include = [ + "../config.toml" +] [alias] xtask = "--config xtasks/.cargo/config.toml run -p xtask --release --" -[env] - -[build] -target = "host-tuple" - [target.'cfg(target_os = "none")'] -rustflags = ["-C", "link-arg=--entry=main",] - -[target] - -[target.thumbv7em-none-eabi] -rustflags = ["-C", "relocation-model=ropi-rwpi"] +rustflags = [ + "-C", "link-arg=--entry=main", + "-C", "link-arg=-Tlink.ld", +] \ No newline at end of file diff --git a/.gitignore b/.gitignore index aeeb6ba..5902a98 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ symbols.map compile_commands.json .cache/ *.img +config.toml diff --git a/build.rs b/build.rs index eb314c7..a1075cd 100644 --- a/build.rs +++ b/build.rs @@ -20,15 +20,6 @@ fn main() { generate_syscall_map("src/syscalls").expect("Failed to generate syscall map."); generate_syscalls_export("src/syscalls").expect("Failed to generate syscall exports."); - generate_device_tree().expect("Failed to generate device tree."); - - // Get linker script from environment variable - if let Ok(linker_script) = std::env::var("DEP_HAL_LINKER_SCRIPT") { - println!("cargo::rustc-link-arg=-T{linker_script}"); - } else { - println!("cargo::warning=LD_SCRIPT_PATH environment variable not set."); - } - cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, } diff --git a/machine/arm/Cargo.toml b/machine/arm/Cargo.toml index 6aef529..9e5a441 100644 --- a/machine/arm/Cargo.toml +++ b/machine/arm/Cargo.toml @@ -5,9 +5,6 @@ rust-version = "1.85.0" authors = ["Thomas Wachter"] edition = "2024" build = "build.rs" -# Through this we can access env variables set by the build script through DEP_HAL_ -# It has nothing to do with native libraries. -links = "halarm" [lib] crate-type = ["rlib"] diff --git a/machine/arm/build.rs b/machine/arm/build.rs index 3ff8569..0a12b84 100644 --- a/machine/arm/build.rs +++ b/machine/arm/build.rs @@ -337,7 +337,8 @@ fn workspace_dir() -> PathBuf { /// /// Exits with error code 1 if any critical build step fails fn main() { - let out = env::var("OUT_DIR").unwrap_or("src".to_string()); + let out = env::var("OUT_DIR").unwrap(); + println!("cargo::rustc-link-search={out}"); let hal = fail_on_error(env::var("OSIRIS_ARM_HAL").with_context( || "OSIRIS_ARM_HAL environment variable not set. Please set it to the path of the ARM HAL.", @@ -363,7 +364,7 @@ fn main() { let libhal = libhal_config.build(); println!("cargo::rustc-link-search=native={}", libhal.display()); - println!("cargo::metadata=linker-script={out}/link.ld"); + println!("cargo::rerun-if-changed={out}/link.ld"); // Extract compile commands for HAL let hal_cc = build_dir.join("compile_commands.json"); diff --git a/machine/select/Cargo.toml b/machine/select/Cargo.toml index 4d068cc..1bb1694 100644 --- a/machine/select/Cargo.toml +++ b/machine/select/Cargo.toml @@ -2,7 +2,6 @@ name = "hal-select" version = "0.1.0" edition = "2024" -links = "hal" [dependencies] hal-api = { path = "../api" } diff --git a/machine/select/build.rs b/machine/select/build.rs index d7c04ac..78d1124 100644 --- a/machine/select/build.rs +++ b/machine/select/build.rs @@ -8,13 +8,6 @@ fn main() { } } - // Pass linker script to top level - if let Ok(linker_script) = std::env::var("DEP_HALARM_LINKER_SCRIPT") { - println!("cargo::metadata=linker-script={linker_script}"); - } else { - println!("cargo::warning=LD_SCRIPT_PATH environment variable not set."); - } - cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, } diff --git a/presets/stm32l4r5zi_def.toml b/presets/stm32l4r5zi_def.toml index e637fc4..8751d75 100644 --- a/presets/stm32l4r5zi_def.toml +++ b/presets/stm32l4r5zi_def.toml @@ -17,9 +17,4 @@ OSIRIS_TUNING_APPSTACKSIZE = "2048" OSIRIS_TUNING_APPMEMSIZE = "8192" [build] -target = "thumbv7em-none-eabi" - -[target.'cfg(target_os = "none")'] -rustflags = [ - "-C", "link-arg=--entry=main", -] \ No newline at end of file +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/xtasks/crates/config/src/file.rs b/xtasks/crates/config/src/file.rs index 0b97663..abc5270 100644 --- a/xtasks/crates/config/src/file.rs +++ b/xtasks/crates/config/src/file.rs @@ -20,6 +20,15 @@ pub fn load_file(path: &Path) -> Result { }) } +pub fn create_if_not_exists(path: &Path) -> Result<()> { + if !path.exists() { + std::fs::write(path, "") + .with_context(|| format!("failed to create file {}", path.display()))?; + } + + Ok(()) +} + pub fn load_files(root: &Path, filename: &str) -> Vec> { let mut files = Vec::new(); diff --git a/xtasks/crates/config/src/lib.rs b/xtasks/crates/config/src/lib.rs index 60172ee..65f431e 100644 --- a/xtasks/crates/config/src/lib.rs +++ b/xtasks/crates/config/src/lib.rs @@ -21,8 +21,7 @@ mod toml_patch; pub mod types; pub mod ui; -use anyhow::anyhow; -use toml_edit::{DocumentMut, ImDocument, Item, Table}; +use toml_edit::{DocumentMut, ImDocument}; pub fn load_config(root: &Path, filename: &str) -> ConfigNode { let files = file::load_files(root, filename); @@ -116,6 +115,7 @@ pub fn load_state<'node>( } pub fn load_toml_mut(toml: &Path) -> Result { + file::create_if_not_exists(&toml)?; let File { path, content } = file::load_file(&toml)?; let path = path.to_string_lossy(); @@ -151,41 +151,11 @@ pub fn load_toml(toml: &Path) -> Result, Error> { Ok(doc) } -#[rustversion::since(1.94)] -compile_error!("config-includes are stable since Rust 1.94; fix the TODOs below."); - pub fn apply_preset(config: &mut DocumentMut, preset: &ImDocument) -> Result<(), Error> { - for (key, value) in preset.iter() { - // We override with a depth of zero or one granularity. - - // TODO: Until we have config-includes stabilized, we skip alias sections. - if key == "alias" { - continue; - } - - match value { - Item::Table(src) => { - let dst = config.entry(key).or_insert(Item::Table(Table::new())); - - if let Item::Table(dst) = dst { - dst.clear(); + config.clear(); - for (key, value) in src.iter() { - dst.insert(key, value.clone()); - } - } else { - return Err(anyhow!( - "type mismatch when applying preset key '{}': expected table, found {}", - key, - dst.type_name() - ) - .into()); - } - } - _ => { - config.insert(key, value.clone()); - } - } + for (key, value) in preset.iter() { + config.insert(key, value.clone()); } Ok(()) diff --git a/xtasks/crates/config/src/main.rs b/xtasks/crates/config/src/main.rs index 85320c1..37ebaf6 100644 --- a/xtasks/crates/config/src/main.rs +++ b/xtasks/crates/config/src/main.rs @@ -58,7 +58,9 @@ pub fn main() { } fn ask_confirmation(prompt: &str) -> bool { - print!("{} (y/N): ", prompt); + print!("{}\n\n(y/N): ", + prompt + ); if let Err(_) = std::io::Write::flush(&mut std::io::stdout()) { return false; @@ -79,14 +81,13 @@ fn run_load_preset(preset_name: &str, no_confirm: bool, current_dir: &Path) -> R let preset_path = PathBuf::from("presets").join(format!("{preset_name}.toml")); let preset = config::load_toml(&preset_path)?; - let config_path = current_dir.join(".cargo/config.toml"); + let config_path = current_dir.join("config.toml"); let mut config = config::load_toml_mut(&config_path)?; // Ask for confirmation if !no_confirm - && !ask_confirmation(&format!( - "Are you sure you want to apply the preset '{preset_name}' to {}?\nThis will overwrite all existing configuration options.", + && !ask_confirmation(&format!("\nApply preset '{preset_name}' to '{}'?\nThis overwrites all existing configuration options.", config_path.display() )) { @@ -111,14 +112,14 @@ fn run_clean(no_confirm: bool, current_dir: &Path) -> Result<(), Error> { // Ask for confirmation if !no_confirm && !ask_confirmation( - "Are you sure you want to remove all configuration options from .cargo/config.toml?", + "Are you sure you want to remove all configuration options from config.toml?", ) { log::info!("Abort."); return Ok(()); } - let config_path = current_dir.join(".cargo/config.toml"); + let config_path = current_dir.join("config.toml"); let mut config = config::load_toml_mut(&config_path)?; @@ -142,7 +143,7 @@ fn run_clean(no_confirm: bool, current_dir: &Path) -> Result<(), Error> { } fn run_ui(current_dir: &Path) { - let config_path = current_dir.join(".cargo/config.toml"); + let config_path = current_dir.join("config.toml"); let node = config::load_config(¤t_dir, "options.toml"); diff --git a/xtasks/crates/injector/src/main.rs b/xtasks/crates/injector/src/main.rs index 973b8c7..3fafe36 100644 --- a/xtasks/crates/injector/src/main.rs +++ b/xtasks/crates/injector/src/main.rs @@ -104,7 +104,7 @@ fn inject(elf: &PathBuf) -> Result<(), String> { } fn get_target_from_cargo_config(manifest_dir: &PathBuf) -> Option { - let cargo_config = manifest_dir.join(".cargo").join("config.toml"); + let cargo_config = manifest_dir.join("config.toml"); if !cargo_config.exists() { return None; From b01b4635f7386de1d599bf1d4f8999ba4949702a Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:28:03 +0000 Subject: [PATCH 07/28] sanify --- Cargo.lock | 11 - Cargo.toml | 5 +- examples/hello-world/Cargo.toml | 13 +- examples/hello-world/src/main.rs | 11 +- interface/Cargo.lock | 473 ----------------------------- interface/Cargo.toml | 12 - interface/build.rs | 45 --- interface/include/bindings.h | 76 ----- interface/src/lib.rs | 82 ----- justfile | 1 - machine/arm/common/CMakeLists.txt | 1 - machine/arm/common/crt0.S | 2 +- machine/arm/common/entry.c | 50 --- machine/arm/stm32l4xx/r5zi/lib.c | 30 -- macros/src/lib.rs | 27 ++ src/lib.rs | 31 +- src/main.rs | 20 +- src/mem.rs | 5 +- src/sched/thread.rs | 2 +- src/syscalls/file.rs | 4 +- src/uspace.rs | 23 +- xtasks/crates/pack/Cargo.toml | 1 - xtasks/crates/pack/src/bootinfo.rs | 67 ---- xtasks/crates/pack/src/main.rs | 1 - xtasks/crates/pack/src/pack.rs | 4 +- 25 files changed, 87 insertions(+), 910 deletions(-) delete mode 100644 interface/Cargo.lock delete mode 100644 interface/Cargo.toml delete mode 100644 interface/build.rs delete mode 100644 interface/include/bindings.h delete mode 100644 interface/src/lib.rs delete mode 100644 machine/arm/common/entry.c delete mode 100644 xtasks/crates/pack/src/bootinfo.rs diff --git a/Cargo.lock b/Cargo.lock index d1469e6..0b14c67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -831,15 +831,6 @@ dependencies = [ "syn", ] -[[package]] -name = "interface" -version = "0.1.0" -dependencies = [ - "bytemuck", - "cbindgen", - "cfg_aliases", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1097,7 +1088,6 @@ dependencies = [ "envparse", "hal-select", "hal-testing", - "interface", "kani", "macros", "quote", @@ -1116,7 +1106,6 @@ dependencies = [ "clap", "crc-fast", "elf", - "interface", "log", "logging", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 0cf5255..bca123c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ members = ["examples/*", "xtasks", "xtasks/crates/*"] default-members = ["."] [workspace.dependencies] -interface = { path = "interface" } logging = { path = "xtasks/logging" } +osiris = { path = "." } [package] name = "osiris" @@ -20,8 +20,7 @@ path = "src/main.rs" [dependencies] hal = { package = "hal-select", path = "machine/select" } -macros = { path = "macros" } -interface = { path = "interface" } +proc_macros = { package = "macros", path = "macros" } envparse = "0.1.0" bitflags = "2.10.0" diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 8f7248e..1bd3456 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -4,7 +4,18 @@ version = "0.1.0" edition = "2024" [dependencies] -osiris = { path = "../../" } +osiris = { workspace = true } [build-dependencies] cfg_aliases = "0.2.1" + +[profile.dev] +panic = "abort" +strip = false +opt-level = 2 + +[profile.release] +panic = "abort" +opt-level = "z" +codegen-units = 1 +lto = true diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 351dd79..d78020d 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -1,13 +1,10 @@ #![no_std] #![no_main] -#[unsafe(no_mangle)] -extern "C" fn main() { - osiris::syscall_print(0, "Hello World!".as_bytes().as_ptr(), 12); -} +use osiris::app_main; -#[cfg(freestanding)] -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { +#[app_main] +fn main() { + osiris::syscall_print(0, "Hello World!".as_bytes().as_ptr(), 12); loop {} } diff --git a/interface/Cargo.lock b/interface/Cargo.lock deleted file mode 100644 index adeff39..0000000 --- a/interface/Cargo.lock +++ /dev/null @@ -1,473 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "cbindgen" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" -dependencies = [ - "clap", - "heck", - "indexmap", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", - "tempfile", - "toml", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "clap" -version = "4.5.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_lex" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "interface" -version = "0.1.0" -dependencies = [ - "bytemuck", - "cbindgen", - "cfg_aliases", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "libc" -version = "0.2.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "proc-macro2" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rustix" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "2.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "winnow" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "zmij" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" diff --git a/interface/Cargo.toml b/interface/Cargo.toml deleted file mode 100644 index a435764..0000000 --- a/interface/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "interface" -version = "0.1.0" -edition = "2024" - -[dependencies] -bytemuck = { version = "1.24.0", features = ["derive"] } - - -[build-dependencies] -cfg_aliases = "0.2.1" -cbindgen = "0.28.0" diff --git a/interface/build.rs b/interface/build.rs deleted file mode 100644 index 2dd23a2..0000000 --- a/interface/build.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::env; - -use cfg_aliases::cfg_aliases; - -fn main() { - cfg_aliases! { - freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, - } - - generate_c_api(); -} - -fn generate_c_api() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - - let config: cbindgen::Config = cbindgen::Config { - no_includes: true, - includes: vec![ - "stdint.h".to_string(), - "stdbool.h".to_string(), - "stdarg.h".to_string(), - ], - layout: cbindgen::LayoutConfig { - packed: Some("__attribute__((packed))".to_string()), - ..Default::default() - }, - language: cbindgen::Language::C, - cpp_compat: false, - ..Default::default() - }; - - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_config(config) - .generate() - .map_or_else( - |error| match error { - cbindgen::Error::ParseSyntaxError { .. } => {} - e => panic!("{e:?}"), - }, - |bindings| { - bindings.write_to_file("include/bindings.h"); - }, - ); -} diff --git a/interface/include/bindings.h b/interface/include/bindings.h deleted file mode 100644 index dd7b765..0000000 --- a/interface/include/bindings.h +++ /dev/null @@ -1,76 +0,0 @@ -#include "stdint.h" -#include "stdbool.h" -#include "stdarg.h" - -#define BOOT_INFO_MAGIC 221566477 - -/** - * The memory map entry type. - * - * This structure shall be compatible with the multiboot_memory_map_t struct at - * Link: [https://www.gnu.org/software/grub/manual/multiboot/multiboot.html]() - */ -typedef struct __attribute__((packed)) MemMapEntry { - /** - * The size of the entry. - */ - uint32_t size; - /** - * The base address of the memory region. - */ - uint64_t addr; - /** - * The length of the memory region. - */ - uint64_t length; - /** - * The type of the memory region. - */ - uint32_t ty; -} MemMapEntry; - -typedef struct InitDescriptor { - /** - * Pointer to the start of the binary of the init program. - */ - uint64_t begin; - /** - * Length of the binary of the init program. - */ - uint64_t len; - uint64_t entry_offset; -} InitDescriptor; - -typedef struct Args { - struct InitDescriptor init; -} Args; - -/** - * The boot information structure. - */ -typedef struct BootInfo { - /** - * The magic number that indicates valid boot information. - */ - uint32_t magic; - /** - * The version of the boot information structure. - */ - uint32_t version; - /** - * The implementer of the processor. - * The variant of the processor. - * The memory map. - */ - struct MemMapEntry mmap[8]; - /** - * The length of the memory map. - */ - uint64_t mmap_len; - /** - * The command line arguments. - */ - struct Args args; -} BootInfo; - -extern void kernel_init(const struct BootInfo *boot_info); diff --git a/interface/src/lib.rs b/interface/src/lib.rs deleted file mode 100644 index ad78f0a..0000000 --- a/interface/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -#![cfg_attr(freestanding, no_std)] - -/// The memory map entry type. -/// -/// This structure shall be compatible with the multiboot_memory_map_t struct at -/// Link: [https://www.gnu.org/software/grub/manual/multiboot/multiboot.html]() -#[repr(packed, C)] -#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] -pub struct MemMapEntry { - /// The size of the entry. - pub size: u32, - /// The base address of the memory region. - pub addr: u64, - /// The length of the memory region. - pub length: u64, - /// The type of the memory region. - pub ty: u32, -} - -#[cfg(kani)] -impl kani::Arbitrary for MemMapEntry { - fn any() -> Self { - let size: u32 = kani::any_where(|&x| x % size_of::() as u32 == 0); - let length = kani::any(); - let addr = kani::any(); - - kani::assume(addr > 0); - - MemMapEntry { - size, - addr, - length, - ty: kani::any(), - } - } - - fn any_array() -> [Self; MAX_ARRAY_LENGTH] { - [(); MAX_ARRAY_LENGTH].map(|_| Self::any()) - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] -pub struct InitDescriptor { - /// Pointer to the start of the binary of the init program. - pub begin: u64, - /// Length of the binary of the init program. - pub len: u64, - pub entry_offset: u64, -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Args { - pub init: InitDescriptor, -} - -pub const BOOT_INFO_MAGIC: u32 = 0xD34D60D; - -/// The boot information structure. -#[repr(C)] -#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] -pub struct BootInfo { - /// The magic number that indicates valid boot information. - pub magic: u32, - /// The version of the boot information structure. - pub version: u32, - /// The implementer of the processor. - //pub implementer: u64, - /// The variant of the processor. - //pub variant: u64, - /// The memory map. - pub mmap: [MemMapEntry; 8], - /// The length of the memory map. - pub mmap_len: u64, - /// The command line arguments. - pub args: Args, -} - -unsafe extern "C" { - pub fn kernel_init(boot_info: *const BootInfo) -> !; -} diff --git a/justfile b/justfile index c1d3dd4..b14c0c4 100644 --- a/justfile +++ b/justfile @@ -10,7 +10,6 @@ pack *args: example name *args: (build args) cargo build -p {{name}} {{args}} - cargo xtask pack --output {{name}}.bin --init examples/{{name}} {{args}} fmt *args: cargo fmt {{args}} diff --git a/machine/arm/common/CMakeLists.txt b/machine/arm/common/CMakeLists.txt index f426b3a..0346c0d 100644 --- a/machine/arm/common/CMakeLists.txt +++ b/machine/arm/common/CMakeLists.txt @@ -33,7 +33,6 @@ set_property(SOURCE irq.S APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-c add_library(common STATIC ivt.S - entry.c syscall.c irq.S crt0.S diff --git a/machine/arm/common/crt0.S b/machine/arm/common/crt0.S index e26deb6..ff93ecd 100644 --- a/machine/arm/common/crt0.S +++ b/machine/arm/common/crt0.S @@ -28,7 +28,7 @@ bootstrap: strlt r3, [r1], #4 blt 2b @ Call the pre_init function. - bl pre_init + bl kernel_init @ If main returns, loop forever. hang: b hang \ No newline at end of file diff --git a/machine/arm/common/entry.c b/machine/arm/common/entry.c deleted file mode 100644 index 52dad42..0000000 --- a/machine/arm/common/entry.c +++ /dev/null @@ -1,50 +0,0 @@ - -#include -#include "mem.h" - -#include - -typedef void (*func_t)(void); - -extern func_t __init_array_start; -extern func_t __init_array_end; -extern func_t __fini_array_start; -extern func_t __fini_array_end; - -extern void pre_init(void) __attribute__((noreturn)); -extern void init_mem_maps(BootInfo *boot_info); - -__attribute__((section(".bootinfo"), used, aligned(4))) -static BootInfo _boot_info = { - .magic = BOOT_INFO_MAGIC, - .version = 1, - .mmap = {0}, - .mmap_len = 0, - .args = {.init = {0}}, -}; - -void call_constructors(void) -{ - for (func_t *func = &__init_array_start; func < &__init_array_end; func++) - { - (*func)(); - } -} - -void call_destructors(void) -{ - for (func_t *func = &__fini_array_start; func < &__fini_array_end; func++) - { - (*func)(); - } -} - -void pre_init(void) -{ - // Init memory maps, etc. - init_mem_maps(&_boot_info); - - // Boot! - kernel_init(&_boot_info); - unreachable(); -} diff --git a/machine/arm/stm32l4xx/r5zi/lib.c b/machine/arm/stm32l4xx/r5zi/lib.c index 877422d..83c8135 100644 --- a/machine/arm/stm32l4xx/r5zi/lib.c +++ b/machine/arm/stm32l4xx/r5zi/lib.c @@ -1,6 +1,4 @@ #include - -#include #include /* @@ -197,31 +195,3 @@ const uintptr_t vector_table_ext[] __attribute__((section(".ivt.ext"))) = { (uintptr_t)&gfxmmu_hndlr, (uintptr_t)&dmamux1_ovr_hndlr, }; - -void init_mem_maps(BootInfo *boot_info) { - boot_info->mmap_len = 3; - - // SRAM1 - boot_info->mmap[0] = (MemMapEntry){ - .size = sizeof(MemMapEntry), - .addr = 0x20000000, - .length = 0x30000, - .ty = 1, - }; - - // SRAM2 - boot_info->mmap[1] = (MemMapEntry){ - .size = sizeof(MemMapEntry), - .addr = 0x20030000, - .length = 0x10000, - .ty = 1, - }; - - // SRAM3 - boot_info->mmap[2] = (MemMapEntry){ - .size = sizeof(MemMapEntry), - .addr = 0x20040000, - .length = 0x60000, - .ty = 1, - }; -} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 178322c..2387d03 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -15,6 +15,33 @@ pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenS }.into() } +#[proc_macro_attribute] +pub fn app_main(input: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let item = syn::parse_macro_input!(item as syn::ItemFn); + let block = &item.block; + + let expanded = quote::quote! { + #[unsafe(no_mangle)] + #[unsafe(naked)] + extern "C" fn main() { + osiris::hal::asm::startup_trampoline!(); + } + + #[cfg(freestanding)] + #[panic_handler] + fn panic(info: &core::panic::PanicInfo) -> ! { + osiris::panic(info); + } + + #[unsafe(no_mangle)] + pub extern "C" fn app_main() -> () { + #block + } + }; + + expanded.into() +} + #[proc_macro_attribute] pub fn service( attr: proc_macro::TokenStream, diff --git a/src/lib.rs b/src/lib.rs index f033cad..39bab6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,10 +20,12 @@ pub mod time; pub mod uspace; use hal::Machinelike; -use interface::BootInfo; include!(concat!(env!("OUT_DIR"), "/syscalls_export.rs")); include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); +pub use hal; +pub use proc_macros::app_main; + /// The kernel initialization function. /// /// `boot_info` - The boot information. @@ -33,22 +35,15 @@ include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); /// This function must be called only once during the kernel startup. /// The `boot_info` pointer must be valid and point to a properly initialized `BootInfo` structure. #[unsafe(no_mangle)] -pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { +pub unsafe extern "C" fn kernel_init() -> ! { // Initialize basic hardware and the logging system. hal::Machine::init(); hal::Machine::bench_start(); - if boot_info.is_null() || !boot_info.is_aligned() { - panic!("boot_info pointer is null or unaligned."); - } - - // Safety: We trust the bootloader to provide a valid boot_info structure. - let boot_info = unsafe { &*boot_info }; - print::print_header(); // Initialize the memory allocator. - let kaddr_space = mem::init_memory(boot_info); + let kaddr_space = mem::init_memory(); kprintln!("Memory initialized."); @@ -68,7 +63,7 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { ); // Start the init application. - if let Err(e) = uspace::init_app(boot_info) { + if let Err(e) = uspace::init_app() { panic!("failed to start init application. Error: {e:?}"); } @@ -76,3 +71,17 @@ pub unsafe extern "C" fn kernel_init(boot_info: *const BootInfo) -> ! { loop {} } + +pub fn panic(info: &core::panic::PanicInfo) -> ! { + kprintln!("**************************** PANIC ****************************"); + kprintln!(""); + kprintln!("Message: {}", info.message()); + + if let Some(location) = info.location() { + kprintln!("Location: {}:{}", location.file(), location.line()); + } + + kprintln!("**************************** PANIC ****************************"); + + hal::Machine::panic_handler(info); +} diff --git a/src/main.rs b/src/main.rs index ae72d07..3191b0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,23 +7,17 @@ pub extern "C" fn main() -> ! { hal::asm::startup_trampoline!(); } +#[unsafe(no_mangle)] +pub extern "C" fn app_main() -> ! { + osiris::syscall_print(0, "Hello World!".as_bytes().as_ptr(), 12); + loop {} +} + /// The panic handler. #[cfg(freestanding)] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - use hal::Machinelike; - - osiris::kprintln!("**************************** PANIC ****************************"); - osiris::kprintln!(""); - osiris::kprintln!("Message: {}", info.message()); - - if let Some(location) = info.location() { - osiris::kprintln!("Location: {}:{}", location.file(), location.line()); - } - - osiris::kprintln!("**************************** PANIC ****************************"); - - hal::Machine::panic_handler(info); + osiris::panic(info); } #[cfg(not(freestanding))] diff --git a/src/mem.rs b/src/mem.rs index a01d32e..f037097 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -3,9 +3,8 @@ use crate::mem::pfa::PAGE_SIZE; use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region}; use crate::sync::spinlock::SpinLocked; -use crate::BootInfo; use alloc::Allocator; -use hal::mem::{PhysAddr, VirtAddr}; +use hal::mem::{PhysAddr}; use core::ptr::NonNull; pub mod alloc; @@ -44,7 +43,7 @@ static GLOBAL_ALLOCATOR: SpinLocked = /// `regions` - The memory node module of device tree codegen file. /// /// Returns an error if the memory allocator could not be initialized. -pub fn init_memory(boot_info: &BootInfo) -> vmm::AddressSpace { +pub fn init_memory() -> vmm::AddressSpace { let stack_top = &raw const __stack_top as usize; if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) { // TODO: Get this from the DeviceTree. panic!("failed to initialize PFA. Error: {e:?}"); diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 66f4ec5..75a1890 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -4,7 +4,7 @@ use core::{borrow::Borrow, ffi::c_void}; use hal::{Stack, stack::EntryFn}; use hal::stack::{FinFn, Stacklike}; -use macros::TaggedLinks; +use proc_macros::TaggedLinks; use crate::sched::task::{self, KERNEL_TASK}; use crate::types::list; diff --git a/src/syscalls/file.rs b/src/syscalls/file.rs index 65131b9..957a765 100644 --- a/src/syscalls/file.rs +++ b/src/syscalls/file.rs @@ -1,7 +1,5 @@ use core::{ffi::c_int, str}; - -use crate::kprintln; -use macros::syscall_handler; +use proc_macros::syscall_handler; #[syscall_handler(num = 0)] fn syscall_print(fd: usize, buf: *const u8, len: usize) -> c_int { diff --git a/src/uspace.rs b/src/uspace.rs index febcf01..d6799de 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -1,24 +1,19 @@ //! This module provides access to userspace structures and services. -use ::core::mem::transmute; - use crate::sched; -pub fn init_app(boot_info: &crate::BootInfo) -> Result<(), crate::utils::KernelError> { - let len = boot_info.args.init.len; - - if len == 0 { - return Err(crate::utils::KernelError::InvalidArgument); - } +unsafe extern "C" { + /// The entry point for the userspace application. + fn app_main() -> (); +} - let entry = unsafe { - transmute::( - boot_info.args.init.begin as usize + boot_info.args.init.entry_offset as usize, - ) - }; +extern "C" fn app_main_entry() { + unsafe { app_main() } +} +pub fn init_app() -> Result<(), crate::utils::KernelError> { let attrs = sched::thread::Attributes { - entry, + entry: app_main_entry, fin: None, }; let uid = sched::create_thread(sched::task::KERNEL_TASK, &attrs)?; diff --git a/xtasks/crates/pack/Cargo.toml b/xtasks/crates/pack/Cargo.toml index 7ca2656..e898333 100644 --- a/xtasks/crates/pack/Cargo.toml +++ b/xtasks/crates/pack/Cargo.toml @@ -10,7 +10,6 @@ clap = { version = "4.5.47", features = ["derive"] } crc-fast = "1.8.0" elf = "0.8.0" log = "0.4.27" -interface = { workspace = true } bytemuck = { version = "1.24.0", features = ["derive"] } tempfile = "3.23.0" cargo_metadata = "0.23.1" diff --git a/xtasks/crates/pack/src/bootinfo.rs b/xtasks/crates/pack/src/bootinfo.rs deleted file mode 100644 index 8f9fcf3..0000000 --- a/xtasks/crates/pack/src/bootinfo.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::image; - -pub struct BootInfo { - inner: Vec, -} - -impl BootInfo { - pub fn new(img_paddr: usize, section: &image::Section) -> Self { - let boot_info = interface::BootInfo { - magic: interface::BOOT_INFO_MAGIC, - version: 1, - mmap: [interface::MemMapEntry { - size: 0, - addr: 0, - length: 0, - ty: 0, - }; 8], - mmap_len: 0, - args: interface::Args { - init: interface::InitDescriptor { - begin: (img_paddr + section.offset()) as u64, - len: section.size() as u64, - entry_offset: section.entry_offset() as u64, - }, - }, - }; - - let boot_info_bytes = bytemuck::bytes_of(&boot_info); - - Self { - inner: boot_info_bytes.to_vec(), - } - } - - pub fn inner(&self) -> &Vec { - &self.inner - } -} - -// Tests for bootinfo -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bootinfo_fields() { - let boot_info = BootInfo::new( - 0x4000, - &image::Section::from_parts(0x4000, 0x2000, 0x100, 0x1000, 0, false), - ); - - // Deserialize back to struct for comparison - assert_eq!( - boot_info.inner().len(), - std::mem::size_of::() - ); - - let reconstructed: interface::BootInfo = - unsafe { std::ptr::read(boot_info.inner().as_ptr() as *const interface::BootInfo) }; - - assert_eq!(reconstructed.magic, interface::BOOT_INFO_MAGIC); - assert_eq!(reconstructed.version, 1); - assert_eq!(reconstructed.args.init.begin, 0x4000 + 0x4000); - assert_eq!(reconstructed.args.init.len, 0x2000); - assert_eq!(reconstructed.args.init.entry_offset, 0x100); - } -} diff --git a/xtasks/crates/pack/src/main.rs b/xtasks/crates/pack/src/main.rs index 5e985a8..f84c7a7 100644 --- a/xtasks/crates/pack/src/main.rs +++ b/xtasks/crates/pack/src/main.rs @@ -2,7 +2,6 @@ use std::path::PathBuf; use clap::Parser; -mod bootinfo; mod elf; mod image; mod pack; diff --git a/xtasks/crates/pack/src/pack.rs b/xtasks/crates/pack/src/pack.rs index e4cf12f..6cab370 100644 --- a/xtasks/crates/pack/src/pack.rs +++ b/xtasks/crates/pack/src/pack.rs @@ -4,7 +4,6 @@ use anyhow::{Result, anyhow, bail}; use cargo_metadata::MetadataCommand; use crate::{ - bootinfo, elf::ElfInfo, image::{self}, }; @@ -126,8 +125,7 @@ pub fn pack(init_info: &ElfInfo, kernel_info: &mut ElfInfo, out: &Path) -> Resul let init_section = img.add_elf(init_info, image::SectionDescripter::Loadable(None))?; // Patch bootinfo into kernel. - let boot_info = bootinfo::BootInfo::new(img.paddr(), &init_section); - kernel_info.patch_section(".bootinfo", 0, boot_info.inner())?; + //kernel_info.patch_section(".bootinfo", 0, boot_info.inner())?; // Update kernel in image. img.update(kernel_info, 0)?; From 4d6de8998702034ad2d9004c52665e662ab0ba00 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:37:03 +0000 Subject: [PATCH 08/28] sanify --- machine/arm/build.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/machine/arm/build.rs b/machine/arm/build.rs index 0a12b84..57df5f2 100644 --- a/machine/arm/build.rs +++ b/machine/arm/build.rs @@ -303,13 +303,12 @@ fn merge_compile_commands(files: &[String]) -> String { /// # Returns /// /// PathBuf pointing to the workspace root directory -fn workspace_dir() -> PathBuf { +fn workspace_dir() -> Option { let output = Command::new("cargo") .args(["locate-project", "--workspace", "--message-format=plain"]) - .output() - .expect("failed to run cargo locate-project"); + .output().ok()?; let path = String::from_utf8(output.stdout).expect("utf8"); - PathBuf::from(path.trim()).parent().unwrap().to_path_buf() + Some(PathBuf::from(path.trim()).parent()?.to_path_buf()) } /// Main build script entry point. @@ -388,8 +387,10 @@ fn main() { // Merge and export compile_commands.json for IDE integration let merged = merge_compile_commands(&[hal_cc, common_cc]); - let project_root = workspace_dir(); - let out_file = project_root.join("compile_commands.json"); - - fs::write(out_file, merged).expect("write merged compile_commands.json"); + if let Some(project_root) = workspace_dir() { + let out_file = project_root.join("compile_commands.json"); + fs::write(out_file, merged).expect("write merged compile_commands.json"); + } else { + println!("cargo::warning=Could not determine workspace root, skipping compile_commands.json generation."); + } } From 2f3d7212434a29380f192e73b0f8a76d008d28dc Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:51:54 +0000 Subject: [PATCH 09/28] remove old options --- options.toml | 24 ++++++------------------ presets/stm32l4r5zi_def.toml | 3 --- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/options.toml b/options.toml index eb66692..d292dd3 100644 --- a/options.toml +++ b/options.toml @@ -26,26 +26,14 @@ description = "Enables the Floating Point Unit (FPU). This is required for appli type = "Boolean" default = false -[stackpages] -name = "Stack Pages" -description = "Number of pages to allocate for the kernel stack." -type = { type = "Integer", min = 1 } -default = 1 - -[tuning.appmemsize] -name = "Application Memory Size" -description = "Sets the size of the initial memory region for the init application. This memory is used for the heap and stack." -type = { type = "Integer", min = 0 } -default = 8192 - -[tuning.appstacksize] -name = "Application Stack Size" -description = "Sets the size of the stack for the init application. This must be less than the application memory size." -type = { type = "Integer", min = 0 } -default = 2048 - [tuning.dts] name = "Device Tree Source" description = "Board DTS file targeted to build OS for. Relative to the toplevel boards/ directory." type = "String" default = "nucleo_l4r5zi.dts" + +[stackpages] +name = "Stack Pages" +description = "Number of pages to allocate for the kernel stack." +type = { type = "Integer", min = 1 } +default = 1 diff --git a/presets/stm32l4r5zi_def.toml b/presets/stm32l4r5zi_def.toml index 8751d75..fc6a853 100644 --- a/presets/stm32l4r5zi_def.toml +++ b/presets/stm32l4r5zi_def.toml @@ -13,8 +13,5 @@ OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" OSIRIS_TUNING_ENABLEFPU = "false" OSIRIS_STACKPAGES = "1" -OSIRIS_TUNING_APPSTACKSIZE = "2048" -OSIRIS_TUNING_APPMEMSIZE = "8192" - [build] target = "thumbv7em-none-eabi" \ No newline at end of file From 5e11a88b084c8337e9cc79fab8cc5fba3a22e863 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 28 Mar 2026 13:57:48 +0000 Subject: [PATCH 10/28] fix lots of stuff --- build.rs | 60 +------ examples/hello-world/src/main.rs | 10 +- machine/api/src/mem.rs | 16 +- machine/arm/src/asm.rs | 75 ++++++-- machine/arm/src/lib.rs | 6 +- machine/arm/src/panic.rs | 7 +- machine/testing/src/asm.rs | 6 +- src/error.rs | 178 +++++++++++++++++++ src/idle.rs | 15 +- src/lib.rs | 28 ++- src/main.rs | 1 - src/mem.rs | 25 +-- src/mem/alloc.rs | 4 +- src/mem/alloc/bestfit.rs | 38 +++-- src/mem/pfa.rs | 8 +- src/mem/pfa/bitset.rs | 13 +- src/mem/vmm.rs | 12 +- src/mem/vmm/nommu.rs | 19 +-- src/sched.rs | 282 ++++++++++++++++++++----------- src/sched/rr.rs | 35 ++-- src/sched/rt.rs | 15 +- src/sched/scheduler.rs | 206 ---------------------- src/sched/task.rs | 23 ++- src/sched/thread.rs | 116 ++++++++++--- src/sync/atomic.rs | 11 +- src/syscalls.rs | 4 +- src/syscalls/sched.rs | 29 ++++ src/syscalls/tasks.rs | 33 ---- src/types/array.rs | 34 ++-- src/types/boxed.rs | 12 +- src/types/heap.rs | 5 +- src/types/list.rs | 52 +++++- src/types/rbtree.rs | 31 ++++ src/uapi.rs | 2 + src/uapi/print.rs | 24 +++ src/uapi/sched.rs | 8 + src/uspace.rs | 16 +- src/utils.rs | 85 ---------- 38 files changed, 850 insertions(+), 694 deletions(-) create mode 100644 src/error.rs delete mode 100644 src/sched/scheduler.rs create mode 100644 src/syscalls/sched.rs delete mode 100644 src/syscalls/tasks.rs create mode 100644 src/uapi.rs create mode 100644 src/uapi/print.rs create mode 100644 src/uapi/sched.rs delete mode 100644 src/utils.rs diff --git a/build.rs b/build.rs index a1075cd..a07afab 100644 --- a/build.rs +++ b/build.rs @@ -6,9 +6,8 @@ extern crate syn; extern crate walkdir; use cfg_aliases::cfg_aliases; -use quote::ToTokens; use std::io::Write; -use syn::{Attribute, FnArg, LitInt, punctuated::Punctuated, token::Comma}; +use syn::{Attribute, LitInt}; use walkdir::WalkDir; extern crate cbindgen; @@ -18,7 +17,6 @@ fn main() { println!("cargo::rerun-if-changed=build.rs"); generate_syscall_map("src/syscalls").expect("Failed to generate syscall map."); - generate_syscalls_export("src/syscalls").expect("Failed to generate syscall exports."); cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, @@ -320,59 +318,3 @@ fn collect_syscalls>(root: P) -> HashMap { syscalls } - -type SyscallDataExport = (u16, Punctuated); - -fn collect_syscalls_export>(root: P) -> HashMap { - let mut syscalls = HashMap::new(); - let mut numbers = HashMap::new(); - - for entry in WalkDir::new(&root) { - let entry = match entry { - Ok(entry) => entry, - Err(_) => continue, - }; - - if entry.file_type().is_file() { - let path = entry.path(); - - println!("Processing file: {}", path.display()); - - let contents = match std::fs::read_to_string(path) { - Ok(contents) => contents, - Err(_) => continue, - }; - - let file = match syn::parse_file(&contents) { - Ok(file) => file, - Err(_) => continue, - }; - - for item in file.items { - let item = match item { - syn::Item::Fn(item) => item, - _ => continue, - }; - - let name = item.sig.ident.to_string(); - - if let Some(num) = is_syscall(&item.attrs, &name) { - if syscalls.contains_key(&name) { - println!("cargo:warning=Duplicate syscall handler: {name}"); - continue; - } - - if numbers.contains_key(&num) { - println!("cargo:warning=Duplicate syscall number: {num} for {name}"); - continue; - } - - syscalls.insert(name.clone(), (num, item.sig.inputs)); - numbers.insert(num, name); - } - } - } - } - - syscalls -} diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index d78020d..2702505 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -5,6 +5,12 @@ use osiris::app_main; #[app_main] fn main() { - osiris::syscall_print(0, "Hello World!".as_bytes().as_ptr(), 12); - loop {} + osiris::uprintln!("Hello World!"); + let mut tick = 0; + + loop { + osiris::uprintln!("Tick: {}", tick); + tick += 1; + osiris::uapi::sched::sleep_for(1000); + } } diff --git a/machine/api/src/mem.rs b/machine/api/src/mem.rs index 4aa98d6..d3a6223 100644 --- a/machine/api/src/mem.rs +++ b/machine/api/src/mem.rs @@ -1,7 +1,7 @@ -use core::{fmt::Display, ops::{Add, Div, Rem, Sub}, ptr::NonNull}; +use core::{fmt::{Display, LowerHex, UpperHex}, ops::{Add, Div, Rem, Sub}, ptr::NonNull}; #[repr(transparent)] -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct PhysAddr(usize); impl PhysAddr { @@ -100,6 +100,18 @@ impl From for usize { } } +impl LowerHex for PhysAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:x}", self.0) + } +} + +impl UpperHex for PhysAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:X}", self.0) + } +} + #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct VirtAddr(usize); diff --git a/machine/arm/src/asm.rs b/machine/arm/src/asm.rs index 9f12f75..b69e90e 100644 --- a/machine/arm/src/asm.rs +++ b/machine/arm/src/asm.rs @@ -22,31 +22,57 @@ macro_rules! __macro_syscall { ($num:expr) => { use core::arch::asm; unsafe { - asm!("svc {0}", const $num); + asm!("svc {num}", num = const $num, clobber_abi("C")); } }; ($num:expr, $arg0:expr) => { use core::arch::asm; unsafe { - asm!("mov r0, {0}", "svc {1}", in(reg)$arg0, const $num); + asm!( + "svc {num}", + in("r0") $arg0, + num = const $num, + clobber_abi("C") + ); } }; ($num:expr, $arg0:expr, $arg1:expr) => { use core::arch::asm; unsafe { - asm!("mov r0, {0}", "mov r1, {1}", "svc {2}", in(reg)$arg0, in(reg)$arg1, const $num); + asm!( + "svc {num}", + in("r0") $arg0, + in("r1") $arg1, + num = const $num, + clobber_abi("C") + ); } }; ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => { use core::arch::asm; unsafe { - asm!("mov r0, {0}", "mov r1, {1}", "mov r2, {2}", "svc {3}", in(reg)$arg0, in(reg)$arg1, in(reg)$arg2, const $num); + asm!( + "svc {num}", + in("r0") $arg0, + in("r1") $arg1, + in("r2") $arg2, + num = const $num, + clobber_abi("C") + ); } }; ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => { use core::arch::asm; unsafe { - asm!("mov r0, {0}", "mov r1, {1}", "mov r2, {2}", "mov r3, {3}", "svc {4}", in(reg)$arg0, in(reg)$arg1, in(reg)$arg2, in(reg)$arg3, const $num); + asm!( + "svc {num}", + in("r0") $arg0, + in("r1") $arg1, + in("r2") $arg2, + in("r3") $arg3, + num = const $num, + clobber_abi("C") + ); } }; } @@ -65,17 +91,28 @@ pub use crate::__macro_syscall as syscall; #[cfg(not(feature = "host"))] #[inline(always)] -pub fn disable_interrupts() { +pub fn disable_irq_save() -> usize { use core::arch::asm; - use core::sync::atomic::compiler_fence; - unsafe { asm!("cpsid i", options(nomem, nostack, preserves_flags)) }; - compiler_fence(core::sync::atomic::Ordering::SeqCst); + let old: usize; + + unsafe { + asm!( + "mrs {old}, primask", + "cpsid i", + "isb", + old = out(reg) old, + options(nostack, preserves_flags) + ); + } + old } #[cfg(feature = "host")] #[inline(always)] -pub fn disable_interrupts() {} +pub fn disable_irq_save() -> usize { + 0 +} #[cfg(not(feature = "host"))] #[inline(always)] @@ -97,17 +134,23 @@ pub fn are_interrupts_enabled() -> bool { #[cfg(not(feature = "host"))] #[inline(always)] -pub fn enable_interrupts() { +pub fn enable_irq_restr(state: usize) { use core::arch::asm; - use core::sync::atomic::compiler_fence; - - unsafe { asm!("cpsie i", options(nomem, nostack, preserves_flags)) }; - compiler_fence(core::sync::atomic::Ordering::SeqCst); + + unsafe { + asm!( + "dsb", + "msr primask, {state}", + "isb", + state = in(reg) state, + options(nostack, preserves_flags) + ); + } } #[cfg(feature = "host")] #[inline(always)] -pub fn enable_interrupts() {} +pub fn enable_irq_restr(state: usize) {} #[cfg(not(feature = "host"))] #[macro_export] diff --git a/machine/arm/src/lib.rs b/machine/arm/src/lib.rs index 31666f5..00fb103 100644 --- a/machine/arm/src/lib.rs +++ b/machine/arm/src/lib.rs @@ -40,14 +40,14 @@ impl hal_api::Machinelike for ArmMachine { fn print(s: &str) -> Result<()> { use crate::asm; - asm::disable_interrupts(); + let state = asm::disable_irq_save(); if (unsafe { bindings::write_debug_uart(s.as_ptr() as *const c_char, s.len() as i32) } != 0) { - asm::enable_interrupts(); + asm::enable_irq_restr(state); Ok(()) } else { - asm::enable_interrupts(); + asm::enable_irq_restr(state); Err(hal_api::Error::default()) } } diff --git a/machine/arm/src/panic.rs b/machine/arm/src/panic.rs index 3d6dfbc..9025b8a 100644 --- a/machine/arm/src/panic.rs +++ b/machine/arm/src/panic.rs @@ -6,9 +6,6 @@ use core::panic::PanicInfo; use crate::asm; pub fn panic_handler(_info: &PanicInfo) -> ! { - asm::disable_interrupts(); - - loop { - core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); - } + asm::disable_irq_save(); + loop {} } diff --git a/machine/testing/src/asm.rs b/machine/testing/src/asm.rs index 9322be2..a303684 100644 --- a/machine/testing/src/asm.rs +++ b/machine/testing/src/asm.rs @@ -21,7 +21,9 @@ macro_rules! __macro_syscall { pub use crate::__macro_syscall as syscall; #[inline(always)] -pub fn disable_interrupts() {} +pub fn disable_irq_save() -> usize { + 0 +} #[inline(always)] pub fn are_interrupts_enabled() -> bool { @@ -29,7 +31,7 @@ pub fn are_interrupts_enabled() -> bool { } #[inline(always)] -pub fn enable_interrupts() {} +pub fn enable_irq_restr(state: usize) {} #[macro_export] macro_rules! __macro_startup_trampoline { diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..f9edb31 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,178 @@ +//! Utility functions and definitions for the kernel. +#![cfg_attr(feature = "nightly", feature(likely_unlikely))] + +use core::fmt::Display; +use hal::mem::PhysAddr; +use core::fmt::Debug; + +/// These two definitions are copied from https://github.com/rust-lang/hashbrown +#[cfg(not(feature = "nightly"))] +#[allow(unused_imports)] +pub(crate) use core::convert::{identity as likely, identity as unlikely}; + +#[cfg(feature = "nightly")] +pub(crate) use core::hint::{likely, unlikely}; + +pub type Result = core::result::Result; + +/// This is a macro that is used to panic when a bug is detected. +/// It is similar to the BUG() macro in the Linux kernel. Link: [https://www.kernel.org/]() +#[macro_export] +macro_rules! bug { + () => { + panic!("BUG at {}:{}", file!(), line!()); + }; + ($fmt:literal $(, $arg:expr)* $(,)?) => {{ + panic!(concat!("BUG at {}:{}: ", $fmt), file!(), line!() $(, $arg)*); + }}; +} + +#[macro_export] +macro_rules! warn { + () => { + kprintln!("WARN at {}:{}", file!(), line!()); + }; + ($fmt:literal $(, $arg:expr)* $(,)?) => {{ + kprintln!(concat!("WARN at {}:{}: ", $fmt), file!(), line!() $(, $arg)*); + }}; +} + +/// This is a macro that is used to panic when a condition is true. +/// It is similar to the BUG_ON() macro in the Linux kernel. Link: [https://www.kernel.org/]() +macro_rules! bug_on { + ($cond:expr) => {{ + let cond = $cond; + #[allow(unused_unsafe)] + if unsafe { $crate::error::unlikely(cond) } { + panic!("BUG({}) at {}:{}", stringify!($cond), file!(), line!()); + } + }}; + ($cond:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{ + let cond = $cond; + #[allow(unused_unsafe)] + if unsafe { $crate::error::unlikely(cond) } { + panic!(concat!("BUG({}) at {}:{}: ", $fmt), stringify!($cond), file!(), line!() $(, $arg)*); + } + }}; +} + +macro_rules! warn_on { + ($cond:expr) => {{ + let cond = $cond; + #[allow(unused_unsafe)] + if unsafe { $crate::error::unlikely(cond) } { + kprintln!("WARN({}) at {}:{}", stringify!($cond), file!(), line!()); + } + }}; + ($cond:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{ + let cond = $cond; + #[allow(unused_unsafe)] + if unsafe { $crate::error::unlikely(cond) } { + kprintln!(concat!("WARN({}) at {}:{}: ", $fmt), stringify!($cond), file!(), line!() $(, $arg)*); + } + }}; +} + +macro_rules! kerr { + ($kind:ident) => { + $crate::error::Error::new($crate::error::Kind::$kind) + }; + ($kind:expr, $msg:expr) => { + use $crate::error::Error; + #[cfg(feature = "error-msg")] + { + Error::new($crate::error::Kind::$kind).with_msg($msg) + } + #[cfg(not(feature = "error-msg"))] + { + Error::new($crate::error::Kind::$kind) + } + }; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Kind { + InvalidAlign, + OutOfMemory, + InvalidSize, + InvalidAddress(PhysAddr), + InvalidArgument, + NotFound, + Hal(hal::Error), +} + +impl Display for Kind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Kind::InvalidAlign => write!(f, "Invalid alignment"), + Kind::OutOfMemory => write!(f, "Out of memory"), + Kind::InvalidSize => write!(f, "Invalid size"), + Kind::InvalidAddress(addr) => write!(f, "Invalid address: {addr:#x}"), + Kind::InvalidArgument => write!(f, "Invalid argument"), + Kind::NotFound => write!(f, "Not found"), + Kind::Hal(e) => write!(f, "HAL error: {e:?}"), + } + } +} + +pub struct Error { + pub kind: Kind, + #[cfg(feature = "error-msg")] + msg: Option<&'static str>, +} + +impl Error { + pub fn new(kind: Kind) -> Self { + #[cfg(feature = "error-msg")] + { + Self { kind, msg: None } + } + #[cfg(not(feature = "error-msg"))] + { + Self { kind } + } + } + + #[cfg(feature = "error-msg")] + pub fn with_msg(mut self, msg: &'static str) -> Self { + self.msg = Some(msg); + self + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "error-msg")] + { + match self.msg { + Some(msg) => write!(f, "{}: {}", self.kind, msg), + None => write!(f, "{}", self.kind), + } + } + #[cfg(not(feature = "error-msg"))] + { + write!(f, "{}", self.kind) + } + } +} + +impl Display for Error { + #[cfg(not(feature = "error-msg"))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.kind) + } + + #[cfg(feature = "error-msg")] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.msg { + Some(msg) => write!(f, "{}: {}", self.kind, msg), + None => write!(f, "{}", self.kind), + } + } +} + +impl From for Error { + fn from(e: hal::Error) -> Self { + Self::new(Kind::Hal(e)) + } +} \ No newline at end of file diff --git a/src/idle.rs b/src/idle.rs index 7c79b37..70633a7 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,11 +7,10 @@ extern "C" fn entry() { } pub fn init() { - let attrs = sched::thread::Attributes { - entry, - fin: None, - }; - if let Err(e) = sched::create_thread(sched::task::KERNEL_TASK, &attrs) { - panic!("failed to create idle thread. Error: {e:?}"); - } -} \ No newline at end of file + let attrs = sched::thread::Attributes { entry, fin: None }; + sched::with(|sched| { + if let Err(e) = sched.create_thread(sched::task::KERNEL_TASK, &attrs) { + panic!("failed to create idle thread. Error: {}", e); + } + }); +} diff --git a/src/lib.rs b/src/lib.rs index 39bab6f..2ce97dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,21 +6,22 @@ #[macro_use] mod macros; #[macro_use] -mod utils; +mod error; mod faults; mod mem; mod types; mod idle; +mod uspace; +mod print; -pub mod print; -pub mod sched; -pub mod sync; -pub mod syscalls; -pub mod time; -pub mod uspace; +mod sched; +mod sync; +mod syscalls; +mod time; + +pub mod uapi; use hal::Machinelike; -include!(concat!(env!("OUT_DIR"), "/syscalls_export.rs")); include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); pub use hal; @@ -44,17 +45,12 @@ pub unsafe extern "C" fn kernel_init() -> ! { // Initialize the memory allocator. let kaddr_space = mem::init_memory(); - kprintln!("Memory initialized."); - if let Err(e) = sched::init(kaddr_space) { - panic!("failed to initialize scheduler. Error: {e:?}"); - } - + sched::init(kaddr_space); kprintln!("Scheduler initialized."); idle::init(); - kprintln!("Idle thread initialized."); let (cyc, ns) = hal::Machine::bench_end(); @@ -63,9 +59,7 @@ pub unsafe extern "C" fn kernel_init() -> ! { ); // Start the init application. - if let Err(e) = uspace::init_app() { - panic!("failed to start init application. Error: {e:?}"); - } + uspace::init_app(); sched::enable(); diff --git a/src/main.rs b/src/main.rs index 3191b0f..5ed2555 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ pub extern "C" fn main() -> ! { #[unsafe(no_mangle)] pub extern "C" fn app_main() -> ! { - osiris::syscall_print(0, "Hello World!".as_bytes().as_ptr(), 12); loop {} } diff --git a/src/mem.rs b/src/mem.rs index f037097..dc1bcdf 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -13,23 +13,6 @@ pub mod pfa; pub const BITS_PER_PTR: usize = core::mem::size_of::() * 8; -/// The possible types of memory. Which is compatible with the multiboot2 memory map. -/// Link: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html -#[repr(C)] -#[allow(unused)] -enum MemoryTypes { - /// Memory that is available for use. - Available = 1, - /// Memory that is reserved for the system. - Reserved = 2, - /// Memory that is reclaimable after ACPI tables are read. - ACPIReclaimable = 3, - /// ACPI Non-volatile-sleeping memory. - Nvs = 4, - /// Memory that is bad. - BadMemory = 5, -} - unsafe extern "C" { unsafe static __stack_top: u8; } @@ -46,18 +29,18 @@ static GLOBAL_ALLOCATOR: SpinLocked = pub fn init_memory() -> vmm::AddressSpace { let stack_top = &raw const __stack_top as usize; if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) { // TODO: Get this from the DeviceTree. - panic!("failed to initialize PFA. Error: {e:?}"); + panic!("failed to initialize PFA. Error: {e}"); } // TODO: Configure. let pgs = 10; let mut kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { - panic!("failed to create kernel address space. Error: {e:?}"); + panic!("failed to create kernel address space. Error: {e}"); }); let begin = kaddr_space.map(Region::new(None, 2 * PAGE_SIZE, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { - panic!("failed to map kernel address space. Error: {e:?}"); + panic!("failed to map kernel address space. Error: {e}"); }); { @@ -65,7 +48,7 @@ pub fn init_memory() -> vmm::AddressSpace { let range = begin..(begin + pgs * PAGE_SIZE); if let Err(e) = unsafe { allocator.add_range(&range) } { - panic!("failed to add range to allocator. Error: {e:?}"); + panic!("failed to add range to allocator. Error: {e}"); } } diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index 85d8830..c7a7831 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -5,7 +5,7 @@ use core::ptr::NonNull; use hal::mem::PhysAddr; -use crate::utils; +use crate::error::Result; pub mod bestfit; @@ -25,7 +25,7 @@ pub const MAX_ADDR: usize = usize::MAX; /// Each range added to the allocator must be valid for the whole lifetime of the allocator and must not overlap with any other range. /// The lifetime of any allocation is only valid as long as the allocator is valid. (A pointer must not be used after the allocator is dropped.) pub trait Allocator { - fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result, utils::KernelError>; + fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result>; unsafe fn free(&mut self, ptr: NonNull, size: usize); } diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index a14c251..9c91fa1 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -2,7 +2,7 @@ use core::{ops::Range, ptr::NonNull}; use hal::mem::PhysAddr; -use crate::utils::{self, KernelError}; +use crate::error::Result; /// The metadata that is before any block in the BestFitAllocator. struct BestFitMeta { @@ -44,13 +44,13 @@ impl BestFitAllocator { /// The range must be valid, 128bit aligned and must not overlapping with any other current or future range. /// The range must also be at least as large as `MIN_RANGE_SIZE`. /// Also the range must stay valid, for the whole lifetime of the allocator. Also the lifetime of any allocation is only valid as long as the allocator is valid. - pub unsafe fn add_range(&mut self, range: &Range) -> Result<(), utils::KernelError> { + pub unsafe fn add_range(&mut self, range: &Range) -> Result<()> { let ptr = range.start; // Check if the pointer is 128bit aligned. if !ptr.is_multiple_of(align_of::()) { - return Err(utils::KernelError::InvalidAlign); - } + return Err(kerr!(InvalidArgument)); + } debug_assert!(range.end > range.start); debug_assert!(range.end.diff(range.start) > size_of::() + Self::align_up()); @@ -92,8 +92,8 @@ impl BestFitAllocator { &mut self, size: usize, requested: Option, - ) -> Result<(NonNull, Option>), utils::KernelError> { - let mut best_fit = Err(utils::KernelError::OutOfMemory); + ) -> Result<(NonNull, Option>)> { + let mut best_fit = Err(kerr!(OutOfMemory)); let mut best_fit_size = usize::MAX; let mut current = self.head; @@ -190,27 +190,27 @@ impl super::Allocator for BestFitAllocator { /// `align` - The alignment of the block. /// /// Returns the user pointer to the block if successful, otherwise an error. - fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result, utils::KernelError> { + fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result> { // Check if the alignment is valid. if align > align_of::() { - return Err(utils::KernelError::InvalidAlign); + return Err(kerr!(InvalidArgument)); } if let Some(request) = request { if !request.is_multiple_of(align) { - return Err(utils::KernelError::InvalidAlign); + return Err(kerr!(InvalidArgument)); } } // Check if the size is valid. if size == 0 { - return Err(utils::KernelError::InvalidSize); + return Err(kerr!(InvalidArgument)); } // For some cfg this warning is correct. But for others its not. #[allow(clippy::absurd_extreme_comparisons)] if size >= super::MAX_ADDR { - return Err(utils::KernelError::InvalidSize); + return Err(kerr!(InvalidArgument)); } // Align the size. @@ -324,7 +324,7 @@ impl super::Allocator for BestFitAllocator { meta.next = self.head; // Check if the size of the block is correct. - BUG_ON!(meta.size != super::super::align_up(size), "Invalid size in free()"); + bug_on!(meta.size != super::super::align_up(size), "Invalid size in free()"); // Set the size of the block. meta.size = size; @@ -338,6 +338,8 @@ impl super::Allocator for BestFitAllocator { #[cfg(test)] mod tests { + use crate::error::Kind; + use super::*; use super::super::*; @@ -421,7 +423,7 @@ mod tests { let request = range.start + 4096; let ptr = allocator.malloc::(128, 1, Some(request)); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + assert!(ptr.is_err_and(|e| e.kind == Kind::OutOfMemory)); } #[test] @@ -436,7 +438,7 @@ mod tests { let request = range.start + 127; let ptr = allocator.malloc::(128, 8, Some(request)); - assert!(ptr.is_err_and(|e| e == utils::KernelError::InvalidAlign)); + assert!(ptr.is_err_and(|e| e.kind == Kind::InvalidAlign)); } #[test] @@ -453,7 +455,7 @@ mod tests { verify_block(ptr, 128, None); let ptr = allocator.malloc::(128, 1, Some(request)); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + assert!(ptr.is_err_and(|e| e.kind == Kind::OutOfMemory)); } #[test] @@ -468,7 +470,7 @@ mod tests { let request = range.end + 128; let ptr = allocator.malloc::(128, 1, Some(request)); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + assert!(ptr.is_err_and(|e| e.kind == Kind::OutOfMemory)); } #[test] @@ -534,7 +536,7 @@ mod tests { } let ptr = allocator.malloc::(SIZE, 1, None); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + assert!(ptr.is_err_and(|e| e.kind == Kind::OutOfMemory)); } #[test] @@ -646,7 +648,7 @@ mod tests { } let ptr = allocator.malloc::(SIZE, 1, None); - assert!(ptr.is_err_and(|e| e == utils::KernelError::OutOfMemory)); + assert!(ptr.is_err_and(|e| e.kind == Kind::OutOfMemory)); verify_ptrs_not_overlaping(ptrs.as_slice()); } diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs index c62b8c1..e20f212 100644 --- a/src/mem/pfa.rs +++ b/src/mem/pfa.rs @@ -2,9 +2,9 @@ use hal::mem::PhysAddr; +use crate::error::Result; use crate::sync::spinlock::SpinLocked; use crate::types::boxed::Box; -use crate::utils::KernelError; use core::pin::Pin; @@ -27,16 +27,16 @@ trait Allocator { /// Safety: /// /// - The returned function must only be called with a useable and valid physical address. - fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>, KernelError>; + fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>>; fn alloc(&mut self, page_count: usize) -> Option; fn free(&mut self, addr: PhysAddr, page_count: usize); } -pub fn init_pfa(addr: PhysAddr) -> Result<(), KernelError> { +pub fn init_pfa(addr: PhysAddr) -> Result<()> { let mut pfa = PFA.lock(); if pfa.is_some() { - return Err(KernelError::CustomError("Page frame allocator is already initialized")); + return Err(kerr!(InvalidArgument)); } let initializer = AllocatorType::initializer(); diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 391ab9a..7dc5692 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -4,8 +4,7 @@ use core::ptr::NonNull; use hal::mem::PhysAddr; use crate::{ - types::boxed::{self, Box}, - utils::KernelError, + error::Result, types::boxed::{self, Box} }; pub struct Allocator { @@ -33,17 +32,17 @@ impl Allocator { } impl super::Allocator for Allocator { - fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>, KernelError> { - |addr: PhysAddr, pcnt: usize| -> Result>, KernelError> { + fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>> { + |addr: PhysAddr, pcnt: usize| -> Result>> { if pcnt > N { todo!("Runtime page frame allocator for more than {} pages", N) } if !addr.is_multiple_of(core::mem::align_of::()) { - return Err(KernelError::InvalidAlign); + return Err(kerr!(InvalidArgument)); } - let ptr = NonNull::new(addr.as_mut_ptr::()).ok_or(KernelError::InvalidAddress(addr))?; + let ptr = NonNull::new(addr.as_mut_ptr::()).ok_or(kerr!(InvalidArgument))?; // Align this up to PAGE_SIZE let begin = addr + size_of::(); let begin = if begin.is_multiple_of(super::PAGE_SIZE) { @@ -52,7 +51,7 @@ impl super::Allocator for Allocator { PhysAddr::new((begin.as_usize() + super::PAGE_SIZE - 1) & !(super::PAGE_SIZE - 1)) }; // TODO: Subtract the needed pages from the available - unsafe { core::ptr::write(ptr.as_ptr(), Self::new(begin).ok_or(KernelError::InvalidAddress(begin))?) }; + unsafe { core::ptr::write(ptr.as_ptr(), Self::new(begin).ok_or(kerr!(InvalidArgument))?) }; // Safety: Ptr is properly aligned and non-null. The validity of the memory at that address is valid by the call contract. Ok(Pin::new(unsafe { boxed::Box::from_raw(ptr) })) diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs index ee86228..6ddc7e9 100644 --- a/src/mem/vmm.rs +++ b/src/mem/vmm.rs @@ -1,6 +1,6 @@ use hal::mem::{PhysAddr, VirtAddr}; -use crate::{utils::KernelError}; +use crate::error::Result; mod nommu; @@ -62,12 +62,12 @@ impl Region { pub trait AddressSpacelike { // Size is the amount of pages in the address space. On nommu systems this will be reserved. - fn new(pages: usize) -> Result where Self: Sized; - fn map(&mut self, region: Region) -> Result; - fn unmap(&mut self, region: &Region) -> Result<(), KernelError>; - fn protect(&mut self, region: &Region, perms: Perms) -> Result<(), KernelError>; + fn new(pages: usize) -> Result where Self: Sized; + fn map(&mut self, region: Region) -> Result; + fn unmap(&mut self, region: &Region) -> Result<()>; + fn protect(&mut self, region: &Region, perms: Perms) -> Result<()>; fn virt_to_phys(&self, addr: VirtAddr) -> Option; fn phys_to_virt(&self, addr: PhysAddr) -> Option; fn end(&self) -> VirtAddr; - fn activate(&self) -> Result<(), KernelError>; + fn activate(&self) -> Result<()>; } \ No newline at end of file diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs index 8927471..f169c14 100644 --- a/src/mem/vmm/nommu.rs +++ b/src/mem/vmm/nommu.rs @@ -3,11 +3,10 @@ use core::ptr::copy_nonoverlapping; use hal::mem::{PhysAddr, VirtAddr}; use crate::{ - mem::{ + error::Result, mem::{ alloc::{Allocator, bestfit}, pfa, vmm, - }, - utils::KernelError, + } }; pub struct AddressSpace { @@ -17,11 +16,11 @@ pub struct AddressSpace { } impl vmm::AddressSpacelike for AddressSpace { - fn new(pgs: usize) -> Result { - let begin = pfa::alloc_page(pgs).ok_or(KernelError::OutOfMemory)?; + fn new(pgs: usize) -> Result { + let begin = pfa::alloc_page(pgs).ok_or(kerr!(OutOfMemory))?; let end = begin .checked_add(pgs * pfa::PAGE_SIZE) - .ok_or(KernelError::OutOfMemory)?; + .ok_or(kerr!(OutOfMemory))?; let mut allocator = bestfit::BestFitAllocator::new(); unsafe { allocator.add_range(&(begin..end))? }; @@ -33,7 +32,7 @@ impl vmm::AddressSpacelike for AddressSpace { }) } - fn map(&mut self, region: vmm::Region) -> Result { + fn map(&mut self, region: vmm::Region) -> Result { let req = region.start.and_then(|virt| self.virt_to_phys(virt)); // TODO: per page align let align = core::mem::align_of::(); @@ -54,11 +53,11 @@ impl vmm::AddressSpacelike for AddressSpace { Ok(start.into()) } - fn unmap(&mut self, _region: &vmm::Region) -> Result<(), KernelError> { + fn unmap(&mut self, _region: &vmm::Region) -> Result<()> { Ok(()) } - fn protect(&mut self, _region: &vmm::Region, _perms: vmm::Perms) -> Result<(), KernelError> { + fn protect(&mut self, _region: &vmm::Region, _perms: vmm::Perms) -> Result<()> { Ok(()) } @@ -76,7 +75,7 @@ impl vmm::AddressSpacelike for AddressSpace { self.phys_to_virt(self.end).unwrap() } - fn activate(&self) -> Result<(), KernelError> { + fn activate(&self) -> Result<()> { Ok(()) } } diff --git a/src/sched.rs b/src/sched.rs index 510ff80..39583d8 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,32 +1,44 @@ //! This module provides access to the scheduler. mod dispch; -pub mod rt; pub mod rr; +pub mod rt; pub mod task; pub mod thread; -use core::{ffi::c_void, sync::atomic::{AtomicBool, Ordering}}; +use core::{ + ffi::c_void, + sync::atomic::{AtomicBool, Ordering}, +}; use hal::Schedable; use crate::{ - mem, sync::{atomic::AtomicU64, spinlock::SpinLocked}, time::{self, tick}, types::{ + error::Result, + mem, + sched::thread::Waiter, + sync::{self, atomic::AtomicU64, spinlock::SpinLocked}, + time::{self}, + types::{ array::IndexMap, rbtree::RbTree, - traits::{Get, GetMut}, + traits::{Get, GetMut, Project}, view::ViewMut, - }, utils::KernelError + }, }; type ThreadMap = IndexMap; type TaskMap = IndexMap; -static SCHED: SpinLocked> = SpinLocked::new(Scheduler::new()); +type GlobalScheduler = Scheduler<32>; + +static SCHED: SpinLocked = SpinLocked::new(GlobalScheduler::new()); static DISABLED: AtomicBool = AtomicBool::new(true); static NEXT_TICK: AtomicU64 = AtomicU64::new(0); +type WaiterView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::Waiter, ThreadMap>; + pub struct Scheduler { threads: ThreadMap, tasks: TaskMap, @@ -55,98 +67,186 @@ impl Scheduler { } } - fn land(&mut self, ctx: *mut c_void) -> Result<(), KernelError> { + fn land(&mut self, ctx: *mut c_void) { if let Some(current) = self.current { - let thread = self.threads.get_mut(current).ok_or(KernelError::InvalidArgument)?; - return thread.save_ctx(ctx); + let mut kill = None; + if let Some(thread) = self.threads.get_mut(current) { + if thread.save_ctx(ctx).is_err() { + warn!( + "failed to save context (SP: {:x}) of thread {}.", + ctx as usize, current + ); + kill = Some(thread.task_id()); + } + } else { + bug!("failed to land thread {}. Does not exist.", current); + } + + if let Some(task_id) = kill { + self.dequeue(current); + self.current = None; + self.kill_task(task_id); + } } + } - Ok(()) + fn schedule_resched(now: u64, next: u64) { + let old = NEXT_TICK.load(Ordering::Acquire); + + if old > now && old <= next { + return; + } + + NEXT_TICK.store(next, Ordering::Release); } - pub fn enqueue(&mut self, uid: thread::UId) -> Result<(), KernelError> { - let thread = self.threads.get(uid).ok_or(KernelError::InvalidArgument)?; + pub fn enqueue(&mut self, now: u64, uid: thread::UId) -> Result<()> { + let thread = self.threads.get(uid).ok_or(kerr!(InvalidArgument))?; if thread.rt_server().is_some() { - let mut view = - ViewMut::>::new(&mut self.threads); - self.rt_scheduler.enqueue(uid, &mut view); + let mut view = rt::ServerView::::new(&mut self.threads); + self.rt_scheduler.enqueue(uid, now, &mut view); } else { self.rr_scheduler.enqueue(uid, &mut self.threads)?; } - - // A new thread was added -> Trigger a reschedule. - NEXT_TICK.store(tick(), Ordering::Release); + reschedule(); Ok(()) } - pub fn do_sched( - &mut self, - now: u64, - old: Option, - ) -> Option<(*mut c_void, &mut task::Task)> { + fn do_wakeups(&mut self, now: u64) { + while let Some(uid) = self.wakeup.min() { + { + let mut view = WaiterView::::new(&mut self.threads); + let waiter = view.get(uid).expect("THIS IS A BUG!"); + + if waiter.until() > now { + Self::schedule_resched(now, waiter.until()); + break; + } + + self.wakeup.remove(uid, &mut view); + } + + self.enqueue(now, uid); + } + } + + pub fn do_sched(&mut self, now: u64) -> Option<(*mut c_void, &mut task::Task)> { let dt = now - self.last_tick; self.last_tick = now; - if let Some(old) = old { + if let Some(old) = self.current { let mut view = rt::ServerView::::new(&mut self.threads); - // If this is not a real-time thread, this will just do nothing. self.rt_scheduler.put(old, dt, &mut view); - // If this is not a round-robin thread, this will just do nothing. self.rr_scheduler.put(old, dt); - - // TODO: thread is still enqueued. Dequeue if blocked or sleeping and put to the respective tree/list. - // If it exited remove it completely. } + self.do_wakeups(now); + let mut view = rt::ServerView::::new(&mut self.threads); - let (new, budget) = if let Some((new, budget)) = self.rt_scheduler.pick(now, &mut view) { - (new, budget) - } else if let Some((new, budget)) = self.rr_scheduler.pick(&mut self.threads) { - (new, budget) - } else { - // No thread to run. Run the idle thread. - (thread::IDLE_THREAD, u64::MAX) - }; + let (new, budget) = self + .rt_scheduler + .pick(now, &mut view) + .or_else(|| self.rr_scheduler.pick(&mut self.threads)) + .unwrap_or((thread::IDLE_THREAD, 1000)); let ctx = self.threads.get(new)?.ctx(); let task = self.tasks.get_mut(self.threads.get(new)?.task_id())?; self.current = Some(new); + let next = now.saturating_add(budget); - // Only store next_tick if now + budget is smaller than the current next tick. - let next_tick = now + budget; - let mut old_tick = NEXT_TICK.load(Ordering::Acquire); + Self::schedule_resched(now, next); + Some((ctx, task)) + } - while NEXT_TICK.compare_exchange(old_tick, next_tick, Ordering::Release, Ordering::Acquire).is_err() { - old_tick = NEXT_TICK.load(Ordering::Acquire); - if next_tick >= old_tick { - break; - } + pub fn sleep_until(&mut self, until: u64, now: u64) -> Result<()> { + if until <= now { + return Ok(()); } + let uid = self.current.ok_or(kerr!(InvalidArgument))?; - Some((ctx, task)) + if let Some(thread) = self.threads.get_mut(uid) { + thread.set_waiter(Some(Waiter::new(until, uid))); + } else { + bug!( + "failed to put current thread {} to sleep. Does not exist.", + uid + ); + } + + if self + .wakeup + .insert(uid, &mut WaiterView::::new(&mut self.threads)) + .is_err() + { + bug!("failed to insert thread {} into wakeup tree.", uid); + } + + + + self.dequeue(uid); + reschedule(); + Ok(()) + } + + pub fn kick(&mut self, uid: thread::UId) -> Result<()> { + let thread = self.threads.get_mut(uid).ok_or(kerr!(InvalidArgument))?; + if let Some(waiter) = Project::::project_mut(thread) { + waiter.set_until(0); + } + Ok(()) } - pub fn dequeue(&mut self, uid: thread::UId) -> Option { + pub fn dequeue(&mut self, uid: thread::UId) { let mut view = rt::ServerView::::new(&mut self.threads); - // If this is not a real-time thread, this will just do nothing. self.rt_scheduler.dequeue(uid, &mut view); - - self.threads.remove(&uid) + self.rr_scheduler.dequeue(uid, &mut self.threads); } - pub fn create_task(&mut self, task: &task::Attributes) -> Result { - let uid = task::UId::new(self.id_gen).ok_or(KernelError::InvalidArgument)?; + pub fn create_task(&mut self, task: &task::Attributes) -> Result { + let uid = task::UId::new(self.id_gen).ok_or(kerr!(InvalidArgument))?; self.id_gen += 1; self.tasks.insert(&uid, task::Task::new(uid, task)?)?; Ok(uid) } - pub fn create_thread(&mut self, task: task::UId, attrs: &thread::Attributes) -> Result { - let task = self.tasks.get_mut(task).ok_or(KernelError::InvalidArgument)?; + pub fn kill_task(&mut self, uid: task::UId) -> Result<()> { + let task_id = self.tasks.get(uid).ok_or(kerr!(InvalidArgument))?.id; + self.tasks.remove(&uid).ok_or(kerr!(InvalidArgument))?; + + let begin = match self.threads.next(None) { + Some(i) => i, + None => return Ok(()), + }; + let mut i = begin; + + while i != begin { + i = (i + 1) % N; + + let mut id = None; + if let Some(thread) = self.threads.at_cont(i) { + if thread.task_id() == task_id { + id = Some(thread.uid()); + } + } + + if let Some(id) = id { + self.dequeue(id); + } + } + + Ok(()) + } + + pub fn create_thread( + &mut self, + task: task::UId, + attrs: &thread::Attributes, + ) -> Result { + let task = self.tasks.get_mut(task).ok_or(kerr!(InvalidArgument))?; let thread = task.create_thread(self.id_gen, attrs)?; let uid = thread.uid(); @@ -157,23 +257,24 @@ impl Scheduler { } } -pub fn init(kaddr_space: mem::vmm::AddressSpace) -> Result<(), KernelError> { - let mut sched = SCHED.lock(); - let uid = task::KERNEL_TASK; - sched.tasks.insert(&uid, task::Task::from_addr_space(uid, kaddr_space)?) +pub fn with T>(f: F) -> T { + sync::atomic::irq_free(|| { + let mut sched = SCHED.lock(); + f(&mut sched) + }) } -pub fn create_task(attrs: &task::Attributes) -> Result { - SCHED.lock().create_task(attrs) -} - -pub fn create_thread(task: task::UId, attrs: &thread::Attributes) -> Result { - let mut sched = SCHED.lock(); - sched.create_thread(task, attrs) -} - -pub fn enqueue(uid: thread::UId) -> Result<(), KernelError> { - SCHED.lock().enqueue(uid) +pub fn init(kaddr_space: mem::vmm::AddressSpace) { + with(|sched| { + let uid = task::KERNEL_TASK; + if let Ok(task) = task::Task::from_addr_space(uid, kaddr_space) { + if sched.tasks.insert(&uid, task).is_err() { + panic!("failed to create kernel task."); + } + } else { + panic!("failed to create kernel address space."); + } + }) } pub fn needs_reschedule(now: u64) -> bool { @@ -196,41 +297,30 @@ pub fn enable() { /// Reschedule the tasks. pub fn reschedule() { + if DISABLED.load(Ordering::Acquire) { + return; + } + hal::Machine::trigger_reschedule(); } /// cbindgen:ignore /// cbindgen:no-export #[unsafe(no_mangle)] -pub extern "C" fn sched_enter(ctx: *mut c_void) -> *mut c_void { - let mut sched = SCHED.lock(); - let mut broken = false; - let old = sched.current; - - if sched.land(ctx).is_err() { - sched.current.inspect(|uid| { - if *uid == thread::IDLE_THREAD { - BUG!("failed to land the idle thread. something is horribly broken."); - } +pub extern "C" fn sched_enter(mut ctx: *mut c_void) -> *mut c_void { + with(|sched| { + let old = sched.current.map(|c| c.owner()); + sched.land(ctx); - // If we cannot reasonably land. We dequeue the thread. - sched.dequeue(*uid); - // TODO: Warn - sched.current = None; - broken = true; - }); - } - - if let Some((ctx, task)) = sched.do_sched(time::tick(), old) { - if let Some(old) = old - && task.id != old.owner() { + if let Some((new, task)) = sched.do_sched(time::tick()) { + if old != Some(task.id) { dispch::prepare(task); } - - ctx - } else if broken { - BUG!("failed to reschedule after a failed landing. something is horribly broken."); - } else { + ctx = new; + } else { + bug!("failed to schedule a thread. No threads available."); + } + ctx - } + }) } diff --git a/src/sched/rr.rs b/src/sched/rr.rs index c4613f2..ee9f877 100644 --- a/src/sched/rr.rs +++ b/src/sched/rr.rs @@ -1,10 +1,5 @@ use crate::{ - sched::{ - thread::{self}, - }, - types::{ - list::List, - }, + error::Result, sched::thread::{self}, types::list::List }; pub struct Scheduler { @@ -21,8 +16,8 @@ impl Scheduler { Self { queue: List::new(), current: None, current_left: 0, quantum: 1000 } } - pub fn enqueue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) -> Result<(), crate::utils::KernelError> { - self.queue.push_back(uid, storage).map_err(|_| crate::utils::KernelError::InvalidArgument) + pub fn enqueue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) -> Result<()> { + self.queue.push_back(uid, storage).map_err(|_| kerr!(InvalidArgument)) } pub fn put(&mut self, uid: thread::UId, dt: u64) { @@ -34,15 +29,29 @@ impl Scheduler { } pub fn pick(&mut self, storage: &mut super::ThreadMap) -> Option<(thread::UId, u64)> { - if self.current_left == 0 { - if let Some(current) = self.current { + match self.current { + Some(current) if self.current_left > 0 => return Some((current, self.current_left)), + Some(current) => { + self.queue.pop_front(storage); self.queue.push_back(current, storage); - } - self.current = self.queue.pop_front(storage).ok().flatten(); - self.current_left = self.quantum; + self.current = self.queue.head(); + self.current_left = self.quantum; + } + None => { + self.current = self.queue.head(); + self.current_left = self.quantum; + } } self.current.map(|id| (id, self.current_left)) } + + pub fn dequeue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) { + self.queue.remove(uid, storage); + + if self.current == Some(uid) { + self.current = None; + } + } } diff --git a/src/sched/rt.rs b/src/sched/rt.rs index b5a66b9..7c3405b 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -13,13 +13,20 @@ impl Scheduler { } } - pub fn enqueue(&mut self, uid: thread::UId, storage: &mut ServerView) { - self.edf.insert(uid, storage); + pub fn enqueue(&mut self, uid: thread::UId, now: u64, storage: &mut ServerView) { + if let Some(server) = storage.get_mut(uid) { + server.replenish(now); + self.edf.insert(uid, storage); + } } pub fn put(&mut self, uid: thread::UId, dt: u64, storage: &mut ServerView) { - if let Some(server) = storage.get_mut(uid) { - server.consume(dt); + if Some(uid) == self.edf.min() { + if let Some(server) = storage.get_mut(uid) { + server.consume(dt); + } else { + bug!("thread {} not found in storage", uid); + } } } diff --git a/src/sched/scheduler.rs b/src/sched/scheduler.rs deleted file mode 100644 index aded20b..0000000 --- a/src/sched/scheduler.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! The scheduler module is responsible for managing the tasks and threads in the system. -//! It provides the necessary functions to create tasks and threads, and to switch between them. - -use core::{ffi::c_void, sync::atomic::AtomicBool}; - -use super::task::{Task, TaskId}; -use crate::{ - mem::{self, array::IndexMap, heap::BinaryHeap, queue::Queue}, - sched::{ - task::TaskDescriptor, - thread::{RunState, ThreadMap, ThreadUId, Timing}, - }, - sync::spinlock::SpinLocked, - utils, -}; - -/// The global scheduler instance. -pub static SCHEDULER: SpinLocked = SpinLocked::new(Scheduler::new()); -static SCHEDULER_ENABLED: AtomicBool = AtomicBool::new(false); - -/// The scheduler struct. It keeps track of the tasks and threads in the system. -/// This scheduler is a simple Rate Monotonic Scheduler (RMS) implementation. -#[derive(Debug)] -pub struct Scheduler { - /// The current running thread. - current: Option, - /// Fast interval store. This gets updated every time a new thread is selected. - current_interval: usize, - /// Stores the tasks in the system. - user_tasks: IndexMap, - /// Stores the threads in the system. - threads: ThreadMap<8>, - /// The priority queue that yields the next thread to run. - queue: BinaryHeap<(usize, ThreadUId), 32>, - /// The callbacks queue that stores the threads that need to be fired in the future. - callbacks: Queue<(ThreadUId, usize), 32>, - /// The progression of the time interval of the scheduler. - time: usize, -} - -impl Scheduler { - /// Create a new scheduler instance. - pub const fn new() -> Self { - Self { - current: None, - current_interval: 0, - user_tasks: IndexMap::new(), - threads: ThreadMap::new(), - queue: BinaryHeap::new(), - callbacks: Queue::new(), - time: 0, - } - } - - pub fn create_task(&mut self, desc: TaskDescriptor) -> Result { - let size = mem::align_up(desc.mem_size); - let idx = self - .user_tasks - .find_empty() - .ok_or(utils::KernelError::OutOfMemory)?; - let task_id = TaskId::new_user(idx); - - let task = Task::new(size, task_id)?; - self.user_tasks.insert(&idx, task)?; - Ok(task_id) - } - - pub fn create_thread( - &mut self, - entry: extern "C" fn(), - fin: Option !>, - timing: Timing, - task_id: TaskId, - ) -> Result { - let task_idx: usize = task_id.into(); - - if let Some(task) = self.user_tasks.get_mut(&task_idx) { - let desc = task.create_thread(entry, fin, timing)?; - let id = self.threads.create(desc)?; - self.queue.push((timing.period, id))?; - Ok(id) - } else { - Err(utils::KernelError::InvalidArgument) - } - } - - /// Updates the current thread context with the given context. - /// - /// `ctx` - The new context to update the current thread with. - fn update_current_ctx(&mut self, ctx: *mut c_void) { - if let Some(id) = self.current - && let Some(thread) = self.threads.get_mut(&id) - { - thread - .update_sp(ctx) - .expect("Failed to update thread context"); - } - } - - /// Selects a new thread to run, sets the previous thread as ready, and sets the new thread as runs. - /// The old thread will be added to the queue to be fired in the next period. - /// The new thread will be selected based on the priority queue. - /// - /// Returns the context of the new thread to run, or `None` if no thread is available. - fn select_new_thread(&mut self) -> Option<*mut c_void> { - if let Some(id) = self.queue.pop().map(|(_, id)| id) { - // Set the previous thread as ready. And add a callback from now. - if let Some(id) = self.current - && let Some(thread) = self.threads.get_mut(&id) - { - thread.update_run_state(RunState::Ready); - // The delay that is already in the queue. - let delay = self.callbacks.back().map(|(_, delay)| *delay).unwrap_or(0); - // Check if the period is already passed. - if thread.timing().period > (self.time + delay) { - // Add the callback to the queue. If it fails, we can't do much. - let _ = self - .callbacks - .push_back((id, thread.timing().period - (self.time + delay))); - } else { - // If the period is already passed, add it to the queue immediately. - let _ = self.queue.push((thread.timing().exec_time, id)); - } - } - - if let Some(thread) = self.threads.get_mut(&id) { - thread.update_run_state(RunState::Runs); - - // Set the new thread as the current one. - self.current_interval = thread.timing().exec_time; - self.current = Some(id); - - // Return the new thread context. - return Some(thread.sp()); - } - } - - None - } - - /// Fires the thread if necessary. - /// - /// Returns `true` if a thread was fired, otherwise `false`. - fn fire_thread_if_necessary(&mut self) -> bool { - let mut found = false; - while let Some((id, cnt)) = self.callbacks.front().cloned() { - // If the delay is 0, we can fire the thread. - if cnt - 1 == 0 { - self.callbacks.pop_front(); - if let Some(thread) = self.threads.get_mut(&id) { - thread.update_run_state(RunState::Ready); - - let _ = self.queue.push((thread.timing().exec_time, id)); - found = true; - } - } else { - // If the delay is not 0, we need to update the delay and reinsert it. - let _ = self.callbacks.insert(0, (id, cnt - 1)); - break; - } - } - - found - } - - /// Ticks the scheduler. This function is called every time the system timer ticks. - pub fn tick(&mut self) -> bool { - self.time += 1; - - // If a thread was fired, we need to reschedule. - if self.fire_thread_if_necessary() { - return true; - } - - // If the current thread is done, we need to reschedule. - if self.time >= self.current_interval { - self.time = 0; - return true; - } - - false - } -} - -pub fn enabled() -> bool { - SCHEDULER_ENABLED.load(core::sync::atomic::Ordering::Acquire) -} - -pub fn set_enabled(enabled: bool) { - SCHEDULER_ENABLED.store(enabled, core::sync::atomic::Ordering::Release); -} - -/// cbindgen:ignore -/// cbindgen:no-export -#[unsafe(no_mangle)] -pub extern "C" fn sched_enter(ctx: *mut c_void) -> *mut c_void { - { - let mut scheduler = SCHEDULER.lock(); - - // Update the current context. - scheduler.update_current_ctx(ctx); - - // Select a new thread to run, if available. - scheduler.select_new_thread().unwrap_or(ctx) - } -} diff --git a/src/sched/task.rs b/src/sched/task.rs index 9527f81..52b0345 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -1,4 +1,5 @@ //! This module provides the basic task and thread structures for the scheduler. +use core::fmt::Display; use core::num::NonZero; use core::borrow::Borrow; @@ -7,12 +8,12 @@ use hal::{Stack}; use hal::stack::{Stacklike}; +use crate::error::Result; use crate::sched::thread; use crate::{mem, sched}; use crate::mem::vmm::{AddressSpacelike}; use crate::types::traits::ToIndex; -use crate::utils::KernelError; pub struct Defaults { pub stack_pages: usize, @@ -50,6 +51,12 @@ impl ToIndex for UId { } } +impl Display for UId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Task-{}", self.uid) + } +} + pub struct Attributes { pub resrv_pgs: Option>, } @@ -65,14 +72,14 @@ pub struct Task { } impl Task { - pub fn new(id: UId, attrs: &Attributes) -> Result { + pub fn new(id: UId, attrs: &Attributes) -> Result { // TODO: On MMU systems, the resrv_pgs attribute will be ignored, as memory will not be reserved. - let resrv_pgs = attrs.resrv_pgs.ok_or(KernelError::OutOfMemory)?; + let resrv_pgs = attrs.resrv_pgs.ok_or(kerr!(InvalidArgument))?; let address_space = mem::vmm::AddressSpace::new(resrv_pgs.get())?; Self::from_addr_space(id, address_space) } - pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Result { + pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Result { Ok(Self { id, address_space, @@ -90,7 +97,7 @@ impl Task { fn allocate_stack( &mut self, attrs: &thread::Attributes, - ) -> Result { + ) -> Result { let size = DEFAULTS.stack_pages * mem::pfa::PAGE_SIZE; let region = mem::vmm::Region::new( None, @@ -112,7 +119,7 @@ impl Task { &mut self, uid: usize, attrs: &thread::Attributes, - ) -> Result { + ) -> Result { let stack = self.allocate_stack(attrs)?; let stack = unsafe { Stack::new(stack) }?; @@ -120,4 +127,8 @@ impl Task { Ok(sched::thread::Thread::new(tid.get_uid(uid), stack)) } + + pub fn tid_cntr(&self) -> usize { + self.tid_cntr + } } diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 75a1890..86af4aa 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -1,17 +1,20 @@ // ----------------------------------- Identifiers ----------------------------------- +use core::fmt::Display; use core::{borrow::Borrow, ffi::c_void}; use hal::{Stack, stack::EntryFn}; use hal::stack::{FinFn, Stacklike}; use proc_macros::TaggedLinks; +use crate::error::Result; use crate::sched::task::{self, KERNEL_TASK}; +use crate::time::tick; use crate::types::list; -use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}, utils::KernelError}; +use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}}; pub const IDLE_THREAD: UId = UId { - uid: 0, + uid: 1, tid: Id { id: 0, owner: KERNEL_TASK }, }; @@ -94,6 +97,12 @@ impl ToIndex for UId { } } +impl Display for UId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "T{}-{}", self.tid.owner(), self.tid.as_usize()) + } +} + // ------------------------------------------------------------------------- /// The state of a thread. @@ -118,9 +127,8 @@ pub struct State { #[derive(TaggedLinks)] pub struct RtServer { budget: u64, - total_budget: u64, - - reservation: u64, + budget_left: u64, + period: u64, deadline: u64, // Back-reference to the thread uid. @@ -132,32 +140,35 @@ pub struct RtServer { } impl RtServer { - pub fn new(budget: u64, reservation: u64, deadline: u64, uid: UId) -> Self { + pub fn new(budget: u64, period: u64, uid: UId) -> Self { Self { budget, - total_budget: budget, - reservation, - deadline, + budget_left: budget, + period, + deadline: tick() + period, uid, _rt_links: rbtree::Links::new(), } } + pub fn budget_left(&self) -> u64 { + self.budget_left + } + pub fn budget(&self) -> u64 { self.budget } pub fn replenish(&mut self, now: u64) { - let next = self.deadline + self.reservation; - self.deadline = next.max(now + self.reservation); - self.budget = self.total_budget; + self.deadline += self.period; + self.budget_left = self.budget; } pub fn consume(&mut self, dt: u64) { - if self.budget >= dt { - self.budget -= dt; + if self.budget_left >= dt { + self.budget_left -= dt; } else { - self.budget = 0; + self.budget_left = 0; } } @@ -182,6 +193,46 @@ impl Compare for RtServer { } } +#[derive(Debug, Clone, Copy)] +#[derive(TaggedLinks)] +pub struct Waiter { + /// The time when the Thread will be awakened. + until: u64, + + // Back-reference to the thread uid. + uid: UId, + /// Wakup tree links for the thread. + #[rbtree(tag = WakupTree, idx = UId)] + _wakeup_links: rbtree::Links, +} + +impl Waiter { + pub fn new(until: u64, uid: UId) -> Self { + Self { + until, + uid, + _wakeup_links: rbtree::Links::new(), + } + } + + pub fn until(&self) -> u64 { + self.until + } + + pub fn set_until(&mut self, until: u64) { + self.until = until; + } +} + +impl Compare for Waiter { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.until.cmp(&other.until) { + core::cmp::Ordering::Equal => self.uid.cmp(&other.uid), + ord => ord, + } + } +} + #[derive(Debug, Clone, Copy)] pub struct WakupTree; #[derive(Debug, Clone, Copy)] @@ -205,9 +256,8 @@ pub struct Thread { uid: UId, /// If the thread is real-time, its contains a constant bandwidth server. rt_server: Option, - /// Wakup tree links for the thread. - #[rbtree(tag = WakupTree, idx = UId)] - _wakeup_links: rbtree::Links, + + waiter: Option, #[list(tag = RRList, idx = UId)] rr_links: list::Links, @@ -228,12 +278,20 @@ impl Thread { }, uid, rt_server: None, - _wakeup_links: rbtree::Links::new(), + waiter: None, rr_links: list::Links::new(), } } - pub fn save_ctx(&mut self, ctx: *mut c_void) -> Result<(), KernelError> { + pub fn set_waiter(&mut self, waiter: Option) { + self.waiter = waiter; + } + + pub fn waiter(&self) -> Option<&Waiter> { + self.waiter.as_ref() + } + + pub fn save_ctx(&mut self, ctx: *mut c_void) -> Result<()> { let sp = self.state.stack.create_sp(ctx)?; self.state.stack.set_sp(sp); Ok(()) @@ -260,12 +318,6 @@ impl Thread { } } -impl Compare for Thread { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.uid.cmp(&other.uid) - } -} - impl Project for Thread { fn project(&self) -> Option<&RtServer> { self.rt_server.as_ref() @@ -274,4 +326,14 @@ impl Project for Thread { fn project_mut(&mut self) -> Option<&mut RtServer> { self.rt_server.as_mut() } -} \ No newline at end of file +} + +impl Project for Thread { + fn project(&self) -> Option<&Waiter> { + self.waiter.as_ref() + } + + fn project_mut(&mut self) -> Option<&mut Waiter> { + self.waiter.as_mut() + } +} diff --git a/src/sync/atomic.rs b/src/sync/atomic.rs index e3a4bde..a1f54be 100644 --- a/src/sync/atomic.rs +++ b/src/sync/atomic.rs @@ -15,16 +15,9 @@ pub use core::sync::atomic::Ordering; #[inline(always)] pub fn irq_free(f: impl FnOnce() -> T) -> T { - let enabled = hal::asm::are_interrupts_enabled(); - if enabled { - hal::asm::disable_interrupts(); - } - + let state = hal::asm::disable_irq_save(); let result = f(); - - if enabled { - hal::asm::enable_interrupts(); - } + hal::asm::enable_irq_restr(state); result } diff --git a/src/syscalls.rs b/src/syscalls.rs index eda3f92..c573d2f 100644 --- a/src/syscalls.rs +++ b/src/syscalls.rs @@ -3,11 +3,11 @@ use core::ffi::{c_int, c_uint}; mod file; -mod tasks; +mod sched; // We need to import everything so that the macro is able to find the entry functions. use file::*; -use tasks::*; +use sched::*; #[unsafe(no_mangle)] pub extern "C" fn handle_syscall(number: usize, args: *const c_uint) -> c_int { diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs new file mode 100644 index 0000000..38688dd --- /dev/null +++ b/src/syscalls/sched.rs @@ -0,0 +1,29 @@ +//! This module provides task management related syscalls. + +use core::ffi::c_int; + +use proc_macros::syscall_handler; + +use crate::{sched, time}; + +#[syscall_handler(num = 1)] +fn sleep(until_hi: u32, until_lo: u32) -> c_int { + let until = ((until_hi as u64) << 32) | (until_lo as u64); + sched::with(|sched| { + sched.sleep_until(until, time::tick()); + }); + 0 +} + +#[syscall_handler(num = 2)] +fn sleep_for(duration_hi: u32, duration_lo: u32) -> c_int { + let duration = ((duration_hi as u64) << 32) | (duration_lo as u64); + sched::with(|sched| { + let now = time::tick(); + if sched.sleep_until(now + duration, now).is_err() { + panic!("failed to sleep for duration: {duration}"); + } + }); + 0 +} + diff --git a/src/syscalls/tasks.rs b/src/syscalls/tasks.rs deleted file mode 100644 index 2a6d68d..0000000 --- a/src/syscalls/tasks.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! This module provides task management related syscalls. - -use core::ffi::c_int; - -/* -use crate::sched; -use macros::syscall_handler; - -/// Syscall handler: reschedule. -/// This syscall is used to request a reschedule. -/// -/// No arguments are passed to this syscall. -#[syscall_handler(num = 1)] -fn syscall_reschedule() -> c_int { - sched::reschedule(); - 0 -} - -#[syscall_handler(num = 2)] -fn syscall_exec(entry: usize) -> c_int { - let entry: extern "C" fn() -> () = unsafe { core::mem::transmute(entry) }; - - let timing = sched::thread::Timing { - period: 8, - deadline: 8, - exec_time: 2, - }; - - sched::create_task(sched::task::TaskDescriptor { mem_size: 0 }) - .and_then(|task| sched::create_thread(task, entry, None, timing)) - .map(|_| 0) - .unwrap_or(-1) -}*/ diff --git a/src/types/array.rs b/src/types/array.rs index 3e50514..67a9cba 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -1,12 +1,12 @@ //! This module implements static and dynamic arrays for in-kernel use. +use crate::error::Result; + use super::{ traits::{Get, GetMut, ToIndex}, boxed::Box, }; -use crate::utils::KernelError; - use core::{borrow::Borrow, mem::MaybeUninit}; use core::{ ops::{Index, IndexMut}, @@ -39,14 +39,14 @@ impl IndexMap /// `value` - The value to insert. /// /// Returns `Ok(())` if the index was in-bounds, otherwise `Err(KernelError::OutOfMemory)`. - pub fn insert(&mut self, idx: &K, value: V) -> Result<(), KernelError> { + pub fn insert(&mut self, idx: &K, value: V) -> Result<()> { let idx = K::to_index(Some(idx)); if idx < N { self.data[idx] = Some(value); Ok(()) } else { - Err(KernelError::OutOfMemory) + Err(kerr!(OutOfMemory)) } } @@ -55,7 +55,7 @@ impl IndexMap /// `value` - The value to insert. /// /// Returns `Ok(index)` if the value was inserted, otherwise `Err(KernelError::OutOfMemory)`. - pub fn insert_next(&mut self, value: V) -> Result { + pub fn insert_next(&mut self, value: V) -> Result { for (i, slot) in self.data.iter_mut().enumerate() { if slot.is_none() { *slot = Some(value); @@ -63,7 +63,7 @@ impl IndexMap } } - Err(KernelError::OutOfMemory) + Err(kerr!(OutOfMemory)) } /// Remove the value at the given index. @@ -113,6 +113,14 @@ impl IndexMap None } + pub fn at_cont(&self, idx: usize) -> Option<&V> { + if idx < N { + self.data[idx].as_ref() + } else { + None + } + } + pub fn find_empty(&self) -> Option { for (i, slot) in self.data.iter().enumerate() { if slot.is_none() { @@ -237,7 +245,7 @@ impl Vec { /// `additional` - The additional space to reserve. /// /// Returns `Ok(())` if the space was reserved, otherwise `Err(KernelError::OutOfMemory)`. - pub fn reserve(&mut self, additional: usize) -> Result<(), KernelError> { + pub fn reserve(&mut self, additional: usize) -> Result<()> { let len_extra = self.extra.len(); // Check if we have enough space in the inline storage. @@ -250,7 +258,7 @@ impl Vec { let mut new_extra = Box::new_slice_uninit(grow)?; // Check that the new extra storage has the requested length. - BUG_ON!(new_extra.len() != grow); + bug_on!(new_extra.len() != grow); // Copy the old extra storage into the new one. new_extra[..len_extra].copy_from_slice(&self.extra); @@ -265,7 +273,7 @@ impl Vec { /// `total_capacity` - The total space to be reserved. /// /// Returns `Ok(())` if the space was reserved, otherwise `Err(KernelError::OutOfMemory)`. - pub fn reserve_total_capacity(&mut self, total_capacity: usize) -> Result<(), KernelError> { + pub fn reserve_total_capacity(&mut self, total_capacity: usize) -> Result<()> { // Check if we already have enough space if self.capacity() >= total_capacity { return Ok(()); @@ -276,7 +284,7 @@ impl Vec { let mut new_extra = Box::new_slice_uninit(new_out_of_line_cap)?; // Check that the new extra storage has the requested length. - BUG_ON!(new_extra.len() != new_out_of_line_cap); + bug_on!(new_extra.len() != new_out_of_line_cap); let curr_out_of_line_size = self.extra.len(); // Copy the old extra storage into the new one. @@ -293,7 +301,7 @@ impl Vec { /// `value` - The value to initialize the elements in the Vec with. /// /// Returns the new Vec or `Err(KernelError::OutOfMemory)` if the allocation failed. - pub fn new_init(length: usize, value: T) -> Result { + pub fn new_init(length: usize, value: T) -> Result { let mut vec = Self::new(); // Check if we can fit all elements in the inline storage. @@ -329,7 +337,7 @@ impl Vec { /// `value` - The value to push. /// /// Returns `Ok(())` if the value was pushed, otherwise `Err(KernelError::OutOfMemory)`. - pub fn push(&mut self, value: T) -> Result<(), KernelError> { + pub fn push(&mut self, value: T) -> Result<()> { // Check if we have enough space in the inline storage. if self.len < N { // Push the value into the inline storage. @@ -350,7 +358,7 @@ impl Vec { let grow = (len_extra + 1) * 2; let mut new_extra = Box::new_slice_uninit(grow)?; - BUG_ON!(new_extra.len() != grow); + bug_on!(new_extra.len() != grow); // Copy the old extra storage into the new one. new_extra[..len_extra].copy_from_slice(&self.extra); diff --git a/src/types/boxed.rs b/src/types/boxed.rs index 3e2277a..0f23e56 100644 --- a/src/types/boxed.rs +++ b/src/types/boxed.rs @@ -1,7 +1,7 @@ //! This module provides a simple heap-allocated memory block for in-kernel use. -use crate::mem; -use crate::utils::KernelError; +use crate::{error::Result, mem}; + use core::{ mem::{MaybeUninit, forget}, ops::{Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeTo}, @@ -23,7 +23,7 @@ impl Box<[T]> { /// `len` - The length of the slice. /// /// Returns a new heap-allocated slice with the given length or an error if the allocation failed. - pub fn new_slice_zeroed(len: usize) -> Result { + pub fn new_slice_zeroed(len: usize) -> Result { if len == 0 { return Ok(Self::new_slice_empty()); } @@ -34,7 +34,7 @@ impl Box<[T]> { ptr: unsafe { NonNull::new_unchecked(ptr) }, }) } else { - Err(KernelError::OutOfMemory) + Err(kerr!(OutOfMemory)) } } @@ -53,7 +53,7 @@ impl Box<[T]> { /// `len` - The length of the slice. /// /// Returns a new heap-allocated slice with the given length or an error if the allocation failed. - pub fn new_slice_uninit(len: usize) -> Result]>, KernelError> { + pub fn new_slice_uninit(len: usize) -> Result]>> { if let Some(ptr) = mem::malloc( size_of::>() * len, align_of::>(), @@ -63,7 +63,7 @@ impl Box<[T]> { ptr: unsafe { NonNull::new_unchecked(ptr) }, }) } else { - Err(KernelError::OutOfMemory) + Err(kerr!(OutOfMemory)) } } } diff --git a/src/types/heap.rs b/src/types/heap.rs index b3b0af3..aa0b579 100644 --- a/src/types/heap.rs +++ b/src/types/heap.rs @@ -1,7 +1,8 @@ //! This module provides a binary heap implementation. +use crate::error::Result; + use super::array::Vec; -use crate::utils::KernelError; /// An array-based binary heap, with N elements stored inline. #[derive(Debug)] @@ -20,7 +21,7 @@ impl BinaryHeap { /// `value` - The value to push onto the binary heap. /// /// Returns `Ok(())` if the value was pushed onto the binary heap, or an error if the heap cannot be extended (e.g. OOM). - pub fn push(&mut self, value: T) -> Result<(), KernelError> { + pub fn push(&mut self, value: T) -> Result<()> { self.vec.push(value)?; self.sift_up(self.len() - 1); Ok(()) diff --git a/src/types/list.rs b/src/types/list.rs index d3c9779..bb64980 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -188,9 +188,23 @@ impl List { where >::Output: Linkable, { - let node = storage.get_mut(id).ok_or(())?; - node.links_mut().prev = None; - node.links_mut().next = None; + let linked = { + let node = storage.get(id).ok_or(())?; + let links = node.links(); + self.head == Some(id) + || self.tail == Some(id) + || links.prev.is_some() + || links.next.is_some() + }; + + if linked { + self.remove(id, storage)?; + } else { + let node = storage.get_mut(id).ok_or(())?; + node.links_mut().prev = None; + node.links_mut().next = None; + } + Ok(()) } } @@ -239,7 +253,7 @@ mod tests { fn storage() -> IndexMap { let mut map = IndexMap::new(); for i in 0..4 { - map.insert(&Id(i), Node::new()).unwrap(); + assert!(map.insert(&Id(i), Node::new()).is_ok()); } map } @@ -268,6 +282,36 @@ mod tests { assert_eq!(n1.links().prev, Some(Id(3))); } + #[test] + fn push_back_and_remove() { + let mut s = storage(); + let mut list = List::::new(); + + list.push_back(Id(1), &mut s).unwrap(); + list.remove(Id(1), &mut s); + + assert_eq!(list.head(), None); + assert_eq!(list.tail(), None); + assert_eq!(list.len(), 0); + } + + #[test] + fn push_back_same_id_reinserts() { + let mut s = storage(); + let mut list = List::::new(); + + list.push_back(Id(1), &mut s).unwrap(); + list.push_back(Id(1), &mut s).unwrap(); + + assert_eq!(list.head(), Some(Id(1))); + assert_eq!(list.tail(), Some(Id(1))); + assert_eq!(list.len(), 1); + + let n1 = s.get(Id(1)).unwrap(); + assert_eq!(n1.links().prev, None); + assert_eq!(n1.links().next, None); + } + #[test] fn pop_back_ordered() { let mut s = storage(); diff --git a/src/types/rbtree.rs b/src/types/rbtree.rs index 969df19..887e386 100644 --- a/src/types/rbtree.rs +++ b/src/types/rbtree.rs @@ -61,6 +61,19 @@ impl RbTree pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> where >::Output: Linkable + Compare,{ + let already_linked = { + let node = storage.get(id).ok_or(())?; + let links = node.links(); + self.root == Some(id) + || links.parent.is_some() + || links.left.is_some() + || links.right.is_some() + }; + + if already_linked { + self.remove(id, storage)?; + } + let mut last = None; { @@ -871,6 +884,24 @@ mod tests { validate_tree(&tree, &store, &keys); } + #[test] + fn reinsert_same_id_is_stable() { + let keys = vec![10, 5, 15]; + let mut store = NodeStore::new(&keys); + let mut tree = RbTree::new(); + + tree.insert(0, &mut store).unwrap(); + tree.insert(1, &mut store).unwrap(); + tree.insert(2, &mut store).unwrap(); + + // Reinsert existing node id. This should not create duplicate structural links. + tree.insert(1, &mut store).unwrap(); + + let mut expected = keys.clone(); + expected.sort(); + validate_tree(&tree, &store, &expected); + } + #[test] fn min_updates_on_insert_and_remove() { let keys = vec![10, 5, 15, 3, 7, 12, 18, 1, 6]; diff --git a/src/uapi.rs b/src/uapi.rs new file mode 100644 index 0000000..bfc9b49 --- /dev/null +++ b/src/uapi.rs @@ -0,0 +1,2 @@ +pub mod print; +pub mod sched; \ No newline at end of file diff --git a/src/uapi/print.rs b/src/uapi/print.rs new file mode 100644 index 0000000..d148107 --- /dev/null +++ b/src/uapi/print.rs @@ -0,0 +1,24 @@ +use core::fmt::{self, Write}; + +use hal::Machinelike; + +#[macro_export] +macro_rules! uprintln { + ($($arg:tt)*) => ({ + use core::fmt::Write; + use osiris::uapi::print::Printer; + + let mut printer = Printer; + printer.write_fmt(format_args!($($arg)*)).unwrap(); + printer.write_str("\n").unwrap(); + }); +} + +pub struct Printer; + +impl Write for Printer { + fn write_str(&mut self, s: &str) -> fmt::Result { + hal::Machine::print(s).map_err(|_| fmt::Error)?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs new file mode 100644 index 0000000..43df677 --- /dev/null +++ b/src/uapi/sched.rs @@ -0,0 +1,8 @@ + +pub fn sleep(until: u64) { + hal::asm::syscall!(1, (until >> 32) as u32, until as u32); +} + +pub fn sleep_for(duration: u64) { + hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32); +} \ No newline at end of file diff --git a/src/uspace.rs b/src/uspace.rs index d6799de..bc40be2 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -1,6 +1,6 @@ //! This module provides access to userspace structures and services. -use crate::sched; +use crate::{sched, time}; unsafe extern "C" { /// The entry point for the userspace application. @@ -11,12 +11,18 @@ extern "C" fn app_main_entry() { unsafe { app_main() } } -pub fn init_app() -> Result<(), crate::utils::KernelError> { +pub fn init_app() { let attrs = sched::thread::Attributes { entry: app_main_entry, fin: None, }; - let uid = sched::create_thread(sched::task::KERNEL_TASK, &attrs)?; - - sched::enqueue(uid) + sched::with(|sched| { + if let Ok(uid) = sched.create_thread(sched::task::KERNEL_TASK, &attrs) { + if sched.enqueue(time::tick(), uid).is_err() { + panic!("failed to enqueue init thread."); + } + } else { + panic!("failed to create init task."); + } + }) } diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 0f1bd71..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Utility functions and definitions for the kernel. -#![cfg_attr(feature = "nightly", feature(likely_unlikely))] - -use core::fmt::Debug; -use core::ptr::NonNull; -use core::mem::offset_of; - -/// These two definitions are copied from https://github.com/rust-lang/hashbrown -#[cfg(not(feature = "nightly"))] -#[allow(unused_imports)] -pub(crate) use core::convert::{identity as likely, identity as unlikely}; - -#[cfg(feature = "nightly")] -pub(crate) use core::hint::{likely, unlikely}; - -use hal::mem::PhysAddr; - - - -/// This is a macro that is used to panic when a bug is detected. -/// It is similar to the BUG() macro in the Linux kernel. Link: [https://www.kernel.org/]() -#[macro_export] -macro_rules! BUG { - () => { - panic!("BUG triggered at {}:{}", file!(), line!()); - }; - ($msg:expr) => { - panic!("BUG triggered: {} at {}:{}", $msg, file!(), line!()); - }; -} - -/// This is a macro that is used to panic when a condition is true. -/// It is similar to the BUG_ON() macro in the Linux kernel. Link: [https://www.kernel.org/]() -#[macro_export] -macro_rules! BUG_ON { - ($cond:expr) => {{ - let cond = $cond; - #[allow(unused_unsafe)] - if unsafe { $crate::utils::unlikely(cond) } { - BUG!(); - } - }}; - ($cond:expr, $msg:expr) => {{ - let cond = $cond; - #[allow(unused_unsafe)] - if unsafe { $crate::utils::unlikely(cond) } { - BUG!($msg); - } - }}; -} - -/// The error type that is returned when an error in the kernel occurs. -#[derive(PartialEq, Eq, Clone)] -pub enum KernelError { - /// The alignment is invalid. - InvalidAlign, - /// The kernel is out of memory. - OutOfMemory, - InvalidSize, - InvalidAddress(PhysAddr), - InvalidArgument, - HalError(hal::Error), - CustomError(&'static str), -} - -/// Debug msg implementation for KernelError. -impl Debug for KernelError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - KernelError::InvalidAlign => write!(f, "Invalid alignment"), - KernelError::OutOfMemory => write!(f, "Out of memory"), - KernelError::InvalidSize => write!(f, "Invalid size"), - KernelError::InvalidAddress(addr) => write!(f, "Invalid address ({})", addr), - KernelError::InvalidArgument => write!(f, "Invalid argument"), - KernelError::HalError(e) => write!(f, "{e} (in HAL)"), - KernelError::CustomError(msg) => write!(f, "{}", msg), - } - } -} - -impl From for KernelError { - fn from(err: hal::Error) -> Self { - KernelError::HalError(err) - } -} From 8816105e8280682b90325ed5727d4b66c2d24e26 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:17:06 +0000 Subject: [PATCH 11/28] cleanup and fixes. --- machine/arm/src/asm.rs | 117 +++++++++++++++----------- machine/testing/src/asm.rs | 10 +-- macros/src/lib.rs | 50 ------------ src/mem/alloc/bestfit.rs | 14 ++-- src/sched.rs | 93 ++++++++++++++------- src/syscalls/sched.rs | 6 +- src/types/list.rs | 49 +++++++---- src/types/rbtree.rs | 163 +++++++++++++++++++++++++------------ src/types/view.rs | 5 ++ src/uapi/sched.rs | 10 +-- 10 files changed, 302 insertions(+), 215 deletions(-) diff --git a/machine/arm/src/asm.rs b/machine/arm/src/asm.rs index b69e90e..1ba1ebb 100644 --- a/machine/arm/src/asm.rs +++ b/machine/arm/src/asm.rs @@ -20,59 +20,84 @@ pub use crate::__macro_nop as nop; #[macro_export] macro_rules! __macro_syscall { ($num:expr) => { - use core::arch::asm; - unsafe { - asm!("svc {num}", num = const $num, clobber_abi("C")); + { + use core::arch::asm; + let ret: isize; + unsafe { + asm!( + "svc {num}", + lateout("r0") ret, + num = const $num, + clobber_abi("C") + ); + } + ret } }; ($num:expr, $arg0:expr) => { - use core::arch::asm; - unsafe { - asm!( - "svc {num}", - in("r0") $arg0, - num = const $num, - clobber_abi("C") - ); + { + use core::arch::asm; + let ret: isize; + unsafe { + asm!( + "svc {num}", + inlateout("r0") $arg0 => ret, + num = const $num, + clobber_abi("C") + ); + } + ret } }; ($num:expr, $arg0:expr, $arg1:expr) => { - use core::arch::asm; - unsafe { - asm!( - "svc {num}", - in("r0") $arg0, - in("r1") $arg1, - num = const $num, - clobber_abi("C") - ); + { + use core::arch::asm; + let ret: isize; + unsafe { + asm!( + "svc {num}", + inlateout("r0") $arg0 => ret, + in("r1") $arg1, + num = const $num, + clobber_abi("C") + ); + } + ret } }; ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => { - use core::arch::asm; - unsafe { - asm!( - "svc {num}", - in("r0") $arg0, - in("r1") $arg1, - in("r2") $arg2, - num = const $num, - clobber_abi("C") - ); + { + use core::arch::asm; + let ret: isize; + unsafe { + asm!( + "svc {num}", + inlateout("r0") $arg0 => ret, + in("r1") $arg1, + in("r2") $arg2, + num = const $num, + clobber_abi("C") + ); + } + ret } }; ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => { - use core::arch::asm; - unsafe { - asm!( - "svc {num}", - in("r0") $arg0, - in("r1") $arg1, - in("r2") $arg2, - in("r3") $arg3, - num = const $num, - clobber_abi("C") - ); + { + use core::arch::asm; + let ret: isize; + unsafe { + asm!( + "svc {num}", + inlateout("r0") $arg0 => ret, + in("r1") $arg1, + in("r2") $arg2, + in("r3") $arg3, + num = const $num, + clobber_abi("C") + ); + } + ret } }; } @@ -80,11 +105,11 @@ macro_rules! __macro_syscall { #[cfg(feature = "host")] #[macro_export] macro_rules! __macro_syscall { - ($num:expr) => {{}}; - ($num:expr, $arg0:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {{}}; + ($num:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {{ 0isize }}; } pub use crate::__macro_syscall as syscall; diff --git a/machine/testing/src/asm.rs b/machine/testing/src/asm.rs index a303684..fbd7dd5 100644 --- a/machine/testing/src/asm.rs +++ b/machine/testing/src/asm.rs @@ -11,11 +11,11 @@ pub use crate::__macro_nop as nop; /// Macro for doing a system call. #[macro_export] macro_rules! __macro_syscall { - ($num:expr) => {{}}; - ($num:expr, $arg0:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => {{}}; - ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {{}}; + ($num:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => {{ 0isize }}; + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {{ 0isize }}; } pub use crate::__macro_syscall as syscall; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2387d03..b2084a6 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -42,56 +42,6 @@ pub fn app_main(input: proc_macro::TokenStream, item: proc_macro::TokenStream) - expanded.into() } -#[proc_macro_attribute] -pub fn service( - attr: proc_macro::TokenStream, - item: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - // This macro should be used to annotate a service struct. - let item = syn::parse_macro_input!(item as syn::ItemStruct); - - let service_name = item.ident.clone(); - - let mut mem_size: usize = 0; - let mut stack_size: usize = 0; - - let parser = syn::meta::parser(|meta| { - if meta.path.is_ident("mem_size") { - mem_size = meta.value()?.parse::()?.base10_parse()?; - Ok(()) - } else if meta.path.is_ident("stack_size") { - stack_size = meta.value()?.parse::()?.base10_parse()?; - Ok(()) - } else { - Err(meta.error("unknown attribute")) - } - }); - - parse_macro_input!(attr with parser); - - let mem_size_ident = format_ident!("TASK_{}_MEM_SIZE", service_name.to_string().to_uppercase()); - let stack_size_ident = format_ident!( - "TASK_{}_STACK_SIZE", - service_name.to_string().to_uppercase() - ); - - let expanded = quote::quote! { - const #mem_size_ident: usize = #mem_size; - const #stack_size_ident: usize = #stack_size; - #item - - impl #service_name { - pub fn task_desc() -> crate::sched::task::TaskDescriptor { - crate::sched::task::TaskDescriptor { - mem_size: #mem_size_ident, - } - } - } - }; - - expanded.into() -} - const SYSCALL_MAX_ARGS: usize = 4; fn is_return_type_register_sized_check( diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index 9c91fa1..00db5a7 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -24,7 +24,7 @@ pub struct BestFitAllocator { /// Implementation of the BestFitAllocator. impl BestFitAllocator { - pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; + pub const MIN_RANGE_SIZE: usize = size_of::() + Self::align_up() + 1; /// Creates a new BestFitAllocator. /// @@ -50,7 +50,11 @@ impl BestFitAllocator { // Check if the pointer is 128bit aligned. if !ptr.is_multiple_of(align_of::()) { return Err(kerr!(InvalidArgument)); - } + } + + if range.end.diff(range.start) < Self::MIN_RANGE_SIZE { + return Err(kerr!(InvalidArgument)); + } debug_assert!(range.end > range.start); debug_assert!(range.end.diff(range.start) > size_of::() + Self::align_up()); @@ -192,13 +196,13 @@ impl super::Allocator for BestFitAllocator { /// Returns the user pointer to the block if successful, otherwise an error. fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result> { // Check if the alignment is valid. - if align > align_of::() { - return Err(kerr!(InvalidArgument)); + if align == 0 || align > align_of::() { + return Err(kerr!(InvalidAlign)); } if let Some(request) = request { if !request.is_multiple_of(align) { - return Err(kerr!(InvalidArgument)); + return Err(kerr!(InvalidAlign)); } } diff --git a/src/sched.rs b/src/sched.rs index 39583d8..e3cd1e5 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -22,7 +22,7 @@ use crate::{ types::{ array::IndexMap, rbtree::RbTree, - traits::{Get, GetMut, Project}, + traits::{Get, GetMut}, view::ViewMut, }, }; @@ -85,11 +85,15 @@ impl Scheduler { if let Some(task_id) = kill { self.dequeue(current); self.current = None; - self.kill_task(task_id); + if self.kill_task(task_id).is_err() { + // Should not be possible. The thread exists, so the task must exist. + bug!("failed to kill task {}", task_id); + } } } } + /// Triggers a reschedule at *latest* when we hit timepoint `next`. fn schedule_resched(now: u64, next: u64) { let old = NEXT_TICK.load(Ordering::Acquire); @@ -107,7 +111,12 @@ impl Scheduler { let mut view = rt::ServerView::::new(&mut self.threads); self.rt_scheduler.enqueue(uid, now, &mut view); } else { - self.rr_scheduler.enqueue(uid, &mut self.threads)?; + if self.rr_scheduler.enqueue(uid, &mut self.threads).is_err() { + // This should not be possible. + // - Thread is in the thread list. + // - Thread is not linked into a different list. + bug!("failed to enqueue thread {} into RR scheduler.", uid); + } } reschedule(); Ok(()) @@ -115,19 +124,27 @@ impl Scheduler { fn do_wakeups(&mut self, now: u64) { while let Some(uid) = self.wakeup.min() { - { - let mut view = WaiterView::::new(&mut self.threads); + let mut done = false; + WaiterView::::with(&mut self.threads, |view| { let waiter = view.get(uid).expect("THIS IS A BUG!"); - if waiter.until() > now { Self::schedule_resched(now, waiter.until()); - break; + done = true; + return; + } + + if let Err(_) = self.wakeup.remove(uid, view) { + bug!("failed to remove thread {} from wakeup tree.", uid); } + }); - self.wakeup.remove(uid, &mut view); + if done { + break; } - self.enqueue(now, uid); + if self.enqueue(now, uid).is_err() { + bug!("failed to enqueue thread {} after wakeup.", uid); + } } } @@ -136,28 +153,35 @@ impl Scheduler { self.last_tick = now; if let Some(old) = self.current { - let mut view = rt::ServerView::::new(&mut self.threads); - self.rt_scheduler.put(old, dt, &mut view); + rt::ServerView::::with(&mut self.threads, |view| { + self.rt_scheduler.put(old, dt, view); + }); self.rr_scheduler.put(old, dt); } self.do_wakeups(now); - let mut view = rt::ServerView::::new(&mut self.threads); + let pick = + rt::ServerView::::with(&mut self.threads, |view| self.rt_scheduler.pick(now, view)); + let pick = pick.or_else(|| self.rr_scheduler.pick(&mut self.threads)); + let (new, budget) = pick.unwrap_or((thread::IDLE_THREAD, 1000)); - let (new, budget) = self - .rt_scheduler - .pick(now, &mut view) - .or_else(|| self.rr_scheduler.pick(&mut self.threads)) - .unwrap_or((thread::IDLE_THREAD, 1000)); + // At this point, the task/thread must exist. Everything else is a bug. + let (ctx, task_id) = if let Some(thread) = self.threads.get(new) { + (thread.ctx(), thread.task_id()) + } else { + bug!("failed to pick thread {}. Does not exist.", new); + }; - let ctx = self.threads.get(new)?.ctx(); - let task = self.tasks.get_mut(self.threads.get(new)?.task_id())?; + let task = if let Some(task) = self.tasks.get_mut(task_id) { + task + } else { + bug!("failed to get task {}. Does not exist.", task_id); + }; + // We don't need to resched if the thread has budget. self.current = Some(new); - let next = now.saturating_add(budget); - - Self::schedule_resched(now, next); + Self::schedule_resched(now, now.saturating_add(budget)); Some((ctx, task)) } @@ -170,6 +194,7 @@ impl Scheduler { if let Some(thread) = self.threads.get_mut(uid) { thread.set_waiter(Some(Waiter::new(until, uid))); } else { + // This should not be possible. The thread must exist since it's the current thread. bug!( "failed to put current thread {} to sleep. Does not exist.", uid @@ -181,27 +206,33 @@ impl Scheduler { .insert(uid, &mut WaiterView::::new(&mut self.threads)) .is_err() { + // This should not be possible. The thread exists. bug!("failed to insert thread {} into wakeup tree.", uid); } - - self.dequeue(uid); reschedule(); Ok(()) } pub fn kick(&mut self, uid: thread::UId) -> Result<()> { - let thread = self.threads.get_mut(uid).ok_or(kerr!(InvalidArgument))?; - if let Some(waiter) = Project::::project_mut(thread) { - waiter.set_until(0); - } - Ok(()) + WaiterView::::with(&mut self.threads, |view| { + self.wakeup.remove(uid, view)?; + let thread = view.get_mut(uid).unwrap_or_else(|| { + bug!("failed to get thread {} from wakeup tree.", uid); + }); + thread.set_until(0); + self.wakeup.insert(uid, view).unwrap_or_else(|_| { + bug!("failed to re-insert thread {} into wakeup tree.", uid); + }); + Ok(()) + }) } pub fn dequeue(&mut self, uid: thread::UId) { - let mut view = rt::ServerView::::new(&mut self.threads); - self.rt_scheduler.dequeue(uid, &mut view); + rt::ServerView::::with(&mut self.threads, |view| { + self.rt_scheduler.dequeue(uid, view); + }); self.rr_scheduler.dequeue(uid, &mut self.threads); } diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index 38688dd..2b7d434 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -10,7 +10,9 @@ use crate::{sched, time}; fn sleep(until_hi: u32, until_lo: u32) -> c_int { let until = ((until_hi as u64) << 32) | (until_lo as u64); sched::with(|sched| { - sched.sleep_until(until, time::tick()); + if sched.sleep_until(until, time::tick()).is_err() { + bug!("no current thread set."); + } }); 0 } @@ -21,7 +23,7 @@ fn sleep_for(duration_hi: u32, duration_lo: u32) -> c_int { sched::with(|sched| { let now = time::tick(); if sched.sleep_until(now + duration, now).is_err() { - panic!("failed to sleep for duration: {duration}"); + bug!("no current thread set."); } }); 0 diff --git a/src/types/list.rs b/src/types/list.rs index bb64980..7156d29 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -1,5 +1,7 @@ use core::marker::PhantomData; +use crate::error::Result; + use super::traits::{Get, GetMut}; #[allow(dead_code)] @@ -62,7 +64,7 @@ impl List { self.len == 0 } - pub fn push_front + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + pub fn push_front + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable, { @@ -71,7 +73,9 @@ impl List { match self.head { Some(old_head) => { let (new_node, old_head_node) = storage.get2_mut(id, old_head); - let (new_node, old_head_node) = (new_node.ok_or(())?, old_head_node.ok_or(())?); + let (new_node, old_head_node) = (new_node.ok_or(kerr!(NotFound))?, old_head_node.unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + })); new_node.links_mut().prev = None; new_node.links_mut().next = Some(old_head); @@ -79,7 +83,7 @@ impl List { old_head_node.links_mut().prev = Some(id); } None => { - let new_node = storage.get_mut(id).ok_or(())?; + let new_node = storage.get_mut(id).ok_or(kerr!(NotFound))?; new_node.links_mut().prev = None; new_node.links_mut().next = None; self.tail = Some(id); @@ -91,7 +95,10 @@ impl List { Ok(()) } - pub fn push_back + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + /// Pushes `id` to the back of the list. If `id` is already in the list, it is moved to the back. + /// + /// Errors if `id` does not exist in `storage` or if the node corresponding to `id` is linked but not in the list. + pub fn push_back + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable, { @@ -100,7 +107,9 @@ impl List { match self.tail { Some(old_tail) => { let (new_node, old_tail_node) = storage.get2_mut(id, old_tail); - let (new_node, old_tail_node) = (new_node.ok_or(())?, old_tail_node.ok_or(())?); + let (new_node, old_tail_node) = (new_node.ok_or(kerr!(NotFound))?, old_tail_node.unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + })); new_node.links_mut().next = None; new_node.links_mut().prev = Some(old_tail); @@ -108,7 +117,7 @@ impl List { old_tail_node.links_mut().next = Some(id); } None => { - let new_node = storage.get_mut(id).ok_or(())?; + let new_node = storage.get_mut(id).ok_or(kerr!(NotFound))?; new_node.links_mut().next = None; new_node.links_mut().prev = None; self.head = Some(id); @@ -120,7 +129,7 @@ impl List { Ok(()) } - pub fn pop_front + GetMut>(&mut self, storage: &mut S) -> Result, ()> + pub fn pop_front + GetMut>(&mut self, storage: &mut S) -> Result> where >::Output: Linkable, { @@ -132,7 +141,7 @@ impl List { Ok(Some(id)) } - pub fn pop_back + GetMut>(&mut self, storage: &mut S) -> Result, ()> + pub fn pop_back + GetMut>(&mut self, storage: &mut S) -> Result> where >::Output: Linkable, { @@ -144,12 +153,13 @@ impl List { Ok(Some(id)) } - pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + /// Removes `id` from the list. Errors if `id` does not exist in `storage` or if the node corresponding to `id` is not linked. + pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable, { let (prev, next, linked) = { - let node = storage.get(id).ok_or(())?; + let node = storage.get(id).ok_or(kerr!(NotFound))?; let links = node.links(); let linked = self.head == Some(id) || self.tail == Some(id) @@ -159,24 +169,28 @@ impl List { }; if !linked { - return Err(()); + return Err(kerr!(NotFound)); } if let Some(prev_id) = prev { - let prev_node = storage.get_mut(prev_id).ok_or(())?; + let prev_node = storage.get_mut(prev_id).unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + }); prev_node.links_mut().next = next; } else { self.head = next; } if let Some(next_id) = next { - let next_node = storage.get_mut(next_id).ok_or(())?; + let next_node = storage.get_mut(next_id).unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + }); next_node.links_mut().prev = prev; } else { self.tail = prev; } - let node = storage.get_mut(id).ok_or(())?; + let node = storage.get_mut(id).ok_or(kerr!(NotFound))?; node.links_mut().prev = None; node.links_mut().next = None; @@ -184,12 +198,13 @@ impl List { Ok(()) } - fn detach_links + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + /// Detaches `id` from any list it is currently in. If `id` is not in any list but is linked, the links are cleared. + fn detach_links + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable, { let linked = { - let node = storage.get(id).ok_or(())?; + let node = storage.get(id).ok_or(kerr!(NotFound))?; let links = node.links(); self.head == Some(id) || self.tail == Some(id) @@ -200,7 +215,7 @@ impl List { if linked { self.remove(id, storage)?; } else { - let node = storage.get_mut(id).ok_or(())?; + let node = storage.get_mut(id).ok_or(kerr!(NotFound))?; node.links_mut().prev = None; node.links_mut().next = None; } diff --git a/src/types/rbtree.rs b/src/types/rbtree.rs index 887e386..842ffe2 100644 --- a/src/types/rbtree.rs +++ b/src/types/rbtree.rs @@ -1,5 +1,7 @@ use core::{marker::PhantomData}; +use crate::error::Result; + use super::traits::{Get, GetMut}; #[allow(dead_code)] @@ -59,10 +61,11 @@ impl RbTree } } - pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + /// Inserts `id` into the tree. If `id` already exists in the tree, it is first removed and then re-inserted. Errors if `id` does not exist in `storage`. + pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare,{ let already_linked = { - let node = storage.get(id).ok_or(())?; + let node = storage.get(id).ok_or(kerr!(NotFound))?; let links = node.links(); self.root == Some(id) || links.parent.is_some() @@ -77,12 +80,14 @@ impl RbTree let mut last = None; { - let node = storage.get(id).ok_or(())?; + let node = storage.get(id).ok_or(kerr!(NotFound))?; let mut current = self.root; while let Some(current_id) = current { last = current; - let current_node = storage.get(current_id).ok_or(())?; + let current_node = storage.get(current_id).unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); let go_left = node.cmp(current_node) == core::cmp::Ordering::Less; current = if go_left { @@ -94,7 +99,7 @@ impl RbTree } { - let node = storage.get_mut(id).ok_or(())?.links_mut(); + let node = storage.get_mut(id).ok_or(kerr!(NotFound))?.links_mut(); node.parent = last; node.left = None; node.right = None; @@ -115,8 +120,10 @@ impl RbTree } if let Some(min_id) = self.min { - let node = storage.get(id).ok_or(())?; - let min_node = storage.get(min_id).ok_or(())?; + let node = storage.get(id).ok_or(kerr!(NotFound))?; + let min_node = storage.get(min_id).unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); if node.cmp(min_node) == core::cmp::Ordering::Less { self.min = Some(id); } @@ -127,18 +134,27 @@ impl RbTree self.insert_fixup(id, storage) } - pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<(), ()> + pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare { - let (node_left, node_right, node_parent, node_is_red) = { - let node = storage.get(id).ok_or(())?; + let (node_left, node_right, node_parent, node_is_red, linked) = { + let node = storage.get(id).ok_or(kerr!(NotFound))?; + let links = node.links(); ( - node.links().left, - node.links().right, - node.links().parent, - matches!(node.links().color, Color::Red), + links.left, + links.right, + links.parent, + matches!(links.color, Color::Red), + self.root == Some(id) + || links.parent.is_some() + || links.left.is_some() + || links.right.is_some(), ) }; + if !linked { + return Err(kerr!(NotFound)); + } + let mut succ_was_red = node_is_red; let child: Option; let child_parent: Option; @@ -154,7 +170,9 @@ impl RbTree self.transplant(id, node_left, storage)?; } else { - let right_id = node_right.ok_or(())?; + let right_id = node_right.unwrap_or_else(|| { + bug!("node's right child is None, but it is not None according to previous get."); + }); let succ = self.minimum(right_id, storage)?; let succ_right = storage.get(succ).and_then(|n| n.links().right); let succ_parent = storage.get(succ).and_then(|n| n.links().parent); @@ -173,7 +191,7 @@ impl RbTree succ_node.links_mut().right = Some(right_id); right_node.links_mut().parent = Some(succ); } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } child_parent = succ_parent; @@ -181,13 +199,15 @@ impl RbTree self.transplant(id, Some(succ), storage)?; - let left_id = node_left.ok_or(())?; + let left_id = node_left.unwrap_or_else(|| { + bug!("node's left child is None, but it is not None according to previous get."); + }); if let (Some(succ_node), Some(left_node)) = storage.get2_mut(succ, left_id) { succ_node.links_mut().left = Some(left_id); left_node.links_mut().parent = Some(succ); } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } if let Some(succ_node) = storage.get_mut(succ) { @@ -197,7 +217,7 @@ impl RbTree Color::Black }; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } } @@ -205,6 +225,17 @@ impl RbTree self.delete_fixup(child, child_parent, storage)?; } + // Fully detach the removed node so stale links cannot be observed on reinsertion. + if let Some(node) = storage.get_mut(id) { + let links = node.links_mut(); + links.parent = None; + links.left = None; + links.right = None; + links.color = Color::Red; + } else { + bug!("node linked from tree does not exist in storage."); + } + if self.min == Some(id) { self.min = match self.root { Some(root_id) => Some(self.minimum(root_id, storage)?), @@ -219,7 +250,7 @@ impl RbTree self.min } - fn insert_fixup + GetMut>(&mut self, mut id: T, storage: &mut S) -> Result<(), ()> + fn insert_fixup + GetMut>(&mut self, mut id: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare, { while let Some(parent) = storage.get(id).and_then(|n| n.links().parent) && storage @@ -229,7 +260,9 @@ impl RbTree let grandparent = storage .get(parent) .and_then(|n| n.links().parent) - .ok_or(())?; + .unwrap_or_else(|| { + bug!("node linked from tree does not have a parent."); + }); // Is left child node if storage @@ -264,11 +297,13 @@ impl RbTree id = old_parent; } - let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(())?; + let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(kerr!(NotFound))?; let grandparent = storage .get(parent) .and_then(|n| n.links().parent) - .ok_or(())?; + .unwrap_or_else(|| { + bug!("node linked from tree does not have a parent."); + }); if let (Some(parent_node), Some(grandparent_node)) = storage.get2_mut(parent, grandparent) @@ -308,11 +343,13 @@ impl RbTree id = old_parent; } - let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(())?; + let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(kerr!(NotFound))?; let grandparent = storage .get(parent) .and_then(|n| n.links().parent) - .ok_or(())?; + .unwrap_or_else(|| { + bug!("node linked from tree does not have a parent."); + }); if let (Some(parent_node), Some(grandparent_node)) = storage.get2_mut(parent, grandparent) @@ -340,7 +377,7 @@ impl RbTree mut id: Option, mut parent: Option, storage: &mut S, - ) -> Result<(), ()> + ) -> Result<()> where >::Output: Linkable + Compare, { let is_red = |node_id: Option, storage: &S| -> bool { node_id @@ -351,7 +388,9 @@ impl RbTree let is_black = |node_id: Option, storage: &S| -> bool { !is_red(node_id, storage) }; while id != self.root && is_black(id, storage) { - let parent_id = parent.ok_or(())?; + let parent_id = parent.unwrap_or_else(|| { + bug!("node linked from tree does not have a parent."); + }); let is_left_child = storage .get(parent_id) @@ -361,20 +400,24 @@ impl RbTree let mut sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); if is_red(sibling_opt, storage) { - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); // Color sibling node black and parent node red, rotate if let (Some(sib), Some(par)) = storage.get2_mut(sibling_id, parent_id) { sib.links_mut().color = Color::Black; par.links_mut().color = Color::Red; } else { - return Err(()); + return Err(kerr!(NotFound)); } self.rotate_left(parent_id, sibling_id, storage)?; sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); } // Sibling node is black - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); let sib_left = storage.get(sibling_id).and_then(|n| n.links().left); let sib_right = storage.get(sibling_id).and_then(|n| n.links().right); @@ -383,26 +426,30 @@ impl RbTree if let Some(sib) = storage.get_mut(sibling_id) { sib.links_mut().color = Color::Red; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } id = Some(parent_id); parent = storage.get(parent_id).and_then(|n| n.links().parent); } else { // Sibling's left node is red if is_black(sib_right, storage) { - let sib_left_id = sib_left.ok_or(())?; + let sib_left_id = sib_left.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); if let (Some(sib), Some(left)) = storage.get2_mut(sibling_id, sib_left_id) { sib.links_mut().color = Color::Red; left.links_mut().color = Color::Black; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } self.rotate_right(sibling_id, sib_left_id, storage)?; sibling_opt = storage.get(parent_id).and_then(|n| n.links().right); } // Sibling's right child node is red - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); let parent_is_red = storage .get(parent_id) .map_or(false, |n| matches!(n.links().color, Color::Red)); @@ -433,19 +480,23 @@ impl RbTree let mut sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); if is_red(sibling_opt, storage) { - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); if let (Some(sib), Some(par)) = storage.get2_mut(sibling_id, parent_id) { sib.links_mut().color = Color::Black; par.links_mut().color = Color::Red; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } self.rotate_right(parent_id, sibling_id, storage)?; sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); } // Sibling node is black - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); let sib_left = storage.get(sibling_id).and_then(|n| n.links().left); let sib_right = storage.get(sibling_id).and_then(|n| n.links().right); @@ -453,27 +504,31 @@ impl RbTree if let Some(sib) = storage.get_mut(sibling_id) { sib.links_mut().color = Color::Red; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } id = Some(parent_id); parent = storage.get(parent_id).and_then(|n| n.links().parent); } else { // Sibling's right node is red if is_black(sib_left, storage) { - let sib_right_id = sib_right.ok_or(())?; + let sib_right_id = sib_right.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); if let (Some(sib), Some(right)) = storage.get2_mut(sibling_id, sib_right_id) { sib.links_mut().color = Color::Red; right.links_mut().color = Color::Black; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } self.rotate_left(sibling_id, sib_right_id, storage)?; sibling_opt = storage.get(parent_id).and_then(|n| n.links().left); } // Sibling's left child node is red - let sibling_id = sibling_opt.ok_or(())?; + let sibling_id = sibling_opt.unwrap_or_else(|| { + bug!("node linked from tree does not exist in storage."); + }); let parent_is_red = storage .get(parent_id) .map_or(false, |n| matches!(n.links().color, Color::Red)); @@ -513,10 +568,10 @@ impl RbTree Ok(()) } - fn minimum>(&self, mut id: T, storage: &S) -> Result + fn minimum>(&self, mut id: T, storage: &S) -> Result where >::Output: Linkable + Compare, { loop { - let left = storage.get(id).ok_or(())?.links().left; + let left = storage.get(id).ok_or(kerr!(NotFound))?.links().left; match left { Some(left_id) => id = left_id, None => return Ok(id), @@ -524,7 +579,7 @@ impl RbTree } } - fn transplant + GetMut>(&mut self, u: T, v: Option, storage: &mut S) -> Result<(), ()> + fn transplant + GetMut>(&mut self, u: T, v: Option, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare, { let u_parent = storage.get(u).and_then(|n| n.links().parent); @@ -538,7 +593,7 @@ impl RbTree parent_node.links_mut().right = v; } } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } } } @@ -547,17 +602,17 @@ impl RbTree if let Some(v_node) = storage.get_mut(v_id) { v_node.links_mut().parent = u_parent; } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } } Ok(()) } - fn rotate_right + GetMut>(&mut self, pivot: T, left: T, storage: &mut S) -> Result<(), ()> + fn rotate_right + GetMut>(&mut self, pivot: T, left: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare, { if pivot == left { - return Err(()); + return Err(kerr!(NotFound)); } let (right, parent) = @@ -580,7 +635,7 @@ impl RbTree (old_right, old_parent) } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); }; if let Some(right_id) = right { @@ -599,7 +654,7 @@ impl RbTree parent_node.links_mut().right = Some(left); } } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } } } @@ -607,10 +662,10 @@ impl RbTree Ok(()) } - fn rotate_left + GetMut>(&mut self, pivot: T, right: T, storage: &mut S) -> Result<(), ()> + fn rotate_left + GetMut>(&mut self, pivot: T, right: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare, { if pivot == right { - return Err(()); + return Err(kerr!(NotFound)); } let (left, parent) = @@ -633,7 +688,7 @@ impl RbTree (old_left, old_parent) } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); }; if let Some(left_id) = left { @@ -652,7 +707,7 @@ impl RbTree parent_node.links_mut().right = Some(right); } } else { - return Err(()); + bug!("node linked from tree does not exist in storage."); } } } diff --git a/src/types/view.rs b/src/types/view.rs index c07de3a..de56096 100644 --- a/src/types/view.rs +++ b/src/types/view.rs @@ -23,6 +23,11 @@ where _proj: PhantomData, } } + + pub fn with R, R>(data: &'a mut S, f: F) -> R { + let mut view = Self::new(data); + f(&mut view) + } } impl<'a, K: ?Sized + ToIndex, P, S: GetMut> Get for ViewMut<'a, K, P, S> diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index 43df677..db6bddd 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -1,8 +1,8 @@ -pub fn sleep(until: u64) { - hal::asm::syscall!(1, (until >> 32) as u32, until as u32); +pub fn sleep(until: u64) -> isize { + hal::asm::syscall!(1, (until >> 32) as u32, until as u32) } - -pub fn sleep_for(duration: u64) { - hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32); + +pub fn sleep_for(duration: u64) -> isize { + hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) } \ No newline at end of file From f69c956cc83a4956f1ffd9083c2791c21054a00e Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:43:33 +0000 Subject: [PATCH 12/28] spawn thread syscall --- examples/hello-world/src/main.rs | 12 ++++++++++++ src/idle.rs | 2 +- src/sched.rs | 7 ++++++- src/sched/thread.rs | 4 ++++ src/syscalls/sched.rs | 19 +++++++++++++++++++ src/uapi/sched.rs | 6 ++++++ src/uspace.rs | 2 +- 7 files changed, 49 insertions(+), 3 deletions(-) diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 2702505..70500bb 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -3,10 +3,22 @@ use osiris::app_main; +extern "C" fn second_thread() { + osiris::uprintln!("Hello from the second thread!"); + + let mut tick = 0; + loop { + osiris::uprintln!("Second thread tick: {}", tick); + tick += 1; + osiris::uapi::sched::sleep_for(1500); + } +} + #[app_main] fn main() { osiris::uprintln!("Hello World!"); let mut tick = 0; + osiris::uapi::sched::spawn_thread(second_thread); loop { osiris::uprintln!("Tick: {}", tick); diff --git a/src/idle.rs b/src/idle.rs index 70633a7..479f407 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -9,7 +9,7 @@ extern "C" fn entry() { pub fn init() { let attrs = sched::thread::Attributes { entry, fin: None }; sched::with(|sched| { - if let Err(e) = sched.create_thread(sched::task::KERNEL_TASK, &attrs) { + if let Err(e) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { panic!("failed to create idle thread. Error: {}", e); } }); diff --git a/src/sched.rs b/src/sched.rs index e3cd1e5..e063b48 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -274,10 +274,15 @@ impl Scheduler { pub fn create_thread( &mut self, - task: task::UId, + task: Option, attrs: &thread::Attributes, ) -> Result { + let task = match task { + Some(t) => t, + None => self.current.ok_or(kerr!(InvalidArgument))?.owner() + }; let task = self.tasks.get_mut(task).ok_or(kerr!(InvalidArgument))?; + let thread = task.create_thread(self.id_gen, attrs)?; let uid = thread.uid(); diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 86af4aa..7cc628d 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -60,6 +60,10 @@ impl UId { self.tid } + pub fn as_usize(&self) -> usize { + self.uid + } + pub fn owner(&self) -> task::UId { self.tid.owner } diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index 2b7d434..b7b1ffd 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -29,3 +29,22 @@ fn sleep_for(duration_hi: u32, duration_lo: u32) -> c_int { 0 } +#[syscall_handler(num = 3)] +fn spawn_thread(func_ptr: usize) -> c_int { + sched::with(|sched| { + let attrs = sched::thread::Attributes { + entry: unsafe { core::mem::transmute(func_ptr) }, + fin: None, + }; + match sched.create_thread(None, &attrs) { + Ok(uid) => { + if sched.enqueue(time::tick(), uid).is_err() { + panic!("failed to enqueue thread."); + } + uid.as_usize() as c_int + } + Err(_) => -1, + } + }) +} + diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index db6bddd..5437a2b 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -1,3 +1,5 @@ +use hal::stack::EntryFn; + pub fn sleep(until: u64) -> isize { hal::asm::syscall!(1, (until >> 32) as u32, until as u32) @@ -5,4 +7,8 @@ pub fn sleep(until: u64) -> isize { pub fn sleep_for(duration: u64) -> isize { hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) +} + +pub fn spawn_thread(func_ptr: EntryFn) -> isize { + hal::asm::syscall!(3, func_ptr as u32) } \ No newline at end of file diff --git a/src/uspace.rs b/src/uspace.rs index bc40be2..2f11b34 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -17,7 +17,7 @@ pub fn init_app() { fin: None, }; sched::with(|sched| { - if let Ok(uid) = sched.create_thread(sched::task::KERNEL_TASK, &attrs) { + if let Ok(uid) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { if sched.enqueue(time::tick(), uid).is_err() { panic!("failed to enqueue init thread."); } From e0af649c6e830dffbccfe69bd8036bbe76530ae9 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:19:13 +0000 Subject: [PATCH 13/28] wip --- Cargo.lock | 75 ++++++++++++-- Cargo.toml | 3 + build.rs | 45 ++++----- examples/hello-world/Cargo.toml | 2 +- examples/hello-world/src/main.rs | 17 +++- machine/arm/Cargo.toml | 1 + machine/arm/src/crit.rs | 14 +++ machine/arm/src/excep.rs | 27 ++++++ machine/arm/src/lib.rs | 1 + macros/src/lib.rs | 161 +++---------------------------- macros/src/logging.rs | 24 +++++ macros/src/syscall.rs | 146 ++++++++++++++++++++++++++++ src/error.rs | 3 +- src/lib.rs | 5 +- src/macros.rs | 48 ++++++--- src/mem/alloc/bestfit.rs | 2 +- src/sched.rs | 52 +++++----- src/sched/task.rs | 50 ++++++---- src/sched/thread.rs | 50 +++++++--- src/sync/spinlock.rs | 4 +- src/syscalls.rs | 3 +- src/syscalls/sched.rs | 17 +++- src/types/array.rs | 46 +++++---- src/types/boxed.rs | 2 +- src/types/heap.rs | 2 +- src/types/list.rs | 6 +- src/types/queue.rs | 2 +- src/types/rbtree.rs | 9 +- src/types/traits.rs | 6 ++ src/uapi/sched.rs | 12 +++ 30 files changed, 542 insertions(+), 293 deletions(-) create mode 100644 machine/arm/src/crit.rs create mode 100644 macros/src/logging.rs create mode 100644 macros/src/syscall.rs diff --git a/Cargo.lock b/Cargo.lock index 0b14c67..8ebf8eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,7 +95,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -118,7 +118,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -132,6 +132,12 @@ dependencies = [ "syn", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -412,13 +418,19 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossterm" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags", + "bitflags 2.10.0", "crossterm_winapi", "libc", "mio 0.8.11", @@ -434,7 +446,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags", + "bitflags 2.10.0", "crossterm_winapi", "mio 1.1.1", "parking_lot", @@ -514,6 +526,48 @@ dependencies = [ "syn", ] +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.17", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -713,6 +767,7 @@ dependencies = [ "bindgen 0.72.1", "cbindgen", "cmake", + "critical-section", "hal-api", "serde_json", ] @@ -1081,9 +1136,11 @@ name = "osiris" version = "0.1.0" dependencies = [ "bindgen 0.69.5", - "bitflags", + "bitflags 2.10.0", "cbindgen", "cfg_aliases", + "defmt", + "defmt-rtt", "dtgen", "envparse", "hal-select", @@ -1257,7 +1314,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags", + "bitflags 2.10.0", "cassowary", "compact_str", "crossterm 0.28.1", @@ -1278,7 +1335,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.10.0", ] [[package]] @@ -1336,7 +1393,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1349,7 +1406,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", diff --git a/Cargo.toml b/Cargo.toml index bca123c..e451399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ hal = { package = "hal-select", path = "machine/select" } proc_macros = { package = "macros", path = "macros" } envparse = "0.1.0" bitflags = "2.10.0" +defmt = { version = "1.0", optional = true } +defmt-rtt = { version = "1.0", optional = true } [dev-dependencies] # This is a host-compatible HAL which will be used for running tests and verification on the host. @@ -37,6 +39,7 @@ default = [] nightly = [] no-atomic-cas = [] multi-core = [] +defmt = ["dep:defmt", "dep:defmt-rtt"] [build-dependencies] cbindgen = "0.28.0" diff --git a/build.rs b/build.rs index a07afab..1efb348 100644 --- a/build.rs +++ b/build.rs @@ -6,6 +6,7 @@ extern crate syn; extern crate walkdir; use cfg_aliases::cfg_aliases; +use quote::format_ident; use std::io::Write; use syn::{Attribute, LitInt}; use walkdir::WalkDir; @@ -15,8 +16,11 @@ extern crate cbindgen; fn main() { println!("cargo::rerun-if-changed=src"); println!("cargo::rerun-if-changed=build.rs"); + let out_dir = std::env::var("OUT_DIR").unwrap(); - generate_syscall_map("src/syscalls").expect("Failed to generate syscall map."); + if gen_syscall_match(Path::new("src/syscalls"), Path::new(&out_dir)).is_err() { + panic!("Failed to generate syscall match statement."); + } cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, @@ -203,27 +207,26 @@ fn get_arg_names(args: &str) -> String { }) } -fn generate_syscall_map>(root: P) -> Result<(), std::io::Error> { - let syscalls = collect_syscalls(root); - - let out_dir = std::env::var("OUT_DIR").unwrap(); - let out_path = Path::new(&out_dir).join("syscall_dispatcher.in"); - let mut file = File::create(out_path)?; - - writeln!(file, "// This file is @generated by build.rs. Do not edit!")?; - writeln!(file)?; - writeln!(file, "match number {{")?; +fn gen_syscall_match(root: &Path, out: &Path) -> Result<(), std::io::Error> { + let syscalls = find_syscalls(root); + let mut file = File::create(out.join("syscall_match.in"))?; - for (name, number) in &syscalls { - writeln!(file, " {number} => entry_{name}(args),")?; - } + let arms = syscalls.iter().map(|(name, number)| { + let entry = format_ident!("entry_{}", name); + quote::quote! { + #number => #entry(args), + } + }); - writeln!( - file, - " _ => panic!(\"Unknown syscall number: {{}}\", number)," - )?; - writeln!(file, "}}")?; + let syscall_match = quote::quote! { + // This match statement is @generated by build.rs. Do not edit. + match number { + #(#arms)* + _ => panic!("Unknown syscall number: {}", number), + } + }; + writeln!(file, "{syscall_match}")?; Ok(()) } @@ -263,9 +266,7 @@ fn is_syscall(attrs: &[Attribute], name: &str) -> Option { None } -type SyscallData = u16; - -fn collect_syscalls>(root: P) -> HashMap { +fn find_syscalls(root: &Path) -> HashMap { let mut syscalls = HashMap::new(); let mut numbers = HashMap::new(); diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 1bd3456..81645ee 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -osiris = { workspace = true } +osiris = { workspace = true, features = ["defmt"] } [build-dependencies] cfg_aliases = "0.2.1" diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 70500bb..baa7bb3 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -7,11 +7,24 @@ extern "C" fn second_thread() { osiris::uprintln!("Hello from the second thread!"); let mut tick = 0; - loop { + for i in 0..5 { osiris::uprintln!("Second thread tick: {}", tick); tick += 1; osiris::uapi::sched::sleep_for(1500); } + + osiris::uapi::sched::exit(0); + osiris::uprintln!("This will never be printed."); +} + +extern "C" fn generator_thread() { + + let mut cnt = 0; + loop { + osiris::uapi::sched::yield_thread(); + osiris::uprintln!("Number: {}", cnt); + cnt += 1; + } } #[app_main] @@ -19,7 +32,7 @@ fn main() { osiris::uprintln!("Hello World!"); let mut tick = 0; osiris::uapi::sched::spawn_thread(second_thread); - + osiris::uapi::sched::spawn_thread(generator_thread); loop { osiris::uprintln!("Tick: {}", tick); tick += 1; diff --git a/machine/arm/Cargo.toml b/machine/arm/Cargo.toml index 9e5a441..9fb4753 100644 --- a/machine/arm/Cargo.toml +++ b/machine/arm/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["rlib"] [dependencies] hal-api = { path = "../api" } +critical-section = { version = "1.2", features = ["restore-state-usize"] } [build-dependencies] cbindgen = "0.28.0" diff --git a/machine/arm/src/crit.rs b/machine/arm/src/crit.rs new file mode 100644 index 0000000..134f2f5 --- /dev/null +++ b/machine/arm/src/crit.rs @@ -0,0 +1,14 @@ +use critical_section::RawRestoreState; + +struct CriticalSection; +critical_section::set_impl!(CriticalSection); + +unsafe impl critical_section::Impl for CriticalSection { + unsafe fn acquire() -> RawRestoreState { + crate::asm::disable_irq_save() + } + + unsafe fn release(token: RawRestoreState) { + crate::asm::enable_irq_restr(token); + } +} \ No newline at end of file diff --git a/machine/arm/src/excep.rs b/machine/arm/src/excep.rs index 4e50bbc..ab51268 100644 --- a/machine/arm/src/excep.rs +++ b/machine/arm/src/excep.rs @@ -1,4 +1,5 @@ use core::fmt::Display; +use core::mem::align_of; #[repr(C)] pub struct ExcepStackFrame { @@ -43,6 +44,11 @@ impl Display for ExcepStackFrame { const BACKTRACE_MAX_FRAMES: usize = 20; +#[inline] +fn is_call_aligned(ptr: *const usize) -> bool { + (ptr as usize).is_multiple_of(align_of::()) +} + #[repr(C)] pub struct ExcepBacktrace { stack_frame: ExcepStackFrame, @@ -79,6 +85,11 @@ impl Display for ExcepBacktrace { writeln!(f, "0: 0x{:08x}", self.stack_frame.pc)?; } + if fp.is_null() || !is_call_aligned(fp) { + writeln!(f, "", fp as usize)?; + return writeln!(f); + } + for i in 1..BACKTRACE_MAX_FRAMES { // Read the return address from the stack. let ret_addr = unsafe { fp.add(1).read_volatile() }; @@ -89,6 +100,9 @@ impl Display for ExcepBacktrace { break; } + // Return addresses in Thumb mode carry bit0 = 1. + let ret_addr = ret_addr & !1; + // Print the return address. if let Some(symbol) = crate::debug::find_nearest_symbol(ret_addr) { writeln!(f, "{i}: {symbol} (0x{ret_addr:08x})")?; @@ -101,6 +115,19 @@ impl Display for ExcepBacktrace { break; } + let fp_addr = fp as usize; + let next_fp_addr = next_fp; + + if next_fp_addr <= fp_addr { + writeln!(f, "")?; + break; + } + + if !is_call_aligned(next_fp_addr as *const usize) { + writeln!(f, "")?; + break; + } + // Move to the next frame. fp = next_fp as *const usize; diff --git a/machine/arm/src/lib.rs b/machine/arm/src/lib.rs index 00fb103..46ba0b9 100644 --- a/machine/arm/src/lib.rs +++ b/machine/arm/src/lib.rs @@ -10,6 +10,7 @@ pub mod excep; pub mod panic; pub mod sched; +mod crit; mod print; mod bindings { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b2084a6..49f1b4a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,9 +1,8 @@ -use quote::{ToTokens, format_ident}; use syn::parse_macro_input; -use proc_macro2::TokenStream; - mod tree; +mod syscall; +mod logging; #[proc_macro_derive(TaggedLinks, attributes(rbtree, list))] pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -15,6 +14,16 @@ pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenS }.into() } +#[proc_macro_attribute] +pub fn fmt(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + match logging::derive_fmt(&input) { + Ok(tokens) => tokens, + Err(e) => e.to_compile_error(), + }.into() +} + #[proc_macro_attribute] pub fn app_main(input: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { let item = syn::parse_macro_input!(item as syn::ItemFn); @@ -42,74 +51,6 @@ pub fn app_main(input: proc_macro::TokenStream, item: proc_macro::TokenStream) - expanded.into() } -const SYSCALL_MAX_ARGS: usize = 4; - -fn is_return_type_register_sized_check( - item: &syn::ItemFn, -) -> Result { - let ret_ty = match &item.sig.output { - syn::ReturnType::Default => { - // no "-> Type" present - return Err(syn::Error::new_spanned( - &item.sig.output, - "syscall_handler: missing return type; expected a register‐sized type", - )); - } - syn::ReturnType::Type(_, ty) => (*ty).clone(), - }; - - Ok(quote::quote! { - const _: () = { - if core::mem::size_of::<#ret_ty>() > core::mem::size_of::() { - panic!("syscall_handler: the return type is bigger than usize. return type must fit in a register."); - } - }; - }) -} - -fn check_and_collect_argument_types(item: &syn::ItemFn) -> Result, syn::Error> { - let types: Vec> = item - .sig - .inputs - .iter() - .map(|arg| { - if let syn::FnArg::Typed(pat_type) = arg { - Ok((*pat_type.ty).clone()) - } else { - Err(syn::Error::new( - item.sig.ident.span(), - format!( - "argument {} is invalid. expected a typed argument.\n", - arg.to_token_stream() - ), - )) - } - }) - .collect(); - - let concat_errors: Vec<_> = types - .iter() - .filter_map(|arg0: &std::result::Result| Result::err(arg0.clone())) - .collect(); - - if !concat_errors.is_empty() { - return Err(syn::Error::new( - item.sig.ident.span(), - format!( - "syscall_handler: function {} has invalid arguments: {}", - item.sig.ident, - concat_errors - .iter() - .map(|e| e.to_string()) - .collect::>() - .join(", ") - ), - )); - } - - Ok(types.into_iter().map(Result::unwrap).collect()) -} - #[proc_macro_attribute] pub fn syscall_handler( attr: proc_macro::TokenStream, @@ -129,83 +70,7 @@ pub fn syscall_handler( parse_macro_input!(attr with parser); let item = syn::parse_macro_input!(item as syn::ItemFn); - syscall_handler_fn(&item).into() + syscall::syscall_handler_fn(&item).into() } -fn syscall_handler_fn(item: &syn::ItemFn) -> TokenStream { - let name = item.sig.ident.to_string().to_uppercase(); - let num_args = item.sig.inputs.len(); - - // Check if the function has a valid signature. So args <= 4 and return type is u32. - if num_args > SYSCALL_MAX_ARGS { - return syn::Error::new( - item.sig.ident.span(), - format!( - "syscall_handler: function {name} has too many arguments (max is {SYSCALL_MAX_ARGS})" - ), - ) - .to_compile_error(); - } - - let ret_check = match is_return_type_register_sized_check(item) { - Ok(check) => check, - Err(e) => return e.to_compile_error(), - }; - let types = match check_and_collect_argument_types(item) { - Ok(types) => { - if types.len() > SYSCALL_MAX_ARGS { - return syn::Error::new( - item.sig.ident.span(), - format!( - "syscall_handler: function {name} has too many arguments (max is {SYSCALL_MAX_ARGS})" - ), - ) - .to_compile_error(); - } - types - } - Err(e) => return e.to_compile_error(), - }; - - // Check if each argument type is valid and fits in a register. - let size_checks: Vec = types.iter().map(|ty| { - quote::quote! { - const _: () = { - if core::mem::size_of::<#ty>() > core::mem::size_of::() { - panic!("syscall_handler: an argument type is bigger than usize. arguments must fit in a register."); - } - }; - } - }).collect(); - - let unpack = types.iter().enumerate().map(|(i, ty)| { - quote::quote! { - unsafe { *(args.add(#i) as *const #ty) } - } - }); - - let wrapper_name = format_ident!("entry_{}", item.sig.ident.clone()); - let func_name = item.sig.ident.clone(); - - let call = quote::quote! { - #func_name( #(#unpack),* ) - }; - - let wrapper = quote::quote! { - #[unsafe(no_mangle)] - pub extern "C" fn #wrapper_name(svc_args: *const core::ffi::c_uint) -> core::ffi::c_int { - // This function needs to extract the arguments from the pointer and call the original function by passing the arguments as actual different parameters. - let args = unsafe { svc_args as *const usize }; - // Call the original function with the extracted arguments. - #call - } - }; - - quote::quote! { - #wrapper - #item - #ret_check - #(#size_checks)* - } -} diff --git a/macros/src/logging.rs b/macros/src/logging.rs new file mode 100644 index 0000000..9e857fe --- /dev/null +++ b/macros/src/logging.rs @@ -0,0 +1,24 @@ +use syn::{DeriveInput, ItemFn}; + +pub fn derive_fmt(input: &DeriveInput) -> syn::Result { + // Check if the env variable "OSIRIS_DEBUG_DEFMT" is set. If it is, generate a defmt::Format implementation. Otherwise, generate a Debug implementation. + if std::env::var("OSIRIS_DEBUG_DEFMT").is_ok() { + Ok(derive_fmt_defmt(input)) + } else { + Ok(derive_fmt_debug(input)) + } +} + +fn derive_fmt_defmt(input: &DeriveInput) -> proc_macro2::TokenStream { + quote::quote! { + #[derive(defmt::Format)] + #input + } +} + +fn derive_fmt_debug(input: &DeriveInput) -> proc_macro2::TokenStream { + quote::quote! { + #[derive(core::fmt::Debug)] + #input + } +} \ No newline at end of file diff --git a/macros/src/syscall.rs b/macros/src/syscall.rs new file mode 100644 index 0000000..45c071c --- /dev/null +++ b/macros/src/syscall.rs @@ -0,0 +1,146 @@ +use quote::{ToTokens, format_ident}; +use proc_macro2::TokenStream; + +pub const MAX_ARGS: usize = 4; + +pub fn valid_ret_type_check(item: &syn::ItemFn) -> Result { + let ret_ty = match &item.sig.output { + syn::ReturnType::Default => { + // no "-> Type" present + return Err(syn::Error::new_spanned( + &item.sig.output, + "syscall_handler: missing return type; expected a register‐sized type", + )); + } + syn::ReturnType::Type(_, ty) => (*ty).clone(), + }; + + Ok(quote::quote! { + const _: () = { + if core::mem::size_of::<#ret_ty>() > core::mem::size_of::() { + panic!("syscall_handler: the return type is bigger than usize. return type must fit in a register."); + } + }; + }) +} + +pub fn valid_arg_types_check(item: &syn::ItemFn) -> Result, syn::Error> { + let types: Vec> = item + .sig + .inputs + .iter() + .map(|arg| { + if let syn::FnArg::Typed(pat_type) = arg { + Ok((*pat_type.ty).clone()) + } else { + Err(syn::Error::new( + item.sig.ident.span(), + format!( + "argument {} is invalid. expected a typed argument.\n", + arg.to_token_stream() + ), + )) + } + }) + .collect(); + + let concat_errors: Vec<_> = types + .iter() + .filter_map(|arg0: &std::result::Result| Result::err(arg0.clone())) + .collect(); + + if !concat_errors.is_empty() { + return Err(syn::Error::new( + item.sig.ident.span(), + format!( + "syscall_handler: function {} has invalid arguments: {}", + item.sig.ident, + concat_errors + .iter() + .map(|e| e.to_string()) + .collect::>() + .join(", ") + ), + )); + } + + Ok(types.into_iter().map(Result::unwrap).collect()) +} + +pub fn syscall_handler_fn(item: &syn::ItemFn) -> TokenStream { + let name = item.sig.ident.to_string().to_uppercase(); + let num_args = item.sig.inputs.len(); + + // Check if the function has a valid signature. So args <= 4 and return type is u32. + if num_args > MAX_ARGS { + return syn::Error::new( + item.sig.ident.span(), + format!( + "syscall_handler: function {name} has too many arguments (max is {MAX_ARGS})", + ), + ) + .to_compile_error(); + } + + let ret_check = match valid_ret_type_check(item) { + Ok(check) => check, + Err(e) => return e.to_compile_error(), + }; + + let types = match valid_arg_types_check(item) { + Ok(types) => { + if types.len() > MAX_ARGS { + return syn::Error::new( + item.sig.ident.span(), + format!( + "syscall_handler: function {name} has too many arguments (max is {MAX_ARGS})", + ), + ) + .to_compile_error(); + } + types + } + Err(e) => return e.to_compile_error(), + }; + + // Check if each argument type is valid and fits in a register. + let size_checks: Vec = types.iter().map(|ty| { + quote::quote! { + const _: () = { + if core::mem::size_of::<#ty>() > core::mem::size_of::() { + panic!("syscall_handler: an argument type is bigger than usize. arguments must fit in a register."); + } + }; + } + }).collect(); + + let unpack = types.iter().enumerate().map(|(i, ty)| { + quote::quote! { + unsafe { *(args.add(#i) as *const #ty) } + } + }); + + let wrapper_name = format_ident!("entry_{}", item.sig.ident.clone()); + let func_name = item.sig.ident.clone(); + + let call = quote::quote! { + #func_name( #(#unpack),* ) + }; + + let wrapper = quote::quote! { + #[unsafe(no_mangle)] + pub extern "C" fn #wrapper_name(svc_args: *const core::ffi::c_uint) -> core::ffi::c_int { + // This function needs to extract the arguments from the pointer and call the original function by passing the arguments as actual different parameters. + let args = unsafe { svc_args as *const usize }; + // Call the original function with the extracted arguments. + #call + } + }; + + quote::quote! { + #wrapper + #item + #ret_check + #(#size_checks)* + } +} diff --git a/src/error.rs b/src/error.rs index f9edb31..cc48035 100644 --- a/src/error.rs +++ b/src/error.rs @@ -90,7 +90,8 @@ macro_rules! kerr { }; } -#[derive(Debug, Clone, PartialEq, Eq)] +#[proc_macros::fmt] +#[derive(Clone, PartialEq, Eq)] pub enum Kind { InvalidAlign, OutOfMemory, diff --git a/src/lib.rs b/src/lib.rs index 2ce97dd..1218892 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,12 +29,9 @@ pub use proc_macros::app_main; /// The kernel initialization function. /// -/// `boot_info` - The boot information. -/// /// # Safety /// /// This function must be called only once during the kernel startup. -/// The `boot_info` pointer must be valid and point to a properly initialized `BootInfo` structure. #[unsafe(no_mangle)] pub unsafe extern "C" fn kernel_init() -> ! { // Initialize basic hardware and the logging system. @@ -43,6 +40,8 @@ pub unsafe extern "C" fn kernel_init() -> ! { print::print_header(); + error!("Hello World!"); + // Initialize the memory allocator. let kaddr_space = mem::init_memory(); kprintln!("Memory initialized."); diff --git a/src/macros.rs b/src/macros.rs index 9d9d06f..0ddf819 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,27 +1,43 @@ //! Macros for kernel development. +use defmt_rtt as _; + + +#[macro_export] +macro_rules! debug { + ($fmt:literal $(, $arg:expr)* $(,)?) => { + #[cfg(feature = "defmt")] + defmt::debug!($fmt $(, $arg)*); + }; +} + +#[macro_export] +macro_rules! trace { + ($fmt:literal $(, $arg:expr)* $(,)?) => { + #[cfg(feature = "defmt")] + defmt::trace!($fmt $(, $arg)*); + }; +} -/// Creates a slice from the raw arguments. #[macro_export] -macro_rules! args_from_raw { - ($argc:expr, $argv:expr) => {{ - let argc = $argc; - let argv = $argv; - - if argc == 0 || argv.is_null() { - &[] - } else { - unsafe { core::slice::from_raw_parts(argv, argc) } - } - }}; +macro_rules! info { + ($fmt:literal $(, $arg:expr)* $(,)?) => { + #[cfg(feature = "defmt")] + defmt::info!($fmt $(, $arg)*); + }; +} + +#[macro_export] +macro_rules! error { + ($fmt:literal $(, $arg:expr)* $(,)?) => { + #[cfg(feature = "defmt")] + defmt::error!($fmt $(, $arg)*); + }; } #[macro_export] macro_rules! kprint { ($($arg:tt)*) => ({ - use core::fmt::Write; - use $crate::print::Printer; - let mut printer = Printer; - printer.write_fmt(format_args!($($arg)*)).unwrap(); + }); } diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index 00db5a7..4732a85 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -16,7 +16,7 @@ struct BestFitMeta { /// That does mean, when we allocate a block, we try to find the smallest block that fits the requested size. /// Blocks are stored in a singly linked list. The important part is that the linked list is stored in-line with the memory blocks. /// This means that every block has a header that contains the size of the block and a pointer to the next block. -#[derive(Debug)] +#[proc_macros::fmt] pub struct BestFitAllocator { /// Head of the free block list. head: Option>, diff --git a/src/sched.rs b/src/sched.rs index e063b48..4001fa1 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -112,7 +112,7 @@ impl Scheduler { self.rt_scheduler.enqueue(uid, now, &mut view); } else { if self.rr_scheduler.enqueue(uid, &mut self.threads).is_err() { - // This should not be possible. + // This should not be possible. // - Thread is in the thread list. // - Thread is not linked into a different list. bug!("failed to enqueue thread {} into RR scheduler.", uid); @@ -245,30 +245,22 @@ impl Scheduler { } pub fn kill_task(&mut self, uid: task::UId) -> Result<()> { - let task_id = self.tasks.get(uid).ok_or(kerr!(InvalidArgument))?.id; - self.tasks.remove(&uid).ok_or(kerr!(InvalidArgument))?; - - let begin = match self.threads.next(None) { - Some(i) => i, - None => return Ok(()), - }; - let mut i = begin; - - while i != begin { - i = (i + 1) % N; + let task = self.tasks.get_mut(uid).ok_or(kerr!(InvalidArgument))?; - let mut id = None; - if let Some(thread) = self.threads.at_cont(i) { - if thread.task_id() == task_id { - id = Some(thread.uid()); - } - } + while let Some(id) = task.threads().head() { + // Borrow checker... + rt::ServerView::::with(&mut self.threads, |view| { + self.rt_scheduler.dequeue(id, view); + }); + self.rr_scheduler.dequeue(id, &mut self.threads); - if let Some(id) = id { - self.dequeue(id); + if task.threads_mut().remove(id, &mut self.threads).is_err() { + // This should not be possible. The thread ID is from the thread list of the task, so it must exist. + bug!("failed to remove thread {} from task {}.", id, uid); } } + self.tasks.remove(&uid).ok_or(kerr!(InvalidArgument))?; Ok(()) } @@ -279,18 +271,26 @@ impl Scheduler { ) -> Result { let task = match task { Some(t) => t, - None => self.current.ok_or(kerr!(InvalidArgument))?.owner() + None => self.current.ok_or(kerr!(InvalidArgument))?.owner(), }; let task = self.tasks.get_mut(task).ok_or(kerr!(InvalidArgument))?; - - let thread = task.create_thread(self.id_gen, attrs)?; - let uid = thread.uid(); - - self.threads.insert(&uid, thread)?; + let uid = task.create_thread(self.id_gen, attrs, &mut self.threads)?; self.id_gen += 1; Ok(uid) } + + pub fn kill_thread(&mut self, uid: Option) -> Result<()> { + let uid = uid.unwrap_or(self.current.ok_or(kerr!(InvalidArgument))?); + self.dequeue(uid); + self.threads.remove(&uid).ok_or(kerr!(InvalidArgument))?; + + if Some(uid) == self.current { + self.current = None; + reschedule(); + } + Ok(()) + } } pub fn with T>(f: F) -> T { diff --git a/src/sched/task.rs b/src/sched/task.rs index 52b0345..c1e094a 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -1,19 +1,20 @@ //! This module provides the basic task and thread structures for the scheduler. +use core::borrow::Borrow; use core::fmt::Display; use core::num::NonZero; -use core::borrow::Borrow; use envparse::parse_env; -use hal::{Stack}; +use hal::Stack; -use hal::stack::{Stacklike}; +use hal::stack::Stacklike; use crate::error::Result; -use crate::sched::thread; +use crate::sched::{GlobalScheduler, ThreadMap, thread}; +use crate::types::list; use crate::{mem, sched}; -use crate::mem::vmm::{AddressSpacelike}; -use crate::types::traits::ToIndex; +use crate::mem::vmm::AddressSpacelike; +use crate::types::traits::{Get, GetMut, ToIndex}; pub struct Defaults { pub stack_pages: usize, @@ -26,18 +27,15 @@ const DEFAULTS: Defaults = Defaults { pub const KERNEL_TASK: UId = UId { uid: 0 }; /// Id of a task. This is unique across all tasks. -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub struct UId { uid: usize, } impl UId { pub fn new(uid: usize) -> Option { - if uid == 0 { - None - } else { - Some(Self { uid }) - } + if uid == 0 { None } else { Some(Self { uid }) } } pub fn is_kernel(&self) -> bool { @@ -53,7 +51,7 @@ impl ToIndex for UId { impl Display for UId { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Task-{}", self.uid) + write!(f, "{}", self.uid) } } @@ -69,6 +67,8 @@ pub struct Task { tid_cntr: usize, /// Sets up the memory for the task. address_space: mem::vmm::AddressSpace, + /// The threads belonging to this task. + threads: list::List, } impl Task { @@ -84,6 +84,7 @@ impl Task { id, address_space, tid_cntr: 0, + threads: list::List::new(), }) } @@ -94,10 +95,7 @@ impl Task { sched::thread::Id::new(tid, self.id) } - fn allocate_stack( - &mut self, - attrs: &thread::Attributes, - ) -> Result { + fn allocate_stack(&mut self, attrs: &thread::Attributes) -> Result { let size = DEFAULTS.stack_pages * mem::pfa::PAGE_SIZE; let region = mem::vmm::Region::new( None, @@ -115,20 +113,32 @@ impl Task { }) } - pub fn create_thread( + pub fn create_thread( &mut self, uid: usize, attrs: &thread::Attributes, - ) -> Result { + storage: &mut ThreadMap, + ) -> Result { let stack = self.allocate_stack(attrs)?; let stack = unsafe { Stack::new(stack) }?; let tid = self.allocate_tid(); + let new = sched::thread::Thread::new(tid.get_uid(uid), stack); + storage.insert(&tid.get_uid(uid), new); + self.threads.push_back(tid.get_uid(uid), storage); - Ok(sched::thread::Thread::new(tid.get_uid(uid), stack)) + Ok(tid.get_uid(uid)) } pub fn tid_cntr(&self) -> usize { self.tid_cntr } + + pub fn threads_mut(&mut self) -> &mut list::List { + &mut self.threads + } + + pub fn threads(&self) -> &list::List { + &self.threads + } } diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 7cc628d..a83203d 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -19,7 +19,8 @@ pub const IDLE_THREAD: UId = UId { }; /// Id of a task. This is only unique within a Task. -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub struct Id { id: usize, owner: task::UId, @@ -45,7 +46,8 @@ impl Id { } /// Unique identifier for a thread. Build from TaskId and ThreadId. -#[derive(Clone, Copy, Debug)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] #[allow(dead_code)] pub struct UId { /// A globally unique identifier for the thread. @@ -65,7 +67,7 @@ impl UId { } pub fn owner(&self) -> task::UId { - self.tid.owner + self.tid.owner() } } @@ -103,14 +105,15 @@ impl ToIndex for UId { impl Display for UId { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "T{}-{}", self.tid.owner(), self.tid.as_usize()) + write!(f, "{}-{}", self.tid.owner(), self.tid.as_usize()) } } // ------------------------------------------------------------------------- /// The state of a thread. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, Eq)] #[allow(dead_code)] pub enum RunState { /// The thread is currently using the cpu. @@ -121,13 +124,15 @@ pub enum RunState { Waits, } -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] pub struct State { run_state: RunState, stack: Stack, } -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] #[derive(TaggedLinks)] pub struct RtServer { budget: u64, @@ -197,7 +202,8 @@ impl Compare for RtServer { } } -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] #[derive(TaggedLinks)] pub struct Waiter { /// The time when the Thread will be awakened. @@ -237,21 +243,29 @@ impl Compare for Waiter { } } -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] pub struct WakupTree; -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] pub struct RtTree; -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] pub struct RRList; +#[proc_macros::fmt] +#[derive(Clone, Copy)] +pub struct ThreadList; + pub struct Attributes { pub entry: EntryFn, pub fin: Option, } /// The struct representing a thread. -#[derive(Debug, Clone, Copy)] +#[proc_macros::fmt] +#[derive(Clone, Copy)] #[derive(TaggedLinks)] pub struct Thread { /// The current state of the thread. @@ -265,6 +279,9 @@ pub struct Thread { #[list(tag = RRList, idx = UId)] rr_links: list::Links, + + #[list(tag = ThreadList, idx = UId)] + thread_links: list::Links, } #[allow(dead_code)] @@ -284,6 +301,7 @@ impl Thread { rt_server: None, waiter: None, rr_links: list::Links::new(), + thread_links: list::Links::new(), } } @@ -318,7 +336,13 @@ impl Thread { } pub fn task_id(&self) -> task::UId { - self.uid.tid().owner + self.uid.tid().owner() + } +} + +impl PartialEq for Thread { + fn eq(&self, other: &Self) -> bool { + self.uid == other.uid } } diff --git a/src/sync/spinlock.rs b/src/sync/spinlock.rs index 7e1b2de..14178cf 100644 --- a/src/sync/spinlock.rs +++ b/src/sync/spinlock.rs @@ -6,7 +6,7 @@ use core::sync::atomic::AtomicBool; use core::sync::atomic::Ordering; /// A mutual exclusion primitive, facilitating busy-waiting. -#[derive(Debug)] +#[proc_macros::fmt] pub struct SpinLock { lock: AtomicBool, } @@ -56,7 +56,7 @@ impl SpinLock { } /// A guard that releases the SpinLock when dropped. -#[derive(Debug)] +#[proc_macros::fmt] pub struct SpinLockGuard<'a, T: ?Sized> { lock: &'a SpinLock, value: NonNull, diff --git a/src/syscalls.rs b/src/syscalls.rs index c573d2f..d692f26 100644 --- a/src/syscalls.rs +++ b/src/syscalls.rs @@ -11,7 +11,8 @@ use sched::*; #[unsafe(no_mangle)] pub extern "C" fn handle_syscall(number: usize, args: *const c_uint) -> c_int { + let number = number as u16; // All functions that are annotated with the #[syscall_handler(num = X)] macro are syscalls. // build.rs will generate a match statement that matches the syscall number to the function which is then included here. - include!(concat!(env!("OUT_DIR"), "/syscall_dispatcher.in")) + include!(concat!(env!("OUT_DIR"), "/syscall_match.in")) } diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index b7b1ffd..1972b31 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -39,7 +39,7 @@ fn spawn_thread(func_ptr: usize) -> c_int { match sched.create_thread(None, &attrs) { Ok(uid) => { if sched.enqueue(time::tick(), uid).is_err() { - panic!("failed to enqueue thread."); + bug!("failed to enqueue thread."); } uid.as_usize() as c_int } @@ -48,3 +48,18 @@ fn spawn_thread(func_ptr: usize) -> c_int { }) } +#[syscall_handler(num = 4)] +fn exit(code: usize) -> c_int { + sched::with(|sched| { + if sched.kill_thread(None).is_err() { + bug!("failed to terminate thread."); + } + }); + 0 +} + +#[syscall_handler(num = 5)] +fn kick_thread(uid: usize) -> c_int { + 0 +} + diff --git a/src/types/array.rs b/src/types/array.rs index 67a9cba..15eae5b 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -13,7 +13,7 @@ use core::{ }; /// This is a fixed-size map that can store up to N consecutive elements. -#[derive(Debug)] +#[proc_macros::fmt] pub struct IndexMap { data: [Option; N], @@ -113,7 +113,7 @@ impl IndexMap None } - pub fn at_cont(&self, idx: usize) -> Option<&V> { + pub fn raw_at(&self, idx: usize) -> Option<&V> { if idx < N { self.data[idx].as_ref() } else { @@ -121,6 +121,14 @@ impl IndexMap } } + pub fn raw_at_mut(&mut self, idx: usize) -> Option<&mut V> { + if idx < N { + self.data[idx].as_mut() + } else { + None + } + } + pub fn find_empty(&self) -> Option { for (i, slot) in self.data.iter().enumerate() { if slot.is_none() { @@ -137,14 +145,14 @@ impl Index for IndexMap type Output = V; fn index(&self, index: K) -> &Self::Output { - self.get(&index).unwrap() + self.get::(index).unwrap() } } impl IndexMut for IndexMap { fn index_mut(&mut self, index: K) -> &mut Self::Output { - self.get_mut(&index).unwrap() + self.get_mut::(index).unwrap() } } @@ -173,23 +181,23 @@ impl GetMut for IndexMap { } fn get2_mut>(&mut self, index1: Q, index2: Q) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { - let index1 = K::to_index(Some(index1.borrow())); - let index2 = K::to_index(Some(index2.borrow())); + let idx1 = K::to_index(Some(index1.borrow())); + let idx2 = K::to_index(Some(index2.borrow())); - if index1 == index2 { + if idx1 == idx2 { debug_assert!(false, "get2_mut called with identical indices"); return (None, None); } - let (left, right) = self.data.split_at_mut(index1.max(index2)); + let (left, right) = self.data.split_at_mut(idx1.max(idx2)); - if index1 < index2 { - let elem1 = left[index1].as_mut(); + if idx1 < idx2 { + let elem1 = left[idx1].as_mut(); let elem2 = right[0].as_mut(); (elem1, elem2) } else { let elem1 = right[0].as_mut(); - let elem2 = left[index2].as_mut(); + let elem2 = left[idx2].as_mut(); (elem1, elem2) } } @@ -200,18 +208,18 @@ impl GetMut for IndexMap { index2: Q, index3: Q, ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>) { - let index1 = K::to_index(Some(index1.borrow())); - let index2 = K::to_index(Some(index2.borrow())); - let index3 = K::to_index(Some(index3.borrow())); + let idx1 = K::to_index(Some(index1.borrow())); + let idx2 = K::to_index(Some(index2.borrow())); + let idx3 = K::to_index(Some(index3.borrow())); - if index1 == index2 || index1 == index3 || index2 == index3 { + if idx1 == idx2 || idx1 == idx3 || idx2 == idx3 { debug_assert!(false, "get3_mut called with identical indices"); return (None, None, None); } - let ptr1 = &mut self.data[index1] as *mut Option; - let ptr2 = &mut self.data[index2] as *mut Option; - let ptr3 = &mut self.data[index3] as *mut Option; + let ptr1 = &mut self.data[idx1] as *mut Option; + let ptr2 = &mut self.data[idx2] as *mut Option; + let ptr3 = &mut self.data[idx3] as *mut Option; // Safety: the elements at index1, index2 and index3 are nowhere else borrowed mutably by function contract. // And they are disjoint because of the check above. @@ -220,7 +228,7 @@ impl GetMut for IndexMap { } /// This is a vector that can store up to N elements inline and will allocate on the heap if more are needed. -#[derive(Debug)] +#[proc_macros::fmt] pub struct Vec { len: usize, data: [MaybeUninit; N], diff --git a/src/types/boxed.rs b/src/types/boxed.rs index 0f23e56..012245b 100644 --- a/src/types/boxed.rs +++ b/src/types/boxed.rs @@ -9,7 +9,7 @@ use core::{ }; /// A heap-allocated memory block. -#[derive(Debug)] +#[proc_macros::fmt] pub struct Box { /// Pointer to the heap-allocated memory. /// This is uniquely owned, so no covariance issues. diff --git a/src/types/heap.rs b/src/types/heap.rs index aa0b579..a25b1f8 100644 --- a/src/types/heap.rs +++ b/src/types/heap.rs @@ -5,7 +5,7 @@ use crate::error::Result; use super::array::Vec; /// An array-based binary heap, with N elements stored inline. -#[derive(Debug)] +#[proc_macros::fmt] pub struct BinaryHeap { vec: Vec, } diff --git a/src/types/list.rs b/src/types/list.rs index 7156d29..be84923 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -19,7 +19,8 @@ pub trait Linkable { } #[allow(dead_code)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct Links { prev: Option, next: Option, @@ -231,7 +232,8 @@ mod tests { use super::{Linkable, Links, List}; use crate::types::{array::IndexMap, traits::{Get, ToIndex}}; - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[proc_macros::fmt] + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct Id(usize); impl ToIndex for Id { diff --git a/src/types/queue.rs b/src/types/queue.rs index 4e2d159..fbd0ba0 100644 --- a/src/types/queue.rs +++ b/src/types/queue.rs @@ -5,7 +5,7 @@ use super::boxed::Box; use crate::utils::KernelError; /// A ring-buffer based queue, with N elements stored inline. TODO: Make this growable. -#[derive(Debug)] +#[proc_macros::fmt] pub struct Queue { data: Vec, len: usize, diff --git a/src/types/rbtree.rs b/src/types/rbtree.rs index 842ffe2..3a5b0f0 100644 --- a/src/types/rbtree.rs +++ b/src/types/rbtree.rs @@ -22,7 +22,8 @@ pub trait Compare { } #[allow(dead_code)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct Links { parent: Option, left: Option, @@ -44,7 +45,8 @@ impl Links { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[proc_macros::fmt] +#[derive(Clone, Copy, PartialEq, Eq)] enum Color { Red, Black, @@ -62,7 +64,8 @@ impl RbTree } /// Inserts `id` into the tree. If `id` already exists in the tree, it is first removed and then re-inserted. Errors if `id` does not exist in `storage`. - pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> + pub fn insert< + S: Get + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where >::Output: Linkable + Compare,{ let already_linked = { let node = storage.get(id).ok_or(kerr!(NotFound))?; diff --git a/src/types/traits.rs b/src/types/traits.rs index cc02d85..aa8c951 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -18,6 +18,12 @@ pub trait ToIndex { fn to_index>(index: Option) -> usize; } +impl ToIndex for usize { + fn to_index>(index: Option) -> usize { + index.map_or(0, |i| *i.borrow()) + } +} + pub trait Project

{ fn project(&self) -> Option<&P>; fn project_mut(&mut self) -> Option<&mut P>; diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index 5437a2b..540060a 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -9,6 +9,18 @@ pub fn sleep_for(duration: u64) -> isize { hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) } +pub fn yield_thread() -> isize { + let until = u64::MAX; + hal::asm::syscall!(1, (until >> 32) as u32, until as u32) +} + pub fn spawn_thread(func_ptr: EntryFn) -> isize { hal::asm::syscall!(3, func_ptr as u32) +} + +pub fn exit(code: usize) -> ! { + hal::asm::syscall!(4, code as u32); + loop { + hal::asm::nop!(); + } } \ No newline at end of file From 5f22c50b44df81f38a001fb5fa25c32bce99fa12 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:35:45 +0000 Subject: [PATCH 14/28] fixed rt sched. --- examples/hello-world/Cargo.toml | 2 +- examples/hello-world/src/main.rs | 24 +++------ src/idle.rs | 2 +- src/macros.rs | 2 - src/sched.rs | 93 ++++++++++++++++++++++---------- src/sched/rr.rs | 8 +-- src/sched/rt.rs | 22 ++++---- src/sched/task.rs | 10 ++-- src/sched/thread.rs | 78 +++++++++++++++++---------- src/syscalls/sched.rs | 11 +++- src/uapi.rs | 3 +- src/uapi/sched.rs | 18 +++++-- src/uapi/time.rs | 9 ++++ src/uspace.rs | 2 + 14 files changed, 178 insertions(+), 106 deletions(-) create mode 100644 src/uapi/time.rs diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 81645ee..1bd3456 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -osiris = { workspace = true, features = ["defmt"] } +osiris = { workspace = true } [build-dependencies] cfg_aliases = "0.2.1" diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index baa7bb3..4bad2e0 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -4,26 +4,13 @@ use osiris::app_main; extern "C" fn second_thread() { - osiris::uprintln!("Hello from the second thread!"); - - let mut tick = 0; - for i in 0..5 { - osiris::uprintln!("Second thread tick: {}", tick); - tick += 1; - osiris::uapi::sched::sleep_for(1500); - } - - osiris::uapi::sched::exit(0); - osiris::uprintln!("This will never be printed."); -} - -extern "C" fn generator_thread() { - + let mut time = osiris::uapi::time::tick(); let mut cnt = 0; loop { - osiris::uapi::sched::yield_thread(); + time += 100; osiris::uprintln!("Number: {}", cnt); cnt += 1; + osiris::uapi::sched::sleep(time); } } @@ -31,8 +18,9 @@ extern "C" fn generator_thread() { fn main() { osiris::uprintln!("Hello World!"); let mut tick = 0; - osiris::uapi::sched::spawn_thread(second_thread); - osiris::uapi::sched::spawn_thread(generator_thread); + let attrs = osiris::uapi::sched::RtAttrs { deadline: 100, period: 100, budget: 100 }; + + osiris::uapi::sched::spawn_thread(second_thread, Some(attrs)); loop { osiris::uprintln!("Tick: {}", tick); tick += 1; diff --git a/src/idle.rs b/src/idle.rs index 479f407..940605e 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,7 +7,7 @@ extern "C" fn entry() { } pub fn init() { - let attrs = sched::thread::Attributes { entry, fin: None }; + let attrs = sched::thread::Attributes { entry, fin: None, attrs: None }; sched::with(|sched| { if let Err(e) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { panic!("failed to create idle thread. Error: {}", e); diff --git a/src/macros.rs b/src/macros.rs index 0ddf819..78ddb33 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,4 @@ //! Macros for kernel development. -use defmt_rtt as _; - #[macro_export] macro_rules! debug { diff --git a/src/sched.rs b/src/sched.rs index 4001fa1..4e5f130 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -94,7 +94,7 @@ impl Scheduler { } /// Triggers a reschedule at *latest* when we hit timepoint `next`. - fn schedule_resched(now: u64, next: u64) { + fn next_resched(now: u64, next: u64) { let old = NEXT_TICK.load(Ordering::Acquire); if old > now && old <= next { @@ -126,15 +126,18 @@ impl Scheduler { while let Some(uid) = self.wakeup.min() { let mut done = false; WaiterView::::with(&mut self.threads, |view| { - let waiter = view.get(uid).expect("THIS IS A BUG!"); - if waiter.until() > now { - Self::schedule_resched(now, waiter.until()); - done = true; - return; - } - - if let Err(_) = self.wakeup.remove(uid, view) { - bug!("failed to remove thread {} from wakeup tree.", uid); + if let Some(waiter) = view.get(uid) { + if waiter.until() > now { + Self::next_resched(now, waiter.until()); + done = true; + return; + } + + if let Err(_) = self.wakeup.remove(uid, view) { + bug!("failed to remove thread {} from wakeup tree.", uid); + } + } else { + bug!("failed to get thread {} from wakeup tree.", uid); } }); @@ -148,40 +151,57 @@ impl Scheduler { } } - pub fn do_sched(&mut self, now: u64) -> Option<(*mut c_void, &mut task::Task)> { + /// Syncs the new state after the last do_sched call to the scheduler, and returns whether we need to immediately reschedule. + fn sync_to_sched(&mut self, now: u64) -> bool { let dt = now - self.last_tick; self.last_tick = now; if let Some(old) = self.current { - rt::ServerView::::with(&mut self.threads, |view| { - self.rt_scheduler.put(old, dt, view); + let throttle = rt::ServerView::::with(&mut self.threads, |view| { + self.rt_scheduler.put(old, dt, view) }); - self.rr_scheduler.put(old, dt); + + if let Some(throttle) = throttle { + self.sleep_until(throttle, now); + return true; + } + + self.rr_scheduler.put(old, dt as u32); } self.do_wakeups(now); + false + } + + fn select_next(&mut self) -> (thread::UId, u32) { + rt::ServerView::::with(&mut self.threads, |view| self.rt_scheduler.pick(view)) + .or_else(|| self.rr_scheduler.pick(&mut self.threads)) + .unwrap_or((thread::IDLE_THREAD, 1000)) + } + + pub fn do_sched(&mut self, now: u64) -> Option<(*mut c_void, &mut task::Task)> { + // Sync the new state to the scheduler. + if self.sync_to_sched(now) { + // Trigger reschedule after interrupts are enabled. + return None; + } - let pick = - rt::ServerView::::with(&mut self.threads, |view| self.rt_scheduler.pick(now, view)); - let pick = pick.or_else(|| self.rr_scheduler.pick(&mut self.threads)); - let (new, budget) = pick.unwrap_or((thread::IDLE_THREAD, 1000)); + // Pick the next thread to run. + let (new, budget) = self.select_next(); // At this point, the task/thread must exist. Everything else is a bug. - let (ctx, task_id) = if let Some(thread) = self.threads.get(new) { - (thread.ctx(), thread.task_id()) - } else { + let Some(thread) = self.threads.get(new) else { bug!("failed to pick thread {}. Does not exist.", new); }; + let (ctx, task_id) = (thread.ctx(), thread.task_id()); - let task = if let Some(task) = self.tasks.get_mut(task_id) { - task - } else { + let Some(task) = self.tasks.get_mut(task_id) else { bug!("failed to get task {}. Does not exist.", task_id); }; // We don't need to resched if the thread has budget. self.current = Some(new); - Self::schedule_resched(now, now.saturating_add(budget)); + Self::next_resched(now, now.saturating_add(budget as u64)); Some((ctx, task)) } @@ -253,11 +273,23 @@ impl Scheduler { self.rt_scheduler.dequeue(id, view); }); self.rr_scheduler.dequeue(id, &mut self.threads); + self.wakeup + .remove(id, &mut WaiterView::::new(&mut self.threads)); if task.threads_mut().remove(id, &mut self.threads).is_err() { // This should not be possible. The thread ID is from the thread list of the task, so it must exist. bug!("failed to remove thread {} from task {}.", id, uid); } + + if self.threads.remove(&id).is_none() { + // This should not be possible. The thread ID is from the thread list of the task, so it must exist. + bug!("failed to remove thread {} from thread list.", id); + } + + if Some(id) == self.current { + self.current = None; + reschedule(); + } } self.tasks.remove(&uid).ok_or(kerr!(InvalidArgument))?; @@ -283,6 +315,15 @@ impl Scheduler { pub fn kill_thread(&mut self, uid: Option) -> Result<()> { let uid = uid.unwrap_or(self.current.ok_or(kerr!(InvalidArgument))?); self.dequeue(uid); + self.wakeup + .remove(uid, &mut WaiterView::::new(&mut self.threads)); + + self.tasks + .get_mut(uid.tid().owner()) + .ok_or(kerr!(InvalidArgument))? + .threads_mut() + .remove(uid, &mut self.threads)?; + self.threads.remove(&uid).ok_or(kerr!(InvalidArgument))?; if Some(uid) == self.current { @@ -353,8 +394,6 @@ pub extern "C" fn sched_enter(mut ctx: *mut c_void) -> *mut c_void { dispch::prepare(task); } ctx = new; - } else { - bug!("failed to schedule a thread. No threads available."); } ctx diff --git a/src/sched/rr.rs b/src/sched/rr.rs index ee9f877..1e966c9 100644 --- a/src/sched/rr.rs +++ b/src/sched/rr.rs @@ -6,8 +6,8 @@ pub struct Scheduler { queue: List, current: Option, - current_left: u64, - quantum: u64, + current_left: u32, + quantum: u32, } impl Scheduler { @@ -20,7 +20,7 @@ impl Scheduler { self.queue.push_back(uid, storage).map_err(|_| kerr!(InvalidArgument)) } - pub fn put(&mut self, uid: thread::UId, dt: u64) { + pub fn put(&mut self, uid: thread::UId, dt: u32) { if let Some(current) = self.current { if current == uid { self.current_left = self.current_left.saturating_sub(dt); @@ -28,7 +28,7 @@ impl Scheduler { } } - pub fn pick(&mut self, storage: &mut super::ThreadMap) -> Option<(thread::UId, u64)> { + pub fn pick(&mut self, storage: &mut super::ThreadMap) -> Option<(thread::UId, u32)> { match self.current { Some(current) if self.current_left > 0 => return Some((current, self.current_left)), Some(current) => { diff --git a/src/sched/rt.rs b/src/sched/rt.rs index 7c3405b..33799a6 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -15,31 +15,27 @@ impl Scheduler { pub fn enqueue(&mut self, uid: thread::UId, now: u64, storage: &mut ServerView) { if let Some(server) = storage.get_mut(uid) { - server.replenish(now); + // Threads are only enqueued when they are runnable. + server.on_wakeup(now); self.edf.insert(uid, storage); } } - pub fn put(&mut self, uid: thread::UId, dt: u64, storage: &mut ServerView) { + /// This should be called on each do_schedule call, to update the internal scheduler state. + /// If this function returns Some(u64) it means the current thread has exhausted its budget and should be throttled until the returned timestamp. + pub fn put(&mut self, uid: thread::UId, dt: u64, storage: &mut ServerView) -> Option { if Some(uid) == self.edf.min() { if let Some(server) = storage.get_mut(uid) { - server.consume(dt); + return server.consume(dt); } else { bug!("thread {} not found in storage", uid); } } - } - pub fn pick(&mut self, now: u64, storage: &mut ServerView) -> Option<(thread::UId, u64)> { - let id = self.edf.min()?; - - if storage.get(id)?.budget() == 0 { - self.edf.remove(id, storage); - storage.get_mut(id)?.replenish(now); - self.edf.insert(id, storage); - } + None + } - // Insert updated the min cache. + pub fn pick(&mut self, storage: &mut ServerView) -> Option<(thread::UId, u32)> { self.edf.min().and_then(|id| storage.get(id).map(|s| (id, s.budget()))) } diff --git a/src/sched/task.rs b/src/sched/task.rs index c1e094a..3c74835 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -9,12 +9,12 @@ use hal::Stack; use hal::stack::Stacklike; use crate::error::Result; -use crate::sched::{GlobalScheduler, ThreadMap, thread}; +use crate::sched::{ThreadMap, thread}; use crate::types::list; use crate::{mem, sched}; use crate::mem::vmm::AddressSpacelike; -use crate::types::traits::{Get, GetMut, ToIndex}; +use crate::types::traits::ToIndex; pub struct Defaults { pub stack_pages: usize, @@ -123,9 +123,9 @@ impl Task { let stack = unsafe { Stack::new(stack) }?; let tid = self.allocate_tid(); - let new = sched::thread::Thread::new(tid.get_uid(uid), stack); - storage.insert(&tid.get_uid(uid), new); - self.threads.push_back(tid.get_uid(uid), storage); + let new = sched::thread::Thread::new(tid.get_uid(uid), stack, attrs.attrs); + storage.insert(&tid.get_uid(uid), new)?; + self.threads.push_back(tid.get_uid(uid), storage)?; Ok(tid.get_uid(uid)) } diff --git a/src/sched/thread.rs b/src/sched/thread.rs index a83203d..80d01f1 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -3,19 +3,25 @@ use core::fmt::Display; use core::{borrow::Borrow, ffi::c_void}; -use hal::{Stack, stack::EntryFn}; use hal::stack::{FinFn, Stacklike}; +use hal::{Stack, stack::EntryFn}; use proc_macros::TaggedLinks; use crate::error::Result; use crate::sched::task::{self, KERNEL_TASK}; -use crate::time::tick; use crate::types::list; -use crate::{types::{rbtree::{self, Compare}, traits::{Project, ToIndex}}}; +use crate::types::{ + rbtree::{self, Compare}, + traits::{Project, ToIndex}, +}; +use crate::uapi; pub const IDLE_THREAD: UId = UId { uid: 1, - tid: Id { id: 0, owner: KERNEL_TASK }, + tid: Id { + id: 0, + owner: KERNEL_TASK, + }, }; /// Id of a task. This is only unique within a Task. @@ -132,12 +138,11 @@ pub struct State { } #[proc_macros::fmt] -#[derive(Clone, Copy)] -#[derive(TaggedLinks)] +#[derive(Clone, Copy, TaggedLinks)] pub struct RtServer { - budget: u64, - budget_left: u64, - period: u64, + budget: u32, + budget_left: u32, + period: u32, deadline: u64, // Back-reference to the thread uid. @@ -149,36 +154,50 @@ pub struct RtServer { } impl RtServer { - pub fn new(budget: u64, period: u64, uid: UId) -> Self { + pub fn new(budget: u32, period: u32, deadline: u64, uid: UId) -> Self { Self { budget, budget_left: budget, period, - deadline: tick() + period, + deadline, uid, _rt_links: rbtree::Links::new(), } } - pub fn budget_left(&self) -> u64 { + pub fn budget_left(&self) -> u32 { self.budget_left } - pub fn budget(&self) -> u64 { + pub fn budget(&self) -> u32 { self.budget } - pub fn replenish(&mut self, now: u64) { - self.deadline += self.period; - self.budget_left = self.budget; + fn violates_sched(&self, now: u64) -> bool { + self.budget_left as u64 * self.period as u64 + > self.budget as u64 * (self.deadline.saturating_sub(now)) } - pub fn consume(&mut self, dt: u64) { - if self.budget_left >= dt { - self.budget_left -= dt; - } else { - self.budget_left = 0; + pub fn on_wakeup(&mut self, now: u64) { + if self.deadline <= now || self.violates_sched(now) { + self.deadline = now + self.period as u64; + self.budget_left = self.budget; + } + } + + pub fn replenish(&mut self) { + self.deadline = self.deadline + self.period as u64; + self.budget_left += self.budget; + } + + pub fn consume(&mut self, dt: u64) -> Option { + self.budget_left = self.budget_left.saturating_sub(dt as u32); + + if self.budget_left == 0 { + return Some(self.deadline); } + + None } pub fn deadline(&self) -> u64 { @@ -203,8 +222,7 @@ impl Compare for RtServer { } #[proc_macros::fmt] -#[derive(Clone, Copy)] -#[derive(TaggedLinks)] +#[derive(Clone, Copy, TaggedLinks)] pub struct Waiter { /// The time when the Thread will be awakened. until: u64, @@ -261,12 +279,12 @@ pub struct ThreadList; pub struct Attributes { pub entry: EntryFn, pub fin: Option, + pub attrs: Option, } /// The struct representing a thread. #[proc_macros::fmt] -#[derive(Clone, Copy)] -#[derive(TaggedLinks)] +#[derive(Clone, Copy, TaggedLinks)] pub struct Thread { /// The current state of the thread. state: State, @@ -291,14 +309,16 @@ impl Thread { /// `stack` - The stack of the thread. /// /// Returns a new thread. - pub fn new(uid: UId, stack: Stack) -> Self { + pub fn new(uid: UId, stack: Stack, rtattrs: Option) -> Self { + let server = + rtattrs.map(|attrs| RtServer::new(attrs.budget, attrs.period, attrs.deadline, uid)); Self { state: State { run_state: RunState::Ready, stack, }, uid, - rt_server: None, + rt_server: server, waiter: None, rr_links: list::Links::new(), thread_links: list::Links::new(), @@ -348,7 +368,7 @@ impl PartialEq for Thread { impl Project for Thread { fn project(&self) -> Option<&RtServer> { - self.rt_server.as_ref() + self.rt_server.as_ref() } fn project_mut(&mut self) -> Option<&mut RtServer> { @@ -358,7 +378,7 @@ impl Project for Thread { impl Project for Thread { fn project(&self) -> Option<&Waiter> { - self.waiter.as_ref() + self.waiter.as_ref() } fn project_mut(&mut self) -> Option<&mut Waiter> { diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index 1972b31..258574e 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -4,7 +4,7 @@ use core::ffi::c_int; use proc_macros::syscall_handler; -use crate::{sched, time}; +use crate::{sched, time, uapi::sched::RtAttrs}; #[syscall_handler(num = 1)] fn sleep(until_hi: u32, until_lo: u32) -> c_int { @@ -30,11 +30,18 @@ fn sleep_for(duration_hi: u32, duration_lo: u32) -> c_int { } #[syscall_handler(num = 3)] -fn spawn_thread(func_ptr: usize) -> c_int { +fn spawn_thread(func_ptr: usize, attrs: *const RtAttrs) -> c_int { sched::with(|sched| { + let attrs = if attrs.is_null() { + None + } else { + Some(unsafe { *attrs }) + }; + let attrs = sched::thread::Attributes { entry: unsafe { core::mem::transmute(func_ptr) }, fin: None, + attrs, }; match sched.create_thread(None, &attrs) { Ok(uid) => { diff --git a/src/uapi.rs b/src/uapi.rs index bfc9b49..60a2b0e 100644 --- a/src/uapi.rs +++ b/src/uapi.rs @@ -1,2 +1,3 @@ pub mod print; -pub mod sched; \ No newline at end of file +pub mod sched; +pub mod time; \ No newline at end of file diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index 540060a..ef39306 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -1,6 +1,5 @@ use hal::stack::EntryFn; - pub fn sleep(until: u64) -> isize { hal::asm::syscall!(1, (until >> 32) as u32, until as u32) } @@ -14,8 +13,21 @@ pub fn yield_thread() -> isize { hal::asm::syscall!(1, (until >> 32) as u32, until as u32) } -pub fn spawn_thread(func_ptr: EntryFn) -> isize { - hal::asm::syscall!(3, func_ptr as u32) +#[repr(C)] +#[derive(Clone, Copy)] +pub struct RtAttrs { + pub deadline: u64, + pub period: u32, + pub budget: u32, +} + +pub fn spawn_thread(func_ptr: EntryFn, attrs: Option) -> isize { + let attr_ptr = if let Some(attrs) = attrs { + &attrs as *const RtAttrs as usize + } else { + 0 + }; + hal::asm::syscall!(3, func_ptr as u32, attr_ptr) } pub fn exit(code: usize) -> ! { diff --git a/src/uapi/time.rs b/src/uapi/time.rs new file mode 100644 index 0000000..1b63ebb --- /dev/null +++ b/src/uapi/time.rs @@ -0,0 +1,9 @@ +use crate::time; + +pub fn mono_now() -> u64 { + time::mono_now() +} + +pub fn tick() -> u64 { + time::tick() +} \ No newline at end of file diff --git a/src/uspace.rs b/src/uspace.rs index 2f11b34..91ae66b 100644 --- a/src/uspace.rs +++ b/src/uspace.rs @@ -15,7 +15,9 @@ pub fn init_app() { let attrs = sched::thread::Attributes { entry: app_main_entry, fin: None, + attrs: None, }; + sched::with(|sched| { if let Ok(uid) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { if sched.enqueue(time::tick(), uid).is_err() { From 9a86fefccdd4e56bba3a99c8f724558804b6fa69 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:42:57 +0000 Subject: [PATCH 15/28] fixed bug. --- machine/arm/stm32l4xx/interface/clock.c | 2 +- machine/arm/stm32l4xx/interface/lib.c | 2 +- machine/arm/stm32l4xx/interface/lib.h | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/machine/arm/stm32l4xx/interface/clock.c b/machine/arm/stm32l4xx/interface/clock.c index ed3acac..4110c3c 100644 --- a/machine/arm/stm32l4xx/interface/clock.c +++ b/machine/arm/stm32l4xx/interface/clock.c @@ -53,7 +53,7 @@ void tim2_hndlr(void) } } -void SystemClock_Config(void) +void init_clock_cfg(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; diff --git a/machine/arm/stm32l4xx/interface/lib.c b/machine/arm/stm32l4xx/interface/lib.c index 3c7d723..41319fa 100644 --- a/machine/arm/stm32l4xx/interface/lib.c +++ b/machine/arm/stm32l4xx/interface/lib.c @@ -31,7 +31,7 @@ void init_hal(void) { enable_faults(); - SystemClock_Config(); + init_clock_cfg(); init_systick(); } diff --git a/machine/arm/stm32l4xx/interface/lib.h b/machine/arm/stm32l4xx/interface/lib.h index 7b9637e..c66f171 100644 --- a/machine/arm/stm32l4xx/interface/lib.h +++ b/machine/arm/stm32l4xx/interface/lib.h @@ -1 +1,3 @@ -#pragma once \ No newline at end of file +#pragma once + +void init_clock_cfg(void); \ No newline at end of file From b5823f755b10dd5ae762d27b71c1022bfeaa12fb Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:29:20 +0000 Subject: [PATCH 16/28] fix stuff --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 4 ++- build.rs | 54 ++-------------------------- config.toml | 7 +--- examples/hello-world/Cargo.toml | 7 ++++ machine/arm/Cargo.toml | 1 - machine/arm/src/panic.rs | 1 + machine/arm/src/sched.rs | 2 +- machine/arm/stm32l4xx/CMakeLists.txt | 2 +- machine/testing/src/lib.rs | 3 ++ xtasks/Cargo.toml | 2 +- xtasks/crates/config/Cargo.toml | 2 +- xtasks/crates/config/src/lib.rs | 3 ++ xtasks/crates/config/src/main.rs | 4 +++ xtasks/crates/dtgen/Cargo.toml | 2 +- xtasks/crates/dtgen/src/lib.rs | 3 ++ xtasks/crates/dtgen/src/main.rs | 4 +++ xtasks/crates/injector/Cargo.toml | 2 +- xtasks/crates/injector/src/main.rs | 4 +++ xtasks/crates/pack/Cargo.toml | 2 +- xtasks/crates/pack/src/main.rs | 4 +++ xtasks/logging/Cargo.toml | 2 +- xtasks/logging/src/lib.rs | 4 +++ xtasks/src/main.rs | 4 +++ 24 files changed, 57 insertions(+), 68 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0857d86..d0a9646 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -86,7 +86,7 @@ ENV CARGO_HOME=/usr/local/cargo ENV KANI_HOME=/usr/local/kani ENV PATH="/usr/local/cargo/bin:${PATH}" -ARG RUST_VERSION=1.93.1 +ARG RUST_VERSION=1.94.1 RUN --mount=type=cache,target=/usr/local/cargo/registry \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ | sh -s -- -y --no-modify-path --default-toolchain ${RUST_VERSION} && \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 89b2ba7..175a961 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -36,7 +36,9 @@ "runArgs": [ // Mount USB devices under Linux "--device", - "/dev/bus/usb:/dev/bus/usb" + "/dev/bus/usb:/dev/bus/usb", + "--group-add", + "keep-groups" ], "mounts": [ // Make ssh keys available diff --git a/build.rs b/build.rs index 1efb348..42b6eb3 100644 --- a/build.rs +++ b/build.rs @@ -22,6 +22,8 @@ fn main() { panic!("Failed to generate syscall match statement."); } + generate_device_tree().expect("Failed to generate device tree."); + cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, } @@ -32,9 +34,8 @@ fn main() { fn generate_device_tree() -> Result<(), Box> { let dts = std::env::var("OSIRIS_TUNING_DTS").unwrap_or_else(|_| "nucleo_l4r5zi.dts".to_string()); - println!("cargo::rerun-if-changed={dts}"); - let dts_path = std::path::Path::new("boards").join(dts); + println!("cargo::rerun-if-changed={}", dts_path.display()); // dependencies SoC/HAL/pins let zephyr = Path::new(&std::env::var("OUT_DIR").unwrap()).join("zephyr"); @@ -158,55 +159,6 @@ fn sparse_clone( // Syscalls --------------------------------------------------------------------------------------- -fn generate_syscalls_export>(root: P) -> Result<(), std::io::Error> { - let syscalls = collect_syscalls_export(root); - - let out_dir = std::env::var("OUT_DIR").unwrap(); - let out_path = Path::new(&out_dir).join("syscalls_export.rs"); - let mut file = File::create(out_path)?; - - writeln!(file, "// This file is @generated by build.rs. Do not edit!")?; - - for (name, (number, inputs)) in &syscalls { - let mut args = &inputs.iter().fold("".to_owned(), |acc, arg| { - acc + "," + &arg.into_token_stream().to_string() - })[..]; - if !args.is_empty() { - args = &args[1..]; - } - let names = get_arg_names(args); - writeln!(file)?; - writeln!(file, "pub fn {name}({args}) {{")?; - writeln!(file, " hal::asm::syscall!({number}{names});")?; - writeln!(file, "}}")?; - } - - Ok(()) -} - -fn get_arg_names(args: &str) -> String { - if args.is_empty() { - return "".to_string(); - } - let mut in_arg_name = true; - - ", ".to_owned() - + &args.chars().fold("".to_owned(), |mut acc, char| { - if char.eq(&' ') { - in_arg_name = false; - return acc; - } - if char.eq(&',') { - in_arg_name = true; - return acc + ", "; - } - if in_arg_name { - acc.push(char); - } - acc - }) -} - fn gen_syscall_match(root: &Path, out: &Path) -> Result<(), std::io::Error> { let syscalls = find_syscalls(root); let mut file = File::create(out.join("syscall_match.in"))?; diff --git a/config.toml b/config.toml index 519b669..0c03ce5 100644 --- a/config.toml +++ b/config.toml @@ -1,10 +1,5 @@ [env] -OSIRIS_ARM_HAL = "stm32l4xx" -OSIRIS_ARM_STM32L4XX_VARIANT = "r5zi" -OSIRIS_DEBUG_UART = "UART5" -OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" -OSIRIS_TUNING_ENABLEFPU = "false" OSIRIS_STACKPAGES = "1" [build] -target = "thumbv7em-none-eabi" +target = "host-tuple" diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 1bd3456..a86e875 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -3,6 +3,13 @@ name = "hello-world" version = "0.1.0" edition = "2024" +[[bin]] +name = "hello-world" +path = "src/main.rs" +test = false +bench = false +doctest = false + [dependencies] osiris = { workspace = true } diff --git a/machine/arm/Cargo.toml b/machine/arm/Cargo.toml index 9fb4753..bafea9d 100644 --- a/machine/arm/Cargo.toml +++ b/machine/arm/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "hal-arm" version = "0.1.0" -rust-version = "1.85.0" authors = ["Thomas Wachter"] edition = "2024" build = "build.rs" diff --git a/machine/arm/src/panic.rs b/machine/arm/src/panic.rs index 9025b8a..946ec21 100644 --- a/machine/arm/src/panic.rs +++ b/machine/arm/src/panic.rs @@ -7,5 +7,6 @@ use crate::asm; pub fn panic_handler(_info: &PanicInfo) -> ! { asm::disable_irq_save(); + #[allow(clippy::empty_loop)] loop {} } diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index eb3f274..9521f33 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -72,7 +72,7 @@ impl ArmStack { } fn is_call_aligned(sp: StackPtr) -> bool { - (sp.offset % 2) == 0 + sp.offset.is_multiple_of(2) } fn in_bounds(&self, sp: *mut u32) -> Option { diff --git a/machine/arm/stm32l4xx/CMakeLists.txt b/machine/arm/stm32l4xx/CMakeLists.txt index 3e11634..d692d85 100644 --- a/machine/arm/stm32l4xx/CMakeLists.txt +++ b/machine/arm/stm32l4xx/CMakeLists.txt @@ -44,7 +44,7 @@ set(LINKER_SCRIPT_OUT ${OUT_DIR}/link.ld) # We track environment variables that start with OSIRIS_ as dependencies as they can change the output of the compilation set(DEPS_FILE ${OUT_DIR}/deps.txt) -file(WRITE ${DEPS_FILE} "${CONFIG_DEFINES}") +file(GENERATE OUTPUT ${DEPS_FILE} CONTENT "${CONFIG_DEFINES}") add_custom_command( OUTPUT ${LINKER_SCRIPT_OUT} diff --git a/machine/testing/src/lib.rs b/machine/testing/src/lib.rs index 7c8dc6a..4477ff1 100644 --- a/machine/testing/src/lib.rs +++ b/machine/testing/src/lib.rs @@ -1,3 +1,6 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg(not(target_os = "none"))] + use core::result::Result::Ok; use hal_api::{Result, Schedable}; diff --git a/xtasks/Cargo.toml b/xtasks/Cargo.toml index bb63b52..96e7895 100644 --- a/xtasks/Cargo.toml +++ b/xtasks/Cargo.toml @@ -3,7 +3,7 @@ name = "xtask" version = "0.1.0" edition = "2024" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] logging = { path = "logging" } clap = "4.5.53" walkdir = "2.5.0" diff --git a/xtasks/crates/config/Cargo.toml b/xtasks/crates/config/Cargo.toml index 5428bb0..78dc4c2 100644 --- a/xtasks/crates/config/Cargo.toml +++ b/xtasks/crates/config/Cargo.toml @@ -3,7 +3,7 @@ name = "config" version = "0.1.0" edition = "2024" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] logging = { workspace = true } ratatui = "0.29.0" crossterm = "0.27" diff --git a/xtasks/crates/config/src/lib.rs b/xtasks/crates/config/src/lib.rs index 65f431e..a5f0704 100644 --- a/xtasks/crates/config/src/lib.rs +++ b/xtasks/crates/config/src/lib.rs @@ -1,3 +1,6 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg(not(target_os = "none"))] + use std::{path::Path, process::exit}; use crate::{ diff --git a/xtasks/crates/config/src/main.rs b/xtasks/crates/config/src/main.rs index 37ebaf6..00f1244 100644 --- a/xtasks/crates/config/src/main.rs +++ b/xtasks/crates/config/src/main.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use std::path::{Path, PathBuf}; use config::error::Error; diff --git a/xtasks/crates/dtgen/Cargo.toml b/xtasks/crates/dtgen/Cargo.toml index 2cedf04..d6cc676 100644 --- a/xtasks/crates/dtgen/Cargo.toml +++ b/xtasks/crates/dtgen/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" name = "dtgen" path = "src/main.rs" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] fdt = "0.1.5" clap = { version = "4", features = ["derive"] } quote = "1" diff --git a/xtasks/crates/dtgen/src/lib.rs b/xtasks/crates/dtgen/src/lib.rs index 36d6917..ec069a5 100644 --- a/xtasks/crates/dtgen/src/lib.rs +++ b/xtasks/crates/dtgen/src/lib.rs @@ -1,3 +1,6 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg(not(target_os = "none"))] + mod codegen; mod ir; mod parser; diff --git a/xtasks/crates/dtgen/src/main.rs b/xtasks/crates/dtgen/src/main.rs index 370bf88..af2bd1f 100644 --- a/xtasks/crates/dtgen/src/main.rs +++ b/xtasks/crates/dtgen/src/main.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use clap::Parser; use std::path::PathBuf; diff --git a/xtasks/crates/injector/Cargo.toml b/xtasks/crates/injector/Cargo.toml index 9364c7e..0faa7cf 100644 --- a/xtasks/crates/injector/Cargo.toml +++ b/xtasks/crates/injector/Cargo.toml @@ -3,7 +3,7 @@ name = "injector" version = "0.1.0" edition = "2024" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] logging = { workspace = true } clap = { version = "4.5", features = ["derive"] } object = "0.36" diff --git a/xtasks/crates/injector/src/main.rs b/xtasks/crates/injector/src/main.rs index 3fafe36..3171e88 100644 --- a/xtasks/crates/injector/src/main.rs +++ b/xtasks/crates/injector/src/main.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use cargo_metadata::MetadataCommand; use clap::Parser; use object::{Object, ObjectSection}; diff --git a/xtasks/crates/pack/Cargo.toml b/xtasks/crates/pack/Cargo.toml index e898333..a9d72eb 100644 --- a/xtasks/crates/pack/Cargo.toml +++ b/xtasks/crates/pack/Cargo.toml @@ -3,7 +3,7 @@ name = "pack" version = "0.1.0" edition = "2024" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] anyhow = "1.0.100" logging = { workspace = true } clap = { version = "4.5.47", features = ["derive"] } diff --git a/xtasks/crates/pack/src/main.rs b/xtasks/crates/pack/src/main.rs index f84c7a7..6eba097 100644 --- a/xtasks/crates/pack/src/main.rs +++ b/xtasks/crates/pack/src/main.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use std::path::PathBuf; use clap::Parser; diff --git a/xtasks/logging/Cargo.toml b/xtasks/logging/Cargo.toml index be24d26..fb270e1 100644 --- a/xtasks/logging/Cargo.toml +++ b/xtasks/logging/Cargo.toml @@ -3,7 +3,7 @@ name = "logging" version = "0.1.0" edition = "2024" -[dependencies] +[target.'cfg(not(target_os = "none"))'.dependencies] env_logger = "0.11.8" log = "0.4.29" once_cell = "1.21.3" diff --git a/xtasks/logging/src/lib.rs b/xtasks/logging/src/lib.rs index 9b64f20..0bec258 100644 --- a/xtasks/logging/src/lib.rs +++ b/xtasks/logging/src/lib.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use std::io::Write; use std::sync::OnceLock; diff --git a/xtasks/src/main.rs b/xtasks/src/main.rs index ad7cb3a..a2a4820 100644 --- a/xtasks/src/main.rs +++ b/xtasks/src/main.rs @@ -1,3 +1,7 @@ +#![cfg_attr(target_os = "none", no_std)] +#![cfg_attr(target_os = "none", no_main)] +#![cfg(not(target_os = "none"))] + use std::path::{Path, PathBuf}; use cargo_metadata::{MetadataCommand, TargetKind}; From fce9a244d01c19322a1671de3c40d6a8d86a1084 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:15:26 +0000 Subject: [PATCH 17/28] wip --- .cargo/config.toml | 1 + Cargo.lock | 3 + boards/nucleo_l4r5zi.dts | 7 +++ build.rs | 30 +++++++--- machine/arm/stm32l4xx/CMakeLists.txt | 1 - machine/arm/stm32l4xx/link.ld | 7 +-- machine/arm/stm32l4xx/r5zi/link.ld | 7 --- xtasks/crates/dtgen/Cargo.toml | 3 + xtasks/crates/dtgen/src/ldgen.rs | 84 ++++++++++++++++++++++++++++ xtasks/crates/dtgen/src/lib.rs | 20 +++++-- xtasks/crates/dtgen/src/main.rs | 18 +++++- 11 files changed, 152 insertions(+), 29 deletions(-) delete mode 100644 machine/arm/stm32l4xx/r5zi/link.ld create mode 100644 xtasks/crates/dtgen/src/ldgen.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index dfc05b1..eb378db 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,5 +8,6 @@ xtask = "--config xtasks/.cargo/config.toml run -p xtask --release --" [target.'cfg(target_os = "none")'] rustflags = [ "-C", "link-arg=--entry=main", + "-C", "link-arg=-Tprelude.ld", "-C", "link-arg=-Tlink.ld", ] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8ebf8eb..e0af60e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -583,6 +583,9 @@ version = "0.1.0" dependencies = [ "clap", "fdt", + "indoc", + "log", + "logging", "prettyplease", "proc-macro2", "quote", diff --git a/boards/nucleo_l4r5zi.dts b/boards/nucleo_l4r5zi.dts index d99a7d1..ce19d57 100644 --- a/boards/nucleo_l4r5zi.dts +++ b/boards/nucleo_l4r5zi.dts @@ -12,6 +12,8 @@ #include #include +/delete-node/ &sram1; + / { model = "STMicroelectronics STM32L4R5ZI-NUCLEO board"; compatible = "st,stm32l4r5zi-nucleo"; @@ -27,6 +29,11 @@ osiris,entropy = &rng; }; + sram1: memory@20030000 { + device_type = "memory"; + reg = <0x20030000 0x00010000>; + }; + leds: leds { compatible = "gpio-leds"; diff --git a/build.rs b/build.rs index 42b6eb3..27c4cfa 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ +use core::panic; use std::process::Command; use std::{collections::HashMap, fs, fs::File, path::Path, path::PathBuf}; @@ -22,7 +23,13 @@ fn main() { panic!("Failed to generate syscall match statement."); } - generate_device_tree().expect("Failed to generate device tree."); + let dt = build_device_tree(Path::new(&out_dir)).unwrap_or_else(|e| { + panic!("Failed to build device tree from DTS files: {e}"); + }); + + if let Err(e) = generate_device_tree(&dt, Path::new(&out_dir)) { + panic!("Failed to generate device tree scripts: {e}"); + } cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, @@ -31,15 +38,25 @@ fn main() { // Device Tree Codegen ---------------------------------------------------------------------------- -fn generate_device_tree() -> Result<(), Box> { +fn generate_device_tree(dt: &dtgen::ir::DeviceTree, out: &Path) -> Result<(), Box> { + let rust_content = dtgen::generate_rust(dt); + std::fs::write(out.join("device_tree.rs"), rust_content)?; + + let ld_content = dtgen::generate_ld(dt).map_err(|e| format!("linker script generation failed: {e}"))?; + std::fs::write(out.join("prelude.ld"), ld_content)?; + println!("cargo::rustc-link-search=native={}", out.display()); + Ok(()) +} + +fn build_device_tree(out: &Path) -> Result> { let dts = std::env::var("OSIRIS_TUNING_DTS").unwrap_or_else(|_| "nucleo_l4r5zi.dts".to_string()); let dts_path = std::path::Path::new("boards").join(dts); println!("cargo::rerun-if-changed={}", dts_path.display()); // dependencies SoC/HAL/pins - let zephyr = Path::new(&std::env::var("OUT_DIR").unwrap()).join("zephyr"); - let hal_stm32 = Path::new(&std::env::var("OUT_DIR").unwrap()).join("hal_stm32"); + let zephyr = Path::new(out).join("zephyr"); + let hal_stm32 = Path::new(out).join("hal_stm32"); // clean state if zephyr.exists() { @@ -70,7 +87,7 @@ fn generate_device_tree() -> Result<(), Box> { Some(&hal_rev), )?; - let out = Path::new(&std::env::var("OUT_DIR").unwrap()).join("device_tree.rs"); + //let out = Path::new(&std::env::var("OUT_DIR").unwrap()).join("device_tree.rs"); let include_paths = [ zephyr.join("include"), zephyr.join("dts/arm/st"), @@ -90,8 +107,7 @@ fn generate_device_tree() -> Result<(), Box> { } } - dtgen::run(&dts_path, &include_refs, &out)?; - Ok(()) + Ok(dtgen::parse_dts(&dts_path, &include_refs)?) } fn get_hal_revision(zephyr_path: &Path) -> Result> { diff --git a/machine/arm/stm32l4xx/CMakeLists.txt b/machine/arm/stm32l4xx/CMakeLists.txt index d692d85..9410027 100644 --- a/machine/arm/stm32l4xx/CMakeLists.txt +++ b/machine/arm/stm32l4xx/CMakeLists.txt @@ -50,7 +50,6 @@ add_custom_command( OUTPUT ${LINKER_SCRIPT_OUT} COMMAND ${CMAKE_C_COMPILER} -E -P -x c ${CONFIG_DEFINES} - -DMCU_HEADER=\"${OSIRIS_ARM_STM32L4XX_VARIANT}/link.ld\" ${LINKER_SCRIPT_IN} -o ${LINKER_SCRIPT_OUT} DEPENDS ${LINKER_SCRIPT_IN} ${DEPS_FILE} diff --git a/machine/arm/stm32l4xx/link.ld b/machine/arm/stm32l4xx/link.ld index b3e0ee8..ef1eae1 100644 --- a/machine/arm/stm32l4xx/link.ld +++ b/machine/arm/stm32l4xx/link.ld @@ -1,11 +1,6 @@ -#ifndef MCU_HEADER -#error "MCU_HEADER is not defined." -#endif - -#include MCU_HEADER - __ram_start = ORIGIN(RAM); __ram_end = ORIGIN(RAM) + LENGTH(RAM); +__stack_size = 0x8000; #if OSIRIS_DEBUG_RUNTIMESYMBOLS /* at least 250kb for the symbol table */ diff --git a/machine/arm/stm32l4xx/r5zi/link.ld b/machine/arm/stm32l4xx/r5zi/link.ld deleted file mode 100644 index 06a4b1f..0000000 --- a/machine/arm/stm32l4xx/r5zi/link.ld +++ /dev/null @@ -1,7 +0,0 @@ -MEMORY -{ - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2M - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 640K -} - -__stack_size = 0x8000; \ No newline at end of file diff --git a/xtasks/crates/dtgen/Cargo.toml b/xtasks/crates/dtgen/Cargo.toml index d6cc676..432387d 100644 --- a/xtasks/crates/dtgen/Cargo.toml +++ b/xtasks/crates/dtgen/Cargo.toml @@ -13,8 +13,11 @@ path = "src/main.rs" [target.'cfg(not(target_os = "none"))'.dependencies] fdt = "0.1.5" +logging = { workspace = true } +log = "0.4.27" clap = { version = "4", features = ["derive"] } quote = "1" proc-macro2 = "1" prettyplease = "0.2" syn = { version = "2", features = ["full"] } +indoc = "2.0.7" \ No newline at end of file diff --git a/xtasks/crates/dtgen/src/ldgen.rs b/xtasks/crates/dtgen/src/ldgen.rs new file mode 100644 index 0000000..e0b5041 --- /dev/null +++ b/xtasks/crates/dtgen/src/ldgen.rs @@ -0,0 +1,84 @@ +use crate::ir::DeviceTree; + +fn format_region(name: &str, base: u64, size: u64) -> String { + format!( + " {} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}", + name, base, size + ) +} + +fn format_memory_section(regions: &[(&str, u64, u64)]) -> String { + let regions = regions + .iter() + .map(|&(name, base, size)| format_region(name, base, size)) + .collect::>() + .join("\n"); + + indoc::formatdoc! {" + /* This file is @generated by dtgen. Do not edit. */ + MEMORY {{ + {regions} + }} + ", regions = regions} +} + +fn format_irq_provides(num: u32) -> String { + format!("PROVIDE(__irq_{}_handler = default_handler);", num) +} + +fn coalesce_regions<'a>(name: &'a str, regions: Vec<(&'a str, u64, u64)>) -> Result, String> { + regions + .clone() + .into_iter() + .try_fold(None, |acc, (_, base, size)| { + if let Some((_, acc_base, acc_size)) = acc { + if base > acc_base + acc_size || acc_base > base + size { + return Err(format!("Regions are not contiguous. {regions:?}")); + } + + let end = (base + size).max(acc_base + acc_size); + let base = base.min(acc_base); + let size = end - base; + Ok(Some((name, base, size))) + } else { + Ok(Some((name, base, size))) + } + }) +} + +pub fn generate_ld(dt: &DeviceTree) -> Result { + // Generates a linker script prelude that defines the memory regions for the device tree. + + let mut ram: Vec<(&str, u64, u64)> = dt + .nodes + .iter() + .filter(|n| n.name.starts_with("memory@") || n.name == "memory") + .filter_map(|n| { + let (base, size) = n.reg?; + Some((n.name.as_str(), base, size)) + }) + .collect(); + ram.sort_by_key(|&(_, base, _)| base); + + let mut flash: Vec<(&str, u64, u64)> = dt + .nodes + .iter() + .filter(|n| n.name.starts_with("flash@") || n.name == "flash") + .filter_map(|n| { + let (base, size) = n.reg?; + Some((n.name.as_str(), base, size)) + }) + .collect(); + flash.sort_by_key(|&(_, base, _)| base); + + let flash = coalesce_regions("FLASH", flash)?; + let ram = coalesce_regions("RAM", ram)?; + + let regions = flash.into_iter().chain(ram).collect::>(); + + if regions.is_empty() { + return Err("No memory regions found in device tree".to_string()); + } + + Ok(format_memory_section(®ions)) +} diff --git a/xtasks/crates/dtgen/src/lib.rs b/xtasks/crates/dtgen/src/lib.rs index ec069a5..aa3d761 100644 --- a/xtasks/crates/dtgen/src/lib.rs +++ b/xtasks/crates/dtgen/src/lib.rs @@ -2,15 +2,23 @@ #![cfg(not(target_os = "none"))] mod codegen; -mod ir; +pub mod ir; mod parser; +mod ldgen; use std::path::Path; -pub fn run(dts_path: &Path, include_dirs: &[&Path], out_path: &Path) -> Result<(), String> { +use crate::ir::DeviceTree; + +pub fn parse_dts(dts_path: &Path, include_dirs: &[&Path]) -> Result { let dtb = parser::dts_to_dtb(dts_path, include_dirs)?; - let dt = parser::dtb_to_devicetree(&dtb)?; - let src = codegen::generate_rust(&dt); - std::fs::write(out_path, src) - .map_err(|e| format!("dtgen: failed to write {}: {e}", out_path.display())) + parser::dtb_to_devicetree(&dtb) +} + +pub fn generate_rust(dt: &DeviceTree) -> String { + codegen::generate_rust(dt) +} + +pub fn generate_ld(dt: &DeviceTree) -> Result { + ldgen::generate_ld(dt) } diff --git a/xtasks/crates/dtgen/src/main.rs b/xtasks/crates/dtgen/src/main.rs index af2bd1f..e2a917e 100644 --- a/xtasks/crates/dtgen/src/main.rs +++ b/xtasks/crates/dtgen/src/main.rs @@ -25,11 +25,25 @@ struct Args { } fn main() { + logging::init(); let args = Args::parse(); let refs: Vec<&std::path::Path> = args.include_dirs.iter().map(|p| p.as_path()).collect(); - dtgen::run(&args.input, &refs, &args.output).unwrap_or_else(|e| { - eprintln!("dtgen error: {e}"); + let dt = dtgen::parse_dts(&args.input, &refs).unwrap_or_else(|e| { + log::error!("dtgen error: Failed to parse device tree: {e}"); + std::process::exit(1); + }); + + let output = args.output.as_path(); + std::fs::create_dir_all(output.parent().unwrap()).unwrap_or_else(|e| { + log::error!("dtgen error: Failed to create output directory: {e}"); + std::process::exit(1); + }); + + let content = dtgen::generate_rust(&dt); + + std::fs::write(&args.output, content).unwrap_or_else(|e| { + log::error!("dtgen error: Failed to write output file: {e}"); std::process::exit(1); }); } From 4d57b77dbaa20a3deedbe7c4b05ded06221f2848 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 17:22:42 +0000 Subject: [PATCH 18/28] simplyfied hal. --- Cargo.lock | 4 +- Cargo.toml | 2 +- build.rs | 145 ------- examples/hello-world/src/main.rs | 6 +- justfile | 3 - machine/api/src/lib.rs | 2 +- machine/api/src/mem.rs | 12 +- machine/api/src/stack.rs | 2 +- machine/arm/Cargo.toml | 3 + machine/arm/build.rs | 353 +++++++++++++++--- machine/arm/common/irq.S | 7 - machine/arm/common/ivt.S | 1 + machine/arm/options.toml | 13 - machine/arm/src/asm.rs | 6 +- machine/arm/src/crit.rs | 2 +- machine/arm/src/lib.rs | 3 +- machine/arm/src/sched.rs | 3 +- .../{stm32l4xx => st/stm32l4}/.gitattributes | 0 .../arm/{stm32l4xx => st/stm32l4}/.gitignore | 0 .../{stm32l4xx => st/stm32l4}/CMakeLists.txt | 9 +- .../arm/{stm32l4xx => st/stm32l4}/README.md | 0 .../stm32l4}/device/CMakeLists.txt | 2 +- .../stm32l4}/device/LICENSE.md | 0 .../stm32l4}/device/stm32l412xx.h | 0 .../stm32l4}/device/stm32l422xx.h | 0 .../stm32l4}/device/stm32l431xx.h | 0 .../stm32l4}/device/stm32l432xx.h | 0 .../stm32l4}/device/stm32l433xx.h | 0 .../stm32l4}/device/stm32l442xx.h | 0 .../stm32l4}/device/stm32l443xx.h | 0 .../stm32l4}/device/stm32l451xx.h | 0 .../stm32l4}/device/stm32l452xx.h | 0 .../stm32l4}/device/stm32l462xx.h | 0 .../stm32l4}/device/stm32l471xx.h | 0 .../stm32l4}/device/stm32l475xx.h | 0 .../stm32l4}/device/stm32l476xx.h | 0 .../stm32l4}/device/stm32l485xx.h | 0 .../stm32l4}/device/stm32l486xx.h | 0 .../stm32l4}/device/stm32l496xx.h | 0 .../stm32l4}/device/stm32l4a6xx.h | 0 .../stm32l4}/device/stm32l4p5xx.h | 0 .../stm32l4}/device/stm32l4q5xx.h | 0 .../stm32l4}/device/stm32l4r5xx.h | 0 .../stm32l4}/device/stm32l4r7xx.h | 0 .../stm32l4}/device/stm32l4r9xx.h | 0 .../stm32l4}/device/stm32l4s5xx.h | 0 .../stm32l4}/device/stm32l4s7xx.h | 0 .../stm32l4}/device/stm32l4s9xx.h | 0 .../stm32l4}/device/stm32l4xx.h | 0 .../stm32l4}/device/system_stm32l4xx.c | 0 .../stm32l4}/device/system_stm32l4xx.h | 0 .../stm32l4}/hal/CMakeLists.txt | 2 +- .../{stm32l4xx => st/stm32l4}/hal/LICENSE.md | 0 .../stm32l4}/hal/Legacy/stm32_hal_legacy.h | 0 .../stm32l4}/hal/Legacy/stm32l4xx_hal_can.c | 0 .../hal/Legacy/stm32l4xx_hal_can_legacy.h | 0 .../stm32l4}/hal/stm32_assert_template.h | 0 .../stm32l4}/hal/stm32l4xx_hal.c | 0 .../stm32l4}/hal/stm32l4xx_hal.h | 0 .../stm32l4}/hal/stm32l4xx_hal_adc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_adc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_adc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_adc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_can.c | 0 .../stm32l4}/hal/stm32l4xx_hal_can.h | 0 .../stm32l4}/hal/stm32l4xx_hal_comp.c | 0 .../stm32l4}/hal/stm32l4xx_hal_comp.h | 0 .../stm32l4}/hal/stm32l4xx_hal_conf.h | 0 .../stm32l4}/hal/stm32l4xx_hal_cortex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_cortex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_crc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_crc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_crc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_crc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_cryp.c | 0 .../stm32l4}/hal/stm32l4xx_hal_cryp.h | 0 .../stm32l4}/hal/stm32l4xx_hal_cryp_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_cryp_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dac.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dac.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dac_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dac_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dcmi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dcmi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_def.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dfsdm.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dfsdm.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dfsdm_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dfsdm_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dma.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dma.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dma2d.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dma2d.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dma_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dma_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_dsi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_dsi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_exti.c | 0 .../stm32l4}/hal/stm32l4xx_hal_exti.h | 0 .../stm32l4}/hal/stm32l4xx_hal_firewall.c | 0 .../stm32l4}/hal/stm32l4xx_hal_firewall.h | 0 .../stm32l4}/hal/stm32l4xx_hal_flash.c | 0 .../stm32l4}/hal/stm32l4xx_hal_flash.h | 0 .../stm32l4}/hal/stm32l4xx_hal_flash_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_flash_ex.h | 0 .../hal/stm32l4xx_hal_flash_ramfunc.c | 0 .../hal/stm32l4xx_hal_flash_ramfunc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_gfxmmu.c | 0 .../stm32l4}/hal/stm32l4xx_hal_gfxmmu.h | 0 .../stm32l4}/hal/stm32l4xx_hal_gpio.c | 0 .../stm32l4}/hal/stm32l4xx_hal_gpio.h | 0 .../stm32l4}/hal/stm32l4xx_hal_gpio_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_hash.c | 0 .../stm32l4}/hal/stm32l4xx_hal_hash.h | 0 .../stm32l4}/hal/stm32l4xx_hal_hash_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_hash_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_hcd.c | 0 .../stm32l4}/hal/stm32l4xx_hal_hcd.h | 0 .../stm32l4}/hal/stm32l4xx_hal_i2c.c | 0 .../stm32l4}/hal/stm32l4xx_hal_i2c.h | 0 .../stm32l4}/hal/stm32l4xx_hal_i2c_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_i2c_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_irda.c | 0 .../stm32l4}/hal/stm32l4xx_hal_irda.h | 0 .../stm32l4}/hal/stm32l4xx_hal_irda_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_iwdg.c | 0 .../stm32l4}/hal/stm32l4xx_hal_iwdg.h | 0 .../stm32l4}/hal/stm32l4xx_hal_lcd.c | 0 .../stm32l4}/hal/stm32l4xx_hal_lcd.h | 0 .../stm32l4}/hal/stm32l4xx_hal_lptim.c | 0 .../stm32l4}/hal/stm32l4xx_hal_lptim.h | 0 .../stm32l4}/hal/stm32l4xx_hal_ltdc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_ltdc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_ltdc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_ltdc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_mmc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_mmc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_mmc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_mmc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_nand.c | 0 .../stm32l4}/hal/stm32l4xx_hal_nand.h | 0 .../stm32l4}/hal/stm32l4xx_hal_nor.c | 0 .../stm32l4}/hal/stm32l4xx_hal_nor.h | 0 .../stm32l4}/hal/stm32l4xx_hal_opamp.c | 0 .../stm32l4}/hal/stm32l4xx_hal_opamp.h | 0 .../stm32l4}/hal/stm32l4xx_hal_opamp_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_opamp_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_ospi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_ospi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pcd.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pcd.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pcd_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pcd_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pka.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pka.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pssi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pssi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pwr.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pwr.h | 0 .../stm32l4}/hal/stm32l4xx_hal_pwr_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_pwr_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_qspi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_qspi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rcc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rcc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rcc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rcc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rng.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rng.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rng_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rng_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rtc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rtc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_rtc_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_rtc_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_sai.c | 0 .../stm32l4}/hal/stm32l4xx_hal_sai.h | 0 .../stm32l4}/hal/stm32l4xx_hal_sai_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_sai_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_sd.c | 0 .../stm32l4}/hal/stm32l4xx_hal_sd.h | 0 .../stm32l4}/hal/stm32l4xx_hal_sd_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_sd_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_smartcard.c | 0 .../stm32l4}/hal/stm32l4xx_hal_smartcard.h | 0 .../stm32l4}/hal/stm32l4xx_hal_smartcard_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_smartcard_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_smbus.c | 0 .../stm32l4}/hal/stm32l4xx_hal_smbus.h | 0 .../stm32l4}/hal/stm32l4xx_hal_smbus_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_smbus_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_spi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_spi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_spi_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_spi_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_sram.c | 0 .../stm32l4}/hal/stm32l4xx_hal_sram.h | 0 .../stm32l4}/hal/stm32l4xx_hal_swpmi.c | 0 .../stm32l4}/hal/stm32l4xx_hal_swpmi.h | 0 .../stm32l4}/hal/stm32l4xx_hal_tim.c | 0 .../stm32l4}/hal/stm32l4xx_hal_tim.h | 0 .../stm32l4}/hal/stm32l4xx_hal_tim_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_tim_ex.h | 0 .../hal/stm32l4xx_hal_timebase_tim_template.c | 0 .../stm32l4}/hal/stm32l4xx_hal_tsc.c | 0 .../stm32l4}/hal/stm32l4xx_hal_tsc.h | 0 .../stm32l4}/hal/stm32l4xx_hal_uart.c | 0 .../stm32l4}/hal/stm32l4xx_hal_uart.h | 0 .../stm32l4}/hal/stm32l4xx_hal_uart_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_uart_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_usart.c | 0 .../stm32l4}/hal/stm32l4xx_hal_usart.h | 0 .../stm32l4}/hal/stm32l4xx_hal_usart_ex.c | 0 .../stm32l4}/hal/stm32l4xx_hal_usart_ex.h | 0 .../stm32l4}/hal/stm32l4xx_hal_wwdg.c | 0 .../stm32l4}/hal/stm32l4xx_hal_wwdg.h | 0 .../stm32l4}/hal/stm32l4xx_ll_adc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_adc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_bus.h | 0 .../stm32l4}/hal/stm32l4xx_ll_comp.c | 0 .../stm32l4}/hal/stm32l4xx_ll_comp.h | 0 .../stm32l4}/hal/stm32l4xx_ll_cortex.h | 0 .../stm32l4}/hal/stm32l4xx_ll_crc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_crc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_crs.c | 0 .../stm32l4}/hal/stm32l4xx_ll_crs.h | 0 .../stm32l4}/hal/stm32l4xx_ll_dac.c | 0 .../stm32l4}/hal/stm32l4xx_ll_dac.h | 0 .../stm32l4}/hal/stm32l4xx_ll_dma.c | 0 .../stm32l4}/hal/stm32l4xx_ll_dma.h | 0 .../stm32l4}/hal/stm32l4xx_ll_dma2d.c | 0 .../stm32l4}/hal/stm32l4xx_ll_dma2d.h | 0 .../stm32l4}/hal/stm32l4xx_ll_dmamux.h | 0 .../stm32l4}/hal/stm32l4xx_ll_exti.c | 0 .../stm32l4}/hal/stm32l4xx_ll_exti.h | 0 .../stm32l4}/hal/stm32l4xx_ll_fmc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_fmc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_gpio.c | 0 .../stm32l4}/hal/stm32l4xx_ll_gpio.h | 0 .../stm32l4}/hal/stm32l4xx_ll_i2c.c | 0 .../stm32l4}/hal/stm32l4xx_ll_i2c.h | 0 .../stm32l4}/hal/stm32l4xx_ll_iwdg.h | 0 .../stm32l4}/hal/stm32l4xx_ll_lptim.c | 0 .../stm32l4}/hal/stm32l4xx_ll_lptim.h | 0 .../stm32l4}/hal/stm32l4xx_ll_lpuart.c | 0 .../stm32l4}/hal/stm32l4xx_ll_lpuart.h | 0 .../stm32l4}/hal/stm32l4xx_ll_opamp.c | 0 .../stm32l4}/hal/stm32l4xx_ll_opamp.h | 0 .../stm32l4}/hal/stm32l4xx_ll_pka.c | 0 .../stm32l4}/hal/stm32l4xx_ll_pka.h | 0 .../stm32l4}/hal/stm32l4xx_ll_pwr.c | 0 .../stm32l4}/hal/stm32l4xx_ll_pwr.h | 0 .../stm32l4}/hal/stm32l4xx_ll_rcc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_rcc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_rng.c | 0 .../stm32l4}/hal/stm32l4xx_ll_rng.h | 0 .../stm32l4}/hal/stm32l4xx_ll_rtc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_rtc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_sdmmc.c | 0 .../stm32l4}/hal/stm32l4xx_ll_sdmmc.h | 0 .../stm32l4}/hal/stm32l4xx_ll_spi.c | 0 .../stm32l4}/hal/stm32l4xx_ll_spi.h | 0 .../stm32l4}/hal/stm32l4xx_ll_swpmi.c | 0 .../stm32l4}/hal/stm32l4xx_ll_swpmi.h | 0 .../stm32l4}/hal/stm32l4xx_ll_system.h | 0 .../stm32l4}/hal/stm32l4xx_ll_tim.c | 0 .../stm32l4}/hal/stm32l4xx_ll_tim.h | 0 .../stm32l4}/hal/stm32l4xx_ll_usart.c | 0 .../stm32l4}/hal/stm32l4xx_ll_usart.h | 0 .../stm32l4}/hal/stm32l4xx_ll_usb.c | 0 .../stm32l4}/hal/stm32l4xx_ll_usb.h | 0 .../stm32l4}/hal/stm32l4xx_ll_utils.c | 0 .../stm32l4}/hal/stm32l4xx_ll_utils.h | 0 .../stm32l4}/hal/stm32l4xx_ll_wwdg.h | 0 .../stm32l4}/interface/CMakeLists.txt | 2 +- .../stm32l4}/interface/README.md | 0 .../stm32l4}/interface/clock.c | 0 .../stm32l4}/interface/export.h | 0 .../stm32l4}/interface/instru.c | 0 .../{stm32l4xx => st/stm32l4}/interface/lib.c | 0 .../{stm32l4xx => st/stm32l4}/interface/lib.h | 0 .../stm32l4}/interface/sched.c | 0 .../stm32l4}/interface/uart.c | 0 machine/arm/st/stm32l4/link.ld | 129 +++++++ machine/arm/stm32l4xx/link.ld | 222 ----------- machine/arm/stm32l4xx/options.toml | 9 - machine/arm/stm32l4xx/r5zi/CMakeLists.txt | 14 - machine/arm/stm32l4xx/r5zi/lib.c | 197 ---------- macros/src/lib.rs | 22 +- macros/src/logging.rs | 2 +- macros/src/syscall.rs | 6 +- macros/src/tree.rs | 31 +- presets/stm32l4r5zi_def.toml | 5 +- src/error.rs | 4 +- src/idle.rs | 6 +- src/lib.rs | 11 +- src/macros.rs | 4 +- src/mem.rs | 24 +- src/mem/alloc.rs | 8 +- src/mem/alloc/bestfit.rs | 34 +- src/mem/pfa.rs | 6 +- src/mem/pfa/bitset.rs | 28 +- src/mem/vmm.rs | 10 +- src/mem/vmm/nommu.rs | 5 +- src/sched/dispch.rs | 2 +- src/sched/rr.rs | 15 +- src/sched/rt.rs | 22 +- src/syscalls/sched.rs | 1 - src/time.rs | 2 +- src/types.rs | 7 +- src/types/array.rs | 45 +-- src/types/list.rs | 27 +- src/types/rbtree.rs | 75 +++- src/types/traits.rs | 19 +- src/types/view.rs | 11 +- src/uapi.rs | 2 +- src/uapi/print.rs | 2 +- src/uapi/sched.rs | 12 +- src/uapi/time.rs | 2 +- xtasks/crates/config/src/main.rs | 7 +- xtasks/crates/dtgen/src/ldgen.rs | 28 +- xtasks/crates/dtgen/src/lib.rs | 2 +- 322 files changed, 778 insertions(+), 862 deletions(-) rename machine/arm/{stm32l4xx => st/stm32l4}/.gitattributes (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/.gitignore (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/CMakeLists.txt (84%) rename machine/arm/{stm32l4xx => st/stm32l4}/README.md (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/CMakeLists.txt (95%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/LICENSE.md (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l412xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l422xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l431xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l432xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l433xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l442xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l443xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l451xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l452xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l462xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l471xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l475xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l476xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l485xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l486xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l496xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4a6xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4p5xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4q5xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4r5xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4r7xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4r9xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4s5xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4s7xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4s9xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/stm32l4xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/system_stm32l4xx.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/device/system_stm32l4xx.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/CMakeLists.txt (97%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/LICENSE.md (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/Legacy/stm32_hal_legacy.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/Legacy/stm32l4xx_hal_can.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/Legacy/stm32l4xx_hal_can_legacy.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32_assert_template.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_adc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_adc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_adc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_adc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_can.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_can.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_comp.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_comp.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_conf.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cortex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cortex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_crc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_crc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_crc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_crc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cryp.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cryp.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cryp_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_cryp_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dac.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dac.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dac_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dac_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dcmi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dcmi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_def.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dfsdm.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dfsdm.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dfsdm_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dfsdm_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma2d.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma2d.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dma_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dsi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_dsi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_exti.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_exti.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_firewall.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_firewall.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash_ramfunc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_flash_ramfunc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_gfxmmu.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_gfxmmu.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_gpio.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_gpio.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_gpio_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hash.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hash.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hash_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hash_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hcd.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_hcd.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_i2c.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_i2c.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_i2c_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_i2c_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_irda.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_irda.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_irda_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_iwdg.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_iwdg.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_lcd.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_lcd.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_lptim.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_lptim.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ltdc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ltdc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ltdc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ltdc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_mmc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_mmc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_mmc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_mmc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_nand.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_nand.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_nor.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_nor.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_opamp.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_opamp.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_opamp_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_opamp_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ospi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_ospi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pcd.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pcd.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pcd_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pcd_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pka.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pka.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pssi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pssi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pwr.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pwr.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pwr_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_pwr_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_qspi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_qspi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rcc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rcc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rcc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rcc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rng.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rng.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rng_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rng_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rtc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rtc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rtc_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_rtc_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sai.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sai.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sai_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sai_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sd.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sd.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sd_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sd_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smartcard.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smartcard.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smartcard_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smartcard_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smbus.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smbus.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smbus_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_smbus_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_spi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_spi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_spi_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_spi_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sram.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_sram.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_swpmi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_swpmi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tim.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tim.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tim_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tim_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_timebase_tim_template.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tsc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_tsc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_uart.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_uart.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_uart_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_uart_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_usart.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_usart.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_usart_ex.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_usart_ex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_wwdg.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_hal_wwdg.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_adc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_adc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_bus.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_comp.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_comp.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_cortex.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_crc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_crc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_crs.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_crs.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dac.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dac.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dma.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dma.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dma2d.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dma2d.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_dmamux.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_exti.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_exti.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_fmc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_fmc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_gpio.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_gpio.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_i2c.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_i2c.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_iwdg.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_lptim.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_lptim.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_lpuart.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_lpuart.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_opamp.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_opamp.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_pka.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_pka.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_pwr.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_pwr.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rcc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rcc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rng.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rng.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rtc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_rtc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_sdmmc.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_sdmmc.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_spi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_spi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_swpmi.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_swpmi.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_system.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_tim.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_tim.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_usart.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_usart.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_usb.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_usb.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_utils.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_utils.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/hal/stm32l4xx_ll_wwdg.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/CMakeLists.txt (96%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/README.md (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/clock.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/export.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/instru.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/lib.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/lib.h (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/sched.c (100%) rename machine/arm/{stm32l4xx => st/stm32l4}/interface/uart.c (100%) create mode 100644 machine/arm/st/stm32l4/link.ld delete mode 100644 machine/arm/stm32l4xx/link.ld delete mode 100644 machine/arm/stm32l4xx/options.toml delete mode 100644 machine/arm/stm32l4xx/r5zi/CMakeLists.txt delete mode 100644 machine/arm/stm32l4xx/r5zi/lib.c diff --git a/Cargo.lock b/Cargo.lock index e0af60e..ae3631c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -771,8 +771,11 @@ dependencies = [ "cbindgen", "cmake", "critical-section", + "dtgen", "hal-api", + "quote", "serde_json", + "syn", ] [[package]] @@ -1144,7 +1147,6 @@ dependencies = [ "cfg_aliases", "defmt", "defmt-rtt", - "dtgen", "envparse", "hal-select", "hal-testing", diff --git a/Cargo.toml b/Cargo.toml index e451399..0e3d8f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ default-members = ["."] [workspace.dependencies] logging = { path = "xtasks/logging" } +dtgen = { path = "xtasks/crates/dtgen" } osiris = { path = "." } [package] @@ -49,7 +50,6 @@ syn = "2.0.100" quote = "1.0.40" rand = "0.8.5" cfg_aliases = "0.2.1" -dtgen = { path = "xtasks/crates/dtgen" } [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/build.rs b/build.rs index 27c4cfa..f37cb9a 100644 --- a/build.rs +++ b/build.rs @@ -23,156 +23,11 @@ fn main() { panic!("Failed to generate syscall match statement."); } - let dt = build_device_tree(Path::new(&out_dir)).unwrap_or_else(|e| { - panic!("Failed to build device tree from DTS files: {e}"); - }); - - if let Err(e) = generate_device_tree(&dt, Path::new(&out_dir)) { - panic!("Failed to generate device tree scripts: {e}"); - } - cfg_aliases! { freestanding: { all(not(test), not(doctest), not(doc), not(kani), any(target_os = "none", target_os = "unknown")) }, } } -// Device Tree Codegen ---------------------------------------------------------------------------- - -fn generate_device_tree(dt: &dtgen::ir::DeviceTree, out: &Path) -> Result<(), Box> { - let rust_content = dtgen::generate_rust(dt); - std::fs::write(out.join("device_tree.rs"), rust_content)?; - - let ld_content = dtgen::generate_ld(dt).map_err(|e| format!("linker script generation failed: {e}"))?; - std::fs::write(out.join("prelude.ld"), ld_content)?; - println!("cargo::rustc-link-search=native={}", out.display()); - Ok(()) -} - -fn build_device_tree(out: &Path) -> Result> { - let dts = - std::env::var("OSIRIS_TUNING_DTS").unwrap_or_else(|_| "nucleo_l4r5zi.dts".to_string()); - let dts_path = std::path::Path::new("boards").join(dts); - println!("cargo::rerun-if-changed={}", dts_path.display()); - - // dependencies SoC/HAL/pins - let zephyr = Path::new(out).join("zephyr"); - let hal_stm32 = Path::new(out).join("hal_stm32"); - - // clean state - if zephyr.exists() { - std::fs::remove_dir_all(&zephyr)?; - } - - if hal_stm32.exists() { - std::fs::remove_dir_all(&hal_stm32)?; - } - - sparse_clone( - "https://github.com/zephyrproject-rtos/zephyr", - &zephyr, - // the west.yaml file is a manifest to manage/pin subprojects used for a specific zephyr - // release - &["include", "dts", "boards", "west.yaml"], - Some("v4.3.0"), - )?; - - // retrieve from manifest - let hal_rev = get_hal_revision(&zephyr)?; - println!("cargo:warning=Detected hal_stm32 revision: {hal_rev}"); - - sparse_clone( - "https://github.com/zephyrproject-rtos/hal_stm32", - &hal_stm32, - &["dts"], - Some(&hal_rev), - )?; - - //let out = Path::new(&std::env::var("OUT_DIR").unwrap()).join("device_tree.rs"); - let include_paths = [ - zephyr.join("include"), - zephyr.join("dts/arm/st"), - zephyr.join("dts/arm/st/l4"), - zephyr.join("dts"), - zephyr.join("dts/arm"), - zephyr.join("dts/common"), - zephyr.join("boards/st"), - hal_stm32.join("dts"), - hal_stm32.join("dts/st"), - ]; - let include_refs: Vec<&Path> = include_paths.iter().map(PathBuf::as_path).collect(); - - for path in &include_paths { - if !path.exists() { - println!("cargo:warning=MISSING INCLUDE PATH: {:?}", path); - } - } - - Ok(dtgen::parse_dts(&dts_path, &include_refs)?) -} - -fn get_hal_revision(zephyr_path: &Path) -> Result> { - let west_yml = fs::read_to_string(zephyr_path.join("west.yml"))?; - let mut in_hal_stm32_block = false; - - for line in west_yml.lines() { - let trimmed = line.trim(); - - // Check if we've entered the hal_stm32 section - if trimmed == "- name: hal_stm32" || trimmed == "name: hal_stm32" { - in_hal_stm32_block = true; - continue; - } - - // If we are in the block, look for the revision - if in_hal_stm32_block { - if trimmed.starts_with("revision:") { - return Ok(trimmed.replace("revision:", "").trim().to_string()); - } - - // If we hit a new project name before finding a revision, something is wrong - if trimmed.starts_with("- name:") || trimmed.starts_with("name:") { - in_hal_stm32_block = false; - } - } - } - - Err("Could not find hal_stm32 revision in west.yml".into()) -} - -fn sparse_clone( - url: &str, - dest: &Path, - paths: &[&str], - revision: Option<&str>, -) -> Result<(), Box> { - Command::new("git") - .args(["clone", "--filter=blob:none", "--no-checkout", url]) - .arg(dest) - .status()?; - - Command::new("git") - .args(["sparse-checkout", "init", "--cone"]) - .current_dir(dest) - .status()?; - - Command::new("git") - .arg("sparse-checkout") - .arg("set") - .args(paths) - .current_dir(dest) - .status()?; - - let mut checkout = Command::new("git"); - checkout.current_dir(dest).arg("checkout"); - - if let Some(rev) = revision { - checkout.arg(rev); - } - - checkout.status()?; - Ok(()) -} - // Syscalls --------------------------------------------------------------------------------------- fn gen_syscall_match(root: &Path, out: &Path) -> Result<(), std::io::Error> { diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 4bad2e0..6a78100 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -18,7 +18,11 @@ extern "C" fn second_thread() { fn main() { osiris::uprintln!("Hello World!"); let mut tick = 0; - let attrs = osiris::uapi::sched::RtAttrs { deadline: 100, period: 100, budget: 100 }; + let attrs = osiris::uapi::sched::RtAttrs { + deadline: 100, + period: 100, + budget: 100, + }; osiris::uapi::sched::spawn_thread(second_thread, Some(attrs)); loop { diff --git a/justfile b/justfile index b14c0c4..6e4afb1 100644 --- a/justfile +++ b/justfile @@ -5,9 +5,6 @@ build *args: config *args: cargo xtask config --root {{justfile_directory()}} {{args}} -pack *args: - cargo xtask pack {{args}} - example name *args: (build args) cargo build -p {{name}} {{args}} diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index f4ebc52..356436d 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -32,7 +32,7 @@ impl Display for Error { "Pointer {:p} out of bounds (expected in {:p}..{:p})", *ptr as *const u8, range.start as *const u8, range.end as *const u8 ) - }, + } Error::InvalidAddress(addr) => write!(f, "Invalid address {:p}", *addr as *const u8), } } diff --git a/machine/api/src/mem.rs b/machine/api/src/mem.rs index d3a6223..a658104 100644 --- a/machine/api/src/mem.rs +++ b/machine/api/src/mem.rs @@ -1,4 +1,8 @@ -use core::{fmt::{Display, LowerHex, UpperHex}, ops::{Add, Div, Rem, Sub}, ptr::NonNull}; +use core::{ + fmt::{Display, LowerHex, UpperHex}, + ops::{Add, Div, Rem, Sub}, + ptr::NonNull, +}; #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] @@ -30,7 +34,7 @@ impl PhysAddr { } pub fn is_multiple_of(&self, align: usize) -> bool { - self.0.is_multiple_of(align) + self.0.is_multiple_of(align) } pub fn diff(&self, other: Self) -> usize { @@ -114,7 +118,7 @@ impl UpperHex for PhysAddr { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct VirtAddr(usize); +pub struct VirtAddr(usize); impl VirtAddr { #[inline] @@ -143,4 +147,4 @@ impl From for usize { fn from(addr: VirtAddr) -> Self { addr.0 } -} \ No newline at end of file +} diff --git a/machine/api/src/stack.rs b/machine/api/src/stack.rs index 11604f1..d915e0d 100644 --- a/machine/api/src/stack.rs +++ b/machine/api/src/stack.rs @@ -1,5 +1,5 @@ -use core::{ffi::c_void, num::NonZero}; use crate::{Result, mem::PhysAddr}; +use core::{ffi::c_void, num::NonZero}; pub type EntryFn = extern "C" fn(); pub type FinFn = extern "C" fn() -> !; diff --git a/machine/arm/Cargo.toml b/machine/arm/Cargo.toml index bafea9d..574d840 100644 --- a/machine/arm/Cargo.toml +++ b/machine/arm/Cargo.toml @@ -18,6 +18,9 @@ bindgen = "0.72.0" cmake = "0.1.54" anyhow = "1.0.99" serde_json = "1.0.145" +quote = "1.0.26" +syn = { version = "2.0.36", features = ["full"] } +dtgen = { workspace = true } [features] # This enables the host implementation of the HAL. Mainly used for testing. diff --git a/machine/arm/build.rs b/machine/arm/build.rs index 57df5f2..06e5ea7 100644 --- a/machine/arm/build.rs +++ b/machine/arm/build.rs @@ -16,7 +16,12 @@ use anyhow::{Context, Result}; use cmake::Config; -use std::{env, fs, path::PathBuf, process::Command}; +use core::panic; +use std::{ + env, fs, + path::{Path, PathBuf}, + process::Command, +}; /// Determines the host target triple by querying the Rust compiler. /// @@ -209,16 +214,16 @@ fn forward_fpu_config(config: &mut Config) -> Result<()> { /// - Header file cannot be found or parsed /// - Binding generation fails /// - Output file cannot be written -fn generate_bindings(out: &str, hal: &str) -> Result<()> { +fn generate_bindings(out: &Path, hal: &Path) -> Result<()> { let bindgen = bindgen::Builder::default() - .header(format!("{hal}/interface/export.h")) + .header(hal.join("interface").join("export.h").to_str().unwrap()) .use_core() .wrap_unsafe_ops(true) .generate()?; - bindgen.write_to_file(format!("{out}/bindings.rs"))?; + bindgen.write_to_file(out.join("bindings.rs"))?; - println!("cargo::rerun-if-changed={hal}"); + println!("cargo::rerun-if-changed={}", hal.display()); Ok(()) } @@ -306,11 +311,213 @@ fn merge_compile_commands(files: &[String]) -> String { fn workspace_dir() -> Option { let output = Command::new("cargo") .args(["locate-project", "--workspace", "--message-format=plain"]) - .output().ok()?; + .output() + .ok()?; let path = String::from_utf8(output.stdout).expect("utf8"); Some(PathBuf::from(path.trim()).parent()?.to_path_buf()) } +mod vector_table { + pub fn generate() -> String { + let entries: Vec<_> = (0..240) + .map(|i| quote::format_ident!("__irq_{i}_handler")) + .collect(); + + quote::quote! { + unsafe extern "C" { + #( + fn #entries(); + )* + } + + #[repr(C)] + struct ExternalVectorTable { + entries: [unsafe extern "C" fn(); 240], + } + + #[unsafe(link_section = ".ivt.ext")] + #[used] + static EXTERNAL_VECTOR_TABLE: ExternalVectorTable = ExternalVectorTable { + entries: [ + #(#entries),* + ], + }; + } + .to_string() + } +} + +// Device Tree Codegen ---------------------------------------------------------------------------- + +mod dt { + use std::{ + fs, + path::{Path, PathBuf}, + process::Command, + }; + + use crate::workspace_dir; + + /// Returns the compatible (vendor, name) tuple for the SoC node in the device tree. + pub fn soc(dt: &dtgen::ir::DeviceTree) -> Vec<(&str, &str)> { + let soc_node = dt + .nodes + .iter() + .find(|n| n.name == "soc") + .expect("Device tree must have a soc node"); + + soc_node + .compatible + .iter() + .filter_map(|s| { + let parts: Vec<&str> = s.split(',').collect(); + if parts.len() == 2 { + Some((parts[0], parts[1])) + } else { + None + } + }) + .collect() + } + + pub fn generate_device_tree( + dt: &dtgen::ir::DeviceTree, + out: &Path, + ) -> Result<(), Box> { + let rust_content = dtgen::generate_rust(dt); + std::fs::write(out.join("device_tree.rs"), rust_content)?; + + let ld_content = + dtgen::generate_ld(dt).map_err(|e| format!("linker script generation failed: {e}"))?; + std::fs::write(out.join("prelude.ld"), ld_content)?; + println!("cargo::rustc-link-search=native={}", out.display()); + Ok(()) + } + + pub fn build_device_tree( + out: &Path, + ) -> Result> { + let dts = std::env::var("OSIRIS_TUNING_DTS") + .expect("OSIRIS_TUNING_DTS environment variable not set"); + let workspace_root = workspace_dir().ok_or("Could not determine workspace root")?; + let dts_path = workspace_root.join("boards").join(dts); + println!("cargo::rerun-if-changed={}", dts_path.display()); + + // dependencies SoC/HAL/pins + let zephyr = Path::new(out).join("zephyr"); + let hal_stm32 = Path::new(out).join("hal_stm32"); + + if !zephyr.exists() { + sparse_clone( + "https://github.com/zephyrproject-rtos/zephyr", + &zephyr, + // the west.yaml file is a manifest to manage/pin subprojects used for a specific zephyr + // release + &["include", "dts", "boards", "west.yaml"], + Some("v4.3.0"), + )?; + } + + if !hal_stm32.exists() { + // retrieve from manifest + let hal_rev = get_hal_revision(&zephyr)?; + println!("cargo:warning=Detected hal_stm32 revision: {hal_rev}"); + + sparse_clone( + "https://github.com/zephyrproject-rtos/hal_stm32", + &hal_stm32, + &["dts"], + Some(&hal_rev), + )?; + } + + //let out = Path::new(&std::env::var("OUT_DIR").unwrap()).join("device_tree.rs"); + let include_paths = [ + zephyr.join("include"), + zephyr.join("dts/arm/st"), + zephyr.join("dts/arm/st/l4"), + zephyr.join("dts"), + zephyr.join("dts/arm"), + zephyr.join("dts/common"), + zephyr.join("boards/st"), + hal_stm32.join("dts"), + hal_stm32.join("dts/st"), + ]; + let include_refs: Vec<&Path> = include_paths.iter().map(PathBuf::as_path).collect(); + + for path in &include_paths { + if !path.exists() { + println!("cargo:warning=MISSING INCLUDE PATH: {:?}", path); + } + } + + Ok(dtgen::parse_dts(&dts_path, &include_refs)?) + } + + fn get_hal_revision(zephyr_path: &Path) -> Result> { + let west_yml = fs::read_to_string(zephyr_path.join("west.yml"))?; + let mut in_hal_stm32_block = false; + + for line in west_yml.lines() { + let trimmed = line.trim(); + + // Check if we've entered the hal_stm32 section + if trimmed == "- name: hal_stm32" || trimmed == "name: hal_stm32" { + in_hal_stm32_block = true; + continue; + } + + // If we are in the block, look for the revision + if in_hal_stm32_block { + if trimmed.starts_with("revision:") { + return Ok(trimmed.replace("revision:", "").trim().to_string()); + } + + // If we hit a new project name before finding a revision, something is wrong + if trimmed.starts_with("- name:") || trimmed.starts_with("name:") { + in_hal_stm32_block = false; + } + } + } + + Err("Could not find hal_stm32 revision in west.yml".into()) + } + + fn sparse_clone( + url: &str, + dest: &Path, + paths: &[&str], + revision: Option<&str>, + ) -> Result<(), Box> { + Command::new("git") + .args(["clone", "--filter=blob:none", "--no-checkout", url]) + .arg(dest) + .status()?; + + Command::new("git") + .args(["sparse-checkout", "init", "--cone"]) + .current_dir(dest) + .status()?; + + Command::new("git") + .arg("sparse-checkout") + .arg("set") + .args(paths) + .current_dir(dest) + .status()?; + + let mut checkout = Command::new("git"); + checkout.current_dir(dest).arg("checkout"); + + if let Some(rev) = revision { + checkout.arg(rev); + } + + checkout.status()?; + Ok(()) + } +} + /// Main build script entry point. /// /// This function orchestrates the entire build process: @@ -337,60 +544,92 @@ fn workspace_dir() -> Option { /// Exits with error code 1 if any critical build step fails fn main() { let out = env::var("OUT_DIR").unwrap(); - println!("cargo::rustc-link-search={out}"); - - let hal = fail_on_error(env::var("OSIRIS_ARM_HAL").with_context( - || "OSIRIS_ARM_HAL environment variable not set. Please set it to the path of the ARM HAL.", - )); + let out = Path::new(&out); + println!("cargo::rustc-link-search={}", out.display()); - fail_on_error(generate_bindings(&out, &hal)); - fail_on_error(set_arm_core_cfg()); + let dt = dt::build_device_tree(out).unwrap_or_else(|e| { + panic!("Failed to build device tree from DTS files: {e}"); + }); - // Only build when we are not on the host - if check_for_host() { - return; + if let Err(e) = dt::generate_device_tree(&dt, out) { + panic!("Failed to generate device tree scripts: {e}"); } - let build_dir = PathBuf::from(&out).join("build"); - - // Build the HAL library - let mut libhal_config = cmake::Config::new(&hal); - libhal_config.generator("Ninja"); - libhal_config.define("OUT_DIR", out.clone()); - libhal_config.always_configure(true); - forward_env_vars(&mut libhal_config); - fail_on_error(forward_fpu_config(&mut libhal_config)); - let libhal = libhal_config.build(); - - println!("cargo::rustc-link-search=native={}", libhal.display()); - println!("cargo::rerun-if-changed={out}/link.ld"); - - // Extract compile commands for HAL - let hal_cc = build_dir.join("compile_commands.json"); - let hal_cc = fs::read_to_string(hal_cc).unwrap_or_default(); - - // Build the common library - let mut common_config = cmake::Config::new("common"); - common_config.generator("Ninja"); - common_config.always_configure(true); - forward_env_vars(&mut common_config); - fail_on_error(forward_fpu_config(&mut common_config)); - let common = common_config.build(); - - // Extract compile commands for common - let common_cc = build_dir.join("compile_commands.json"); - let common_cc = fs::read_to_string(common_cc).unwrap_or_default(); - - println!("cargo::rerun-if-changed=common"); - println!("cargo::rustc-link-search=native={}", common.display()); - - // Merge and export compile_commands.json for IDE integration - let merged = merge_compile_commands(&[hal_cc, common_cc]); - - if let Some(project_root) = workspace_dir() { - let out_file = project_root.join("compile_commands.json"); - fs::write(out_file, merged).expect("write merged compile_commands.json"); - } else { - println!("cargo::warning=Could not determine workspace root, skipping compile_commands.json generation."); + for (vendor, name) in dt::soc(&dt) { + let hal = Path::new(vendor).join(name); + + if hal.exists() { + fail_on_error(generate_bindings(&out, &hal)); + fail_on_error(set_arm_core_cfg()); + + let vector_code = vector_table::generate(); + + if let Err(e) = fs::write(PathBuf::from(&out).join("vector_table.rs"), vector_code) { + println!("cargo::error=Failed to write vector_table.rs: {e}"); + std::process::exit(1); + } + + // Only build when we are not on the host + if check_for_host() { + return; + } + + let build_dir = PathBuf::from(&out).join("build"); + + // Build the HAL library + let mut libhal_config = cmake::Config::new(&hal); + libhal_config.generator("Ninja"); + libhal_config.define("OUT_DIR", out); + + for (vendor, name) in dt::soc(&dt) { + if vendor == "st" { + libhal_config.cflag(format!("-D{}xx", name.to_uppercase())); + } + libhal_config.cflag(format!("-D{}", name.to_uppercase())); + } + + libhal_config.always_configure(true); + forward_env_vars(&mut libhal_config); + fail_on_error(forward_fpu_config(&mut libhal_config)); + let libhal = libhal_config.build(); + + println!("cargo::rustc-link-search=native={}", libhal.display()); + println!("cargo::rerun-if-changed={}/link.ld", out.display()); + + // Extract compile commands for HAL + let hal_cc = build_dir.join("compile_commands.json"); + let hal_cc = fs::read_to_string(hal_cc).unwrap_or_default(); + + // Build the common library + let mut common_config = cmake::Config::new("common"); + common_config.generator("Ninja"); + common_config.always_configure(true); + forward_env_vars(&mut common_config); + fail_on_error(forward_fpu_config(&mut common_config)); + let common = common_config.build(); + + // Extract compile commands for common + let common_cc = build_dir.join("compile_commands.json"); + let common_cc = fs::read_to_string(common_cc).unwrap_or_default(); + + println!("cargo::rerun-if-changed=common"); + println!("cargo::rustc-link-search=native={}", common.display()); + + // Merge and export compile_commands.json for IDE integration + let merged = merge_compile_commands(&[hal_cc, common_cc]); + + if let Some(project_root) = workspace_dir() { + let out_file = project_root.join("compile_commands.json"); + fs::write(out_file, merged).expect("write merged compile_commands.json"); + } else { + println!( + "cargo::warning=Could not determine workspace root, skipping compile_commands.json generation." + ); + } + + return; + } } + + panic!("No compatible SoC found in device tree"); } diff --git a/machine/arm/common/irq.S b/machine/arm/common/irq.S index 7887fe8..e070711 100644 --- a/machine/arm/common/irq.S +++ b/machine/arm/common/irq.S @@ -64,12 +64,6 @@ pendsv_hndlr: bx lr .align 2 -.thumb_func -.global irq_enter_no_switch_fp -irq_enter_no_switch_fp: - @ Do nothing, just return. - bx lr - .thumb_func .global svc_hndlr svc_hndlr: @@ -82,7 +76,6 @@ svc_hndlr: @ We don't need to save the other general purpose registers, because the syscall obeys the AAPCS calling convention. b _syscall_hndlr - .thumb_func .global hard_fault_hndlr hard_fault_hndlr: diff --git a/machine/arm/common/ivt.S b/machine/arm/common/ivt.S index 7928463..5d9fc36 100644 --- a/machine/arm/common/ivt.S +++ b/machine/arm/common/ivt.S @@ -37,5 +37,6 @@ vector_table: .text .align +hndl_weak_endl default_handler hndl_weak_endl nmi_hndlr hndl_weak_endl debug_mon_hndlr \ No newline at end of file diff --git a/machine/arm/options.toml b/machine/arm/options.toml index 8278de4..17de70d 100644 --- a/machine/arm/options.toml +++ b/machine/arm/options.toml @@ -6,16 +6,3 @@ name = "ARM" description = "Options for ARM-based microcontrollers." depends_on = [{ key = ".arch", value = "arm" }] attributes = ["skip"] - -[arm.hal] -name = "Selected HAL" -description = "The hardware abstraction layer that will be used." -type = { type = "String", allowed_values = ["stm32l4xx"] } -default = "stm32l4xx" -attributes = ["no_hidden_preview"] - -[arm.stm32l4xx] -name = "Hardware" -description = "Config for the STM32L4xx microcontroller series." -depends_on = [{ key = "hal", value = "stm32l4xx" }] -attributes = ["no_hidden_preview"] diff --git a/machine/arm/src/asm.rs b/machine/arm/src/asm.rs index 1ba1ebb..bb1c311 100644 --- a/machine/arm/src/asm.rs +++ b/machine/arm/src/asm.rs @@ -121,7 +121,7 @@ pub fn disable_irq_save() -> usize { let old: usize; - unsafe { + unsafe { asm!( "mrs {old}, primask", "cpsid i", @@ -161,8 +161,8 @@ pub fn are_interrupts_enabled() -> bool { #[inline(always)] pub fn enable_irq_restr(state: usize) { use core::arch::asm; - - unsafe { + + unsafe { asm!( "dsb", "msr primask, {state}", diff --git a/machine/arm/src/crit.rs b/machine/arm/src/crit.rs index 134f2f5..51c8384 100644 --- a/machine/arm/src/crit.rs +++ b/machine/arm/src/crit.rs @@ -11,4 +11,4 @@ unsafe impl critical_section::Impl for CriticalSection { unsafe fn release(token: RawRestoreState) { crate::asm::enable_irq_restr(token); } -} \ No newline at end of file +} diff --git a/machine/arm/src/lib.rs b/machine/arm/src/lib.rs index 46ba0b9..3c22791 100644 --- a/machine/arm/src/lib.rs +++ b/machine/arm/src/lib.rs @@ -25,9 +25,10 @@ mod bindings { #[link(name = "device_native")] #[link(name = "hal_native")] #[link(name = "interface_native")] -#[link(name = "variant_native")] unsafe extern "C" {} +include!(concat!(env!("OUT_DIR"), "/vector_table.rs")); + pub struct ArmMachine; impl hal_api::Machinelike for ArmMachine { diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index 9521f33..5a4f98d 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -203,7 +203,8 @@ impl hal_api::stack::Stacklike for ArmStack { } = desc; // We expect a PhysAddr, which can be converted to a ptr on nommu. - let top = NonNull::new(top.as_mut_ptr::()).ok_or(hal_api::Error::InvalidAddress(top.as_usize()))?; + let top = NonNull::new(top.as_mut_ptr::()) + .ok_or(hal_api::Error::InvalidAddress(top.as_usize()))?; let mut stack = Self { top, diff --git a/machine/arm/stm32l4xx/.gitattributes b/machine/arm/st/stm32l4/.gitattributes similarity index 100% rename from machine/arm/stm32l4xx/.gitattributes rename to machine/arm/st/stm32l4/.gitattributes diff --git a/machine/arm/stm32l4xx/.gitignore b/machine/arm/st/stm32l4/.gitignore similarity index 100% rename from machine/arm/stm32l4xx/.gitignore rename to machine/arm/st/stm32l4/.gitignore diff --git a/machine/arm/stm32l4xx/CMakeLists.txt b/machine/arm/st/stm32l4/CMakeLists.txt similarity index 84% rename from machine/arm/stm32l4xx/CMakeLists.txt rename to machine/arm/st/stm32l4/CMakeLists.txt index 9410027..6e28844 100644 --- a/machine/arm/stm32l4xx/CMakeLists.txt +++ b/machine/arm/st/stm32l4/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.28) -set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/toolchain.cmake) +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/toolchain.cmake) project(HAL C ASM) @@ -30,9 +30,6 @@ foreach(var ${_cache_vars}) endif() endforeach() -# this will compile our variant_stm32l4xx library -add_subdirectory(${OSIRIS_ARM_STM32L4XX_VARIANT}) - # HAL add_subdirectory(hal) add_subdirectory(device) @@ -64,6 +61,6 @@ target_link_libraries(hal_native PUBLIC device_native) # Interface add_subdirectory(interface) -target_link_libraries(interface_native PUBLIC hal_native variant_native) +target_link_libraries(interface_native PUBLIC hal_native) -install(TARGETS interface_native hal_native device_native variant_native DESTINATION .) +install(TARGETS interface_native hal_native device_native DESTINATION .) diff --git a/machine/arm/stm32l4xx/README.md b/machine/arm/st/stm32l4/README.md similarity index 100% rename from machine/arm/stm32l4xx/README.md rename to machine/arm/st/stm32l4/README.md diff --git a/machine/arm/stm32l4xx/device/CMakeLists.txt b/machine/arm/st/stm32l4/device/CMakeLists.txt similarity index 95% rename from machine/arm/stm32l4xx/device/CMakeLists.txt rename to machine/arm/st/stm32l4/device/CMakeLists.txt index b57e2d4..e71e957 100644 --- a/machine/arm/stm32l4xx/device/CMakeLists.txt +++ b/machine/arm/st/stm32l4/device/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(device_native STATIC ${SRC_FILES}) # Set the include directories so the cmake src root machine/arm/cmsis machine/arm/device/stm32l4xx target_include_directories(device_native PUBLIC - ../../cmsis + ../../../cmsis ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/machine/arm/stm32l4xx/device/LICENSE.md b/machine/arm/st/stm32l4/device/LICENSE.md similarity index 100% rename from machine/arm/stm32l4xx/device/LICENSE.md rename to machine/arm/st/stm32l4/device/LICENSE.md diff --git a/machine/arm/stm32l4xx/device/stm32l412xx.h b/machine/arm/st/stm32l4/device/stm32l412xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l412xx.h rename to machine/arm/st/stm32l4/device/stm32l412xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l422xx.h b/machine/arm/st/stm32l4/device/stm32l422xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l422xx.h rename to machine/arm/st/stm32l4/device/stm32l422xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l431xx.h b/machine/arm/st/stm32l4/device/stm32l431xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l431xx.h rename to machine/arm/st/stm32l4/device/stm32l431xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l432xx.h b/machine/arm/st/stm32l4/device/stm32l432xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l432xx.h rename to machine/arm/st/stm32l4/device/stm32l432xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l433xx.h b/machine/arm/st/stm32l4/device/stm32l433xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l433xx.h rename to machine/arm/st/stm32l4/device/stm32l433xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l442xx.h b/machine/arm/st/stm32l4/device/stm32l442xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l442xx.h rename to machine/arm/st/stm32l4/device/stm32l442xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l443xx.h b/machine/arm/st/stm32l4/device/stm32l443xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l443xx.h rename to machine/arm/st/stm32l4/device/stm32l443xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l451xx.h b/machine/arm/st/stm32l4/device/stm32l451xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l451xx.h rename to machine/arm/st/stm32l4/device/stm32l451xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l452xx.h b/machine/arm/st/stm32l4/device/stm32l452xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l452xx.h rename to machine/arm/st/stm32l4/device/stm32l452xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l462xx.h b/machine/arm/st/stm32l4/device/stm32l462xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l462xx.h rename to machine/arm/st/stm32l4/device/stm32l462xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l471xx.h b/machine/arm/st/stm32l4/device/stm32l471xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l471xx.h rename to machine/arm/st/stm32l4/device/stm32l471xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l475xx.h b/machine/arm/st/stm32l4/device/stm32l475xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l475xx.h rename to machine/arm/st/stm32l4/device/stm32l475xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l476xx.h b/machine/arm/st/stm32l4/device/stm32l476xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l476xx.h rename to machine/arm/st/stm32l4/device/stm32l476xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l485xx.h b/machine/arm/st/stm32l4/device/stm32l485xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l485xx.h rename to machine/arm/st/stm32l4/device/stm32l485xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l486xx.h b/machine/arm/st/stm32l4/device/stm32l486xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l486xx.h rename to machine/arm/st/stm32l4/device/stm32l486xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l496xx.h b/machine/arm/st/stm32l4/device/stm32l496xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l496xx.h rename to machine/arm/st/stm32l4/device/stm32l496xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4a6xx.h b/machine/arm/st/stm32l4/device/stm32l4a6xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4a6xx.h rename to machine/arm/st/stm32l4/device/stm32l4a6xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4p5xx.h b/machine/arm/st/stm32l4/device/stm32l4p5xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4p5xx.h rename to machine/arm/st/stm32l4/device/stm32l4p5xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4q5xx.h b/machine/arm/st/stm32l4/device/stm32l4q5xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4q5xx.h rename to machine/arm/st/stm32l4/device/stm32l4q5xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4r5xx.h b/machine/arm/st/stm32l4/device/stm32l4r5xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4r5xx.h rename to machine/arm/st/stm32l4/device/stm32l4r5xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4r7xx.h b/machine/arm/st/stm32l4/device/stm32l4r7xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4r7xx.h rename to machine/arm/st/stm32l4/device/stm32l4r7xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4r9xx.h b/machine/arm/st/stm32l4/device/stm32l4r9xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4r9xx.h rename to machine/arm/st/stm32l4/device/stm32l4r9xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4s5xx.h b/machine/arm/st/stm32l4/device/stm32l4s5xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4s5xx.h rename to machine/arm/st/stm32l4/device/stm32l4s5xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4s7xx.h b/machine/arm/st/stm32l4/device/stm32l4s7xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4s7xx.h rename to machine/arm/st/stm32l4/device/stm32l4s7xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4s9xx.h b/machine/arm/st/stm32l4/device/stm32l4s9xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4s9xx.h rename to machine/arm/st/stm32l4/device/stm32l4s9xx.h diff --git a/machine/arm/stm32l4xx/device/stm32l4xx.h b/machine/arm/st/stm32l4/device/stm32l4xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/stm32l4xx.h rename to machine/arm/st/stm32l4/device/stm32l4xx.h diff --git a/machine/arm/stm32l4xx/device/system_stm32l4xx.c b/machine/arm/st/stm32l4/device/system_stm32l4xx.c similarity index 100% rename from machine/arm/stm32l4xx/device/system_stm32l4xx.c rename to machine/arm/st/stm32l4/device/system_stm32l4xx.c diff --git a/machine/arm/stm32l4xx/device/system_stm32l4xx.h b/machine/arm/st/stm32l4/device/system_stm32l4xx.h similarity index 100% rename from machine/arm/stm32l4xx/device/system_stm32l4xx.h rename to machine/arm/st/stm32l4/device/system_stm32l4xx.h diff --git a/machine/arm/stm32l4xx/hal/CMakeLists.txt b/machine/arm/st/stm32l4/hal/CMakeLists.txt similarity index 97% rename from machine/arm/stm32l4xx/hal/CMakeLists.txt rename to machine/arm/st/stm32l4/hal/CMakeLists.txt index 7c49676..50624a2 100644 --- a/machine/arm/stm32l4xx/hal/CMakeLists.txt +++ b/machine/arm/st/stm32l4/hal/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(hal_native STATIC ${SRC_FILES}) # Set the include directories so the cmake src root machine/arm/cmsis machine/arm/device/stm32l4xx target_include_directories(hal_native PUBLIC - ../../cmsis + ../../../cmsis ../device ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/machine/arm/stm32l4xx/hal/LICENSE.md b/machine/arm/st/stm32l4/hal/LICENSE.md similarity index 100% rename from machine/arm/stm32l4xx/hal/LICENSE.md rename to machine/arm/st/stm32l4/hal/LICENSE.md diff --git a/machine/arm/stm32l4xx/hal/Legacy/stm32_hal_legacy.h b/machine/arm/st/stm32l4/hal/Legacy/stm32_hal_legacy.h similarity index 100% rename from machine/arm/stm32l4xx/hal/Legacy/stm32_hal_legacy.h rename to machine/arm/st/stm32l4/hal/Legacy/stm32_hal_legacy.h diff --git a/machine/arm/stm32l4xx/hal/Legacy/stm32l4xx_hal_can.c b/machine/arm/st/stm32l4/hal/Legacy/stm32l4xx_hal_can.c similarity index 100% rename from machine/arm/stm32l4xx/hal/Legacy/stm32l4xx_hal_can.c rename to machine/arm/st/stm32l4/hal/Legacy/stm32l4xx_hal_can.c diff --git a/machine/arm/stm32l4xx/hal/Legacy/stm32l4xx_hal_can_legacy.h b/machine/arm/st/stm32l4/hal/Legacy/stm32l4xx_hal_can_legacy.h similarity index 100% rename from machine/arm/stm32l4xx/hal/Legacy/stm32l4xx_hal_can_legacy.h rename to machine/arm/st/stm32l4/hal/Legacy/stm32l4xx_hal_can_legacy.h diff --git a/machine/arm/stm32l4xx/hal/stm32_assert_template.h b/machine/arm/st/stm32l4/hal/stm32_assert_template.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32_assert_template.h rename to machine/arm/st/stm32l4/hal/stm32_assert_template.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_adc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_adc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_can.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_can.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_can.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_can.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_can.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_can.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_can.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_can.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_comp.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_comp.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_comp.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_comp.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_comp.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_comp.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_comp.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_comp.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_conf.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_conf.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_conf.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_conf.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cortex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cortex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cortex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cortex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cortex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cortex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cortex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cortex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_crc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_crc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_cryp_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_cryp_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dac_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dac_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dcmi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dcmi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dcmi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dcmi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dcmi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dcmi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dcmi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dcmi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_def.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_def.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_def.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_def.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dfsdm_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dfsdm_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma2d.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma2d.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma2d.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma2d.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma2d.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma2d.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma2d.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma2d.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dma_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dma_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dsi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dsi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dsi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dsi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_dsi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_dsi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_dsi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_dsi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_exti.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_exti.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_exti.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_exti.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_exti.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_exti.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_exti.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_exti.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_firewall.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_firewall.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_firewall.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_firewall.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_firewall.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_firewall.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_firewall.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_firewall.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ramfunc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ramfunc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ramfunc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ramfunc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ramfunc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ramfunc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_flash_ramfunc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_flash_ramfunc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_gfxmmu.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_gfxmmu.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_gfxmmu.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_gfxmmu.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_gfxmmu.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_gfxmmu.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_gfxmmu.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_gfxmmu.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_gpio_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_gpio_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hash_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hash_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hcd.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hcd.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hcd.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hcd.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_hcd.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_hcd.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_hcd.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_hcd.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_i2c_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_i2c_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_irda_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_irda_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_iwdg.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_iwdg.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_iwdg.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_iwdg.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_iwdg.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_iwdg.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_iwdg.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_iwdg.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_lcd.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_lcd.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_lcd.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_lcd.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_lcd.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_lcd.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_lcd.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_lcd.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_lptim.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_lptim.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_lptim.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_lptim.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_lptim.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_lptim.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_lptim.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_lptim.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ltdc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ltdc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_mmc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_mmc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_nand.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_nand.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_nand.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_nand.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_nand.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_nand.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_nand.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_nand.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_nor.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_nor.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_nor.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_nor.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_nor.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_nor.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_nor.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_nor.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_opamp_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_opamp_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ospi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ospi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ospi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ospi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_ospi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_ospi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_ospi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_ospi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pcd_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pcd_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pka.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pka.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pka.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pka.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pka.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pka.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pka.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pka.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pssi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pssi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pssi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pssi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pssi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pssi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pssi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pssi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_pwr_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_pwr_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_qspi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_qspi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_qspi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_qspi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_qspi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_qspi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_qspi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_qspi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rcc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rcc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rng_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rng_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_rtc_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_rtc_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sai_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sai_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sd_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sd_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smartcard_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smartcard_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_smbus_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_smbus_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_spi_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_spi_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sram.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sram.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sram.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sram.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_sram.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_sram.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_sram.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_sram.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_swpmi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_swpmi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_swpmi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_swpmi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_swpmi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_swpmi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_swpmi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_swpmi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tim_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tim_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_timebase_tim_template.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_timebase_tim_template.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_timebase_tim_template.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_timebase_tim_template.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tsc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tsc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tsc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tsc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_tsc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_tsc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_tsc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_tsc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_uart_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_uart_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart_ex.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart_ex.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart_ex.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart_ex.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart_ex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart_ex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_usart_ex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_usart_ex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_wwdg.c b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_wwdg.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_wwdg.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_wwdg.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_hal_wwdg.h b/machine/arm/st/stm32l4/hal/stm32l4xx_hal_wwdg.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_hal_wwdg.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_hal_wwdg.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_adc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_adc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_adc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_adc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_adc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_adc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_adc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_adc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_bus.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_bus.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_bus.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_bus.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_comp.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_comp.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_comp.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_comp.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_comp.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_comp.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_comp.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_comp.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_cortex.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_cortex.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_cortex.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_cortex.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_crc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_crc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_crc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_crc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_crc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_crc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_crc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_crc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_crs.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_crs.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_crs.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_crs.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_crs.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_crs.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_crs.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_crs.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dac.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dac.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dac.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dac.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dac.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dac.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dac.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dac.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma2d.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma2d.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma2d.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma2d.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma2d.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma2d.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dma2d.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dma2d.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_dmamux.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_dmamux.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_dmamux.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_dmamux.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_exti.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_exti.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_exti.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_exti.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_exti.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_exti.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_exti.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_exti.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_fmc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_fmc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_fmc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_fmc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_fmc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_fmc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_fmc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_fmc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_gpio.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_gpio.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_gpio.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_gpio.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_gpio.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_gpio.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_gpio.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_gpio.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_i2c.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_i2c.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_i2c.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_i2c.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_i2c.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_i2c.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_i2c.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_i2c.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_iwdg.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_iwdg.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_iwdg.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_iwdg.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_lptim.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_lptim.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_lptim.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_lptim.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_lptim.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_lptim.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_lptim.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_lptim.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_lpuart.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_lpuart.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_lpuart.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_lpuart.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_lpuart.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_lpuart.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_lpuart.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_lpuart.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_opamp.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_opamp.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_opamp.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_opamp.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_opamp.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_opamp.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_opamp.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_opamp.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_pka.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_pka.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_pka.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_pka.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_pka.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_pka.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_pka.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_pka.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_pwr.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_pwr.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_pwr.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_pwr.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_pwr.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_pwr.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_pwr.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_pwr.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rcc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rcc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rcc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rcc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rcc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rcc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rcc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rcc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rng.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rng.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rng.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rng.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rng.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rng.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rng.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rng.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rtc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rtc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rtc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rtc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_rtc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_rtc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_rtc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_rtc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_sdmmc.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_sdmmc.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_sdmmc.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_sdmmc.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_sdmmc.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_sdmmc.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_sdmmc.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_sdmmc.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_spi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_spi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_spi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_spi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_spi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_spi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_spi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_spi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_swpmi.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_swpmi.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_swpmi.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_swpmi.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_swpmi.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_swpmi.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_swpmi.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_swpmi.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_system.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_system.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_system.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_system.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_tim.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_tim.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_tim.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_tim.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_tim.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_tim.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_tim.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_tim.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_usart.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_usart.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_usart.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_usart.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_usart.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_usart.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_usart.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_usart.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_usb.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_usb.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_usb.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_usb.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_usb.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_usb.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_usb.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_usb.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_utils.c b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_utils.c similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_utils.c rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_utils.c diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_utils.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_utils.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_utils.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_utils.h diff --git a/machine/arm/stm32l4xx/hal/stm32l4xx_ll_wwdg.h b/machine/arm/st/stm32l4/hal/stm32l4xx_ll_wwdg.h similarity index 100% rename from machine/arm/stm32l4xx/hal/stm32l4xx_ll_wwdg.h rename to machine/arm/st/stm32l4/hal/stm32l4xx_ll_wwdg.h diff --git a/machine/arm/stm32l4xx/interface/CMakeLists.txt b/machine/arm/st/stm32l4/interface/CMakeLists.txt similarity index 96% rename from machine/arm/stm32l4xx/interface/CMakeLists.txt rename to machine/arm/st/stm32l4/interface/CMakeLists.txt index 28f1d8d..4a8ee6c 100644 --- a/machine/arm/stm32l4xx/interface/CMakeLists.txt +++ b/machine/arm/st/stm32l4/interface/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(interface_native STATIC ${SRC_FILES}) # Set the include directories so the cmake src root machine/arm/cmsis machine/arm/device/stm32l4xx target_include_directories(interface_native PUBLIC - ../../cmsis + ../../../cmsis ../device ../hal ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/machine/arm/stm32l4xx/interface/README.md b/machine/arm/st/stm32l4/interface/README.md similarity index 100% rename from machine/arm/stm32l4xx/interface/README.md rename to machine/arm/st/stm32l4/interface/README.md diff --git a/machine/arm/stm32l4xx/interface/clock.c b/machine/arm/st/stm32l4/interface/clock.c similarity index 100% rename from machine/arm/stm32l4xx/interface/clock.c rename to machine/arm/st/stm32l4/interface/clock.c diff --git a/machine/arm/stm32l4xx/interface/export.h b/machine/arm/st/stm32l4/interface/export.h similarity index 100% rename from machine/arm/stm32l4xx/interface/export.h rename to machine/arm/st/stm32l4/interface/export.h diff --git a/machine/arm/stm32l4xx/interface/instru.c b/machine/arm/st/stm32l4/interface/instru.c similarity index 100% rename from machine/arm/stm32l4xx/interface/instru.c rename to machine/arm/st/stm32l4/interface/instru.c diff --git a/machine/arm/stm32l4xx/interface/lib.c b/machine/arm/st/stm32l4/interface/lib.c similarity index 100% rename from machine/arm/stm32l4xx/interface/lib.c rename to machine/arm/st/stm32l4/interface/lib.c diff --git a/machine/arm/stm32l4xx/interface/lib.h b/machine/arm/st/stm32l4/interface/lib.h similarity index 100% rename from machine/arm/stm32l4xx/interface/lib.h rename to machine/arm/st/stm32l4/interface/lib.h diff --git a/machine/arm/stm32l4xx/interface/sched.c b/machine/arm/st/stm32l4/interface/sched.c similarity index 100% rename from machine/arm/stm32l4xx/interface/sched.c rename to machine/arm/st/stm32l4/interface/sched.c diff --git a/machine/arm/stm32l4xx/interface/uart.c b/machine/arm/st/stm32l4/interface/uart.c similarity index 100% rename from machine/arm/stm32l4xx/interface/uart.c rename to machine/arm/st/stm32l4/interface/uart.c diff --git a/machine/arm/st/stm32l4/link.ld b/machine/arm/st/stm32l4/link.ld new file mode 100644 index 0000000..7a63985 --- /dev/null +++ b/machine/arm/st/stm32l4/link.ld @@ -0,0 +1,129 @@ +__ram_start = ORIGIN(RAM); +__ram_end = ORIGIN(RAM) + LENGTH(RAM); +__stack_size = 0x8000; + +#if OSIRIS_DEBUG_RUNTIMESYMBOLS +/* at least 250kb for the symbol table */ +__syms_size = 0x3e800; +#else +/* if we don't want runtime symbols, we have exactly 4 null bytes which represent the size of the section as u32 */ +__syms_size = 4; +#endif + + +/* +We need to ensure that .data and .bss will be emitted in one segment. +The reason for that is, that with ropi-rwpi enabled LLD will generate incorrect r9 relative addressing when multiple segments are involved. +See https://github.com/llvm/llvm-project/issues/55628 for details. +*/ +PHDRS +{ + text PT_LOAD FLAGS((1 << 2) | (1 << 0)); /* PF_X | PF_R */ + ro PT_LOAD FLAGS((1 << 0)); /* PF_R */ + data PT_LOAD FLAGS((1 << 1) | (1 << 0)); /* PF_W | PF_R */ +} + +SECTIONS +{ + /* Our vector table. */ + .ivt ORIGIN(FLASH) : + { + __vector_table = .; + + KEEP(*(.ivt.core)); + KEEP(*(.ivt.ext)); + } > FLASH :text + + .text : + { + *(.text .text.* .gnu.linkonce.t*) + *(.gnu.warning) + } > FLASH :text + + /* Some arm exception stuff */ + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > FLASH :text + . = ALIGN(4); + PROVIDE_HIDDEN(__exidx_start = .); + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > FLASH :text + PROVIDE_HIDDEN(__exidx_end = .); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } > FLASH :text + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } > FLASH :text + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } > FLASH :text + + .rodata : + { + *(.lit) + *(.rodata .rodata.* .gnu.linkonce.r*) + . = ALIGN(0x4); + __rom_end = .; + } > FLASH :ro + + /* A section of memory where the runtime symbols will be placed. + * We just make sure that nothing leaks into this memory space. + */ + .syms_area : + { + . = ALIGN(4); + __syms_area_start = .; + KEEP(*(.syms_area)) + FILL(0x00); + /* BYTE(0x00); make sure the section is not empty */ + . = . + __syms_size; + __syms_area_end = .; + } > FLASH :ro + + __data = LOADADDR(.bootinfo); + .bootinfo : + { + __data_start = .; + KEEP(*(.bootinfo)) + . = ALIGN(4); + } > RAM AT > FLASH :data + + .data : + { + *(.data .data.* .gnu.linkonce.d*) + . = ALIGN(4); + __data_end = .; + } > RAM AT > FLASH :data + + .bss : + { + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM :data + + .stack (NOLOAD) : + { + . = ALIGN(4); + __stack_start = .; + . = . + __stack_size; + . = ALIGN(4); + __stack_top = .; + } > RAM + + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} \ No newline at end of file diff --git a/machine/arm/stm32l4xx/link.ld b/machine/arm/stm32l4xx/link.ld deleted file mode 100644 index ef1eae1..0000000 --- a/machine/arm/stm32l4xx/link.ld +++ /dev/null @@ -1,222 +0,0 @@ -__ram_start = ORIGIN(RAM); -__ram_end = ORIGIN(RAM) + LENGTH(RAM); -__stack_size = 0x8000; - -#if OSIRIS_DEBUG_RUNTIMESYMBOLS -/* at least 250kb for the symbol table */ -__syms_size = 0x3e800; -#else -/* if we don't want runtime symbols, we have exactly 4 null bytes which represent the size of the section as u32 */ -__syms_size = 4; -#endif - - -/* -We need to ensure that .data and .bss will be emitted in one segment. -The reason for that is, that with ropi-rwpi enabled LLD will generate incorrect r9 relative addressing when multiple segments are involved. -See https://github.com/llvm/llvm-project/issues/55628 for details. -*/ -PHDRS -{ - text PT_LOAD FLAGS((1 << 2) | (1 << 0)); /* PF_X | PF_R */ - ro PT_LOAD FLAGS((1 << 0)); /* PF_R */ - data PT_LOAD FLAGS((1 << 1) | (1 << 0)); /* PF_W | PF_R */ -} - -SECTIONS -{ - /* Our vector table. */ - .ivt ORIGIN(FLASH) : - { - __vector_table = .; - - KEEP(*(.ivt.core)); - KEEP(*(.ivt.ext)); - } > FLASH :text - - .text : - { - *(.text .text.* .gnu.linkonce.t*) - *(.gnu.warning) - } > FLASH :text - - /* Some arm exception stuff */ - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > FLASH :text - . = ALIGN(4); - PROVIDE_HIDDEN(__exidx_start = .); - .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > FLASH :text - PROVIDE_HIDDEN(__exidx_end = .); - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } > FLASH :text - - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } > FLASH :text - - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } > FLASH :text - - .rodata : - { - *(.lit) - *(.rodata .rodata.* .gnu.linkonce.r*) - . = ALIGN(0x4); - __rom_end = .; - } > FLASH :ro - - /* A section of memory where the runtime symbols will be placed. - * We just make sure that nothing leaks into this memory space. - */ - .syms_area : - { - . = ALIGN(4); - __syms_area_start = .; - KEEP(*(.syms_area)) - FILL(0x00); - /* BYTE(0x00); make sure the section is not empty */ - . = . + __syms_size; - __syms_area_end = .; - } > FLASH :ro - - __data = LOADADDR(.bootinfo); - .bootinfo : - { - __data_start = .; - KEEP(*(.bootinfo)) - . = ALIGN(4); - } > RAM AT > FLASH :data - - .data : - { - *(.data .data.* .gnu.linkonce.d*) - . = ALIGN(4); - __data_end = .; - } > RAM AT > FLASH :data - - .bss : - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - . = ALIGN(4); - __bss_end = .; - } > RAM :data - - .stack (NOLOAD) : - { - . = ALIGN(4); - __stack_start = .; - . = . + __stack_size; - . = ALIGN(4); - __stack_top = .; - } > RAM - - /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } -} - -PROVIDE(irq_hndlr = default_hndlr); - -PROVIDE(wwdg_hndlr = irq_enter_no_switch_fp); -PROVIDE(pvd_pvm_hndlr = irq_enter_no_switch_fp); -PROVIDE(tamp_stamp_hndlr = irq_enter_no_switch_fp); -PROVIDE(rtc_wkup_hndlr = irq_enter_no_switch_fp); -PROVIDE(flash_hndlr = irq_enter_no_switch_fp); -PROVIDE(rcc_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti0_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti1_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti2_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti3_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti4_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch1_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch2_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch3_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch4_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch5_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch6_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma1_ch7_hndlr = irq_enter_no_switch_fp); -PROVIDE(adc1_hndlr = irq_enter_no_switch_fp); -PROVIDE(can1_tx_hndlr = irq_enter_no_switch_fp); -PROVIDE(can1_rx0_hndlr = irq_enter_no_switch_fp); -PROVIDE(can1_rx1_hndlr = irq_enter_no_switch_fp); -PROVIDE(can1_sce_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti9_5_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim1_brk_tim15_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim1_up_tim16_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim1_trg_com_tim17_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim1_cc_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim2_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim3_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim4_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c1_ev_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c1_er_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c2_ev_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c2_er_hndlr = irq_enter_no_switch_fp); -PROVIDE(spi1_hndlr = irq_enter_no_switch_fp); -PROVIDE(spi2_hndlr = irq_enter_no_switch_fp); -PROVIDE(usart1_hndlr = irq_enter_no_switch_fp); -PROVIDE(usart2_hndlr = irq_enter_no_switch_fp); -PROVIDE(usart3_hndlr = irq_enter_no_switch_fp); -PROVIDE(exti15_10_hndlr = irq_enter_no_switch_fp); -PROVIDE(rtc_alarm_hndlr = irq_enter_no_switch_fp); -PROVIDE(dfsdm1_flt3_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim8_brk_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim8_up_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim8_trg_com_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim8_cc_hndlr = irq_enter_no_switch_fp); -PROVIDE(fmc_hndlr = irq_enter_no_switch_fp); -PROVIDE(sdmmc1_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim5_hndlr = irq_enter_no_switch_fp); -PROVIDE(spi3_hndlr = irq_enter_no_switch_fp); -PROVIDE(uart4_hndlr = irq_enter_no_switch_fp); -PROVIDE(uart5_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim6_dac_under_hndlr = irq_enter_no_switch_fp); -PROVIDE(tim7_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch1_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch2_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch3_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch4_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch5_hndlr = irq_enter_no_switch_fp); -PROVIDE(dfsdm1_flt0_hndlr = irq_enter_no_switch_fp); -PROVIDE(dfsdm1_flt1_hndlr = irq_enter_no_switch_fp); -PROVIDE(dfsdm1_flt2_hndlr = irq_enter_no_switch_fp); -PROVIDE(comp_hndlr = irq_enter_no_switch_fp); -PROVIDE(lptim1_hndlr = irq_enter_no_switch_fp); -PROVIDE(lptim2_hndlr = irq_enter_no_switch_fp); -PROVIDE(otg_fs_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch6_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2_ch7_hndlr = irq_enter_no_switch_fp); -PROVIDE(lpuart1_hndlr = irq_enter_no_switch_fp); -PROVIDE(octospi1_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c3_ev_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c3_er_hndlr = irq_enter_no_switch_fp); -PROVIDE(sai1_hndlr = irq_enter_no_switch_fp); -PROVIDE(sai2_hndlr = irq_enter_no_switch_fp); -PROVIDE(octospi2_hndlr = irq_enter_no_switch_fp); -PROVIDE(tsc_hndlr = irq_enter_no_switch_fp); -PROVIDE(dsihot_hndlr = irq_enter_no_switch_fp); -PROVIDE(aes_hndlr = irq_enter_no_switch_fp); -PROVIDE(rng_hndlr = irq_enter_no_switch_fp); -PROVIDE(fpu_hndlr = irq_enter_no_switch_fp); -PROVIDE(hash_crs_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c4_ev_hndlr = irq_enter_no_switch_fp); -PROVIDE(i2c4_er_hndlr = irq_enter_no_switch_fp); -PROVIDE(dcmi_hndlr = irq_enter_no_switch_fp); -PROVIDE(dma2d_hndlr = irq_enter_no_switch_fp); -PROVIDE(lcd_tft_hndlr = irq_enter_no_switch_fp); -PROVIDE(lcd_tft_er_hndlr = irq_enter_no_switch_fp); -PROVIDE(gfxmmu_hndlr = irq_enter_no_switch_fp); -PROVIDE(dmamux1_ovr_hndlr = irq_enter_no_switch_fp); \ No newline at end of file diff --git a/machine/arm/stm32l4xx/options.toml b/machine/arm/stm32l4xx/options.toml deleted file mode 100644 index f3d6482..0000000 --- a/machine/arm/stm32l4xx/options.toml +++ /dev/null @@ -1,9 +0,0 @@ -[metadata] -parent = ".arm.stm32l4xx" - -[variant] -name = "Variant" -description = "The specific microcontroller to use." -type = { type = "String", allowed_values = ["r5zi"] } -default = "r5zi" -attributes = [] \ No newline at end of file diff --git a/machine/arm/stm32l4xx/r5zi/CMakeLists.txt b/machine/arm/stm32l4xx/r5zi/CMakeLists.txt deleted file mode 100644 index df8e1c9..0000000 --- a/machine/arm/stm32l4xx/r5zi/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.28) - -add_library(variant_native STATIC lib.c) - -target_include_directories(variant_native PUBLIC - ../../../../interface/include - ${CMAKE_CURRENT_SOURCE_DIR} -) - -target_compile_definitions(variant_native PUBLIC - STM32L4R5xx -) - -set(TARGET_DEFINES "STM32L4R5xx" PARENT_SCOPE) \ No newline at end of file diff --git a/machine/arm/stm32l4xx/r5zi/lib.c b/machine/arm/stm32l4xx/r5zi/lib.c deleted file mode 100644 index 83c8135..0000000 --- a/machine/arm/stm32l4xx/r5zi/lib.c +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include - -/* - * External interrupt vector table for the STM32 Nucleo L4R5ZI - * Used references: - * https://www.st.com/resource/en/reference_manual/rm0432-stm32l4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf - */ - -extern void wwdg_hndlr(void); -extern void pvd_pvm_hndlr(void); -extern void tamp_stamp_hndlr(void); -extern void rtc_wkup_hndlr(void); -extern void flash_hndlr(void); -extern void rcc_hndlr(void); -extern void exti0_hndlr(void); -extern void exti1_hndlr(void); -extern void exti2_hndlr(void); -extern void exti3_hndlr(void); -extern void exti4_hndlr(void); -extern void dma1_ch1_hndlr(void); -extern void dma1_ch2_hndlr(void); -extern void dma1_ch3_hndlr(void); -extern void dma1_ch4_hndlr(void); -extern void dma1_ch5_hndlr(void); -extern void dma1_ch6_hndlr(void); -extern void dma1_ch7_hndlr(void); -extern void adc1_hndlr(void); -extern void can1_tx_hndlr(void); -extern void can1_rx0_hndlr(void); -extern void can1_rx1_hndlr(void); -extern void can1_sce_hndlr(void); -extern void exti9_5_hndlr(void); -extern void tim1_brk_tim15_hndlr(void); -extern void tim1_up_tim16_hndlr(void); -extern void tim1_trg_com_tim17_hndlr(void); -extern void tim1_cc_hndlr(void); -extern void tim2_hndlr(void); -extern void tim3_hndlr(void); -extern void tim4_hndlr(void); -extern void i2c1_ev_hndlr(void); -extern void i2c1_er_hndlr(void); -extern void i2c2_ev_hndlr(void); -extern void i2c2_er_hndlr(void); -extern void spi1_hndlr(void); -extern void spi2_hndlr(void); -extern void usart1_hndlr(void); -extern void usart2_hndlr(void); -extern void usart3_hndlr(void); -extern void exti15_10_hndlr(void); -extern void rtc_alarm_hndlr(void); -extern void dfsdm1_flt3_hndlr(void); -extern void tim8_brk_hndlr(void); -extern void tim8_up_hndlr(void); -extern void tim8_trg_com_hndlr(void); -extern void tim8_cc_hndlr(void); -extern void fmc_hndlr(void); -extern void sdmmc1_hndlr(void); -extern void tim5_hndlr(void); -extern void spi3_hndlr(void); -extern void uart4_hndlr(void); -extern void uart5_hndlr(void); -extern void tim6_dac_under_hndlr(void); -extern void tim7_hndlr(void); -extern void dma2_ch1_hndlr(void); -extern void dma2_ch2_hndlr(void); -extern void dma2_ch3_hndlr(void); -extern void dma2_ch4_hndlr(void); -extern void dma2_ch5_hndlr(void); -extern void dfsdm1_flt0_hndlr(void); -extern void dfsdm1_flt1_hndlr(void); -extern void dfsdm1_flt2_hndlr(void); -extern void comp_hndlr(void); -extern void lptim1_hndlr(void); -extern void lptim2_hndlr(void); -extern void otg_fs_hndlr(void); -extern void dma2_ch6_hndlr(void); -extern void dma2_ch7_hndlr(void); -extern void lpuart1_hndlr(void); -extern void octospi1_hndlr(void); -extern void i2c3_ev_hndlr(void); -extern void i2c3_er_hndlr(void); -extern void sai1_hndlr(void); -extern void sai2_hndlr(void); -extern void octospi2_hndlr(void); -extern void tsc_hndlr(void); -extern void dsihot_hndlr(void); -extern void aes_hndlr(void); -extern void rng_hndlr(void); -extern void fpu_hndlr(void); -extern void hash_crs_hndlr(void); -extern void i2c4_er_hndlr(void); -extern void i2c4_ev_hndlr(void); -extern void dcmi_hndlr(void); -extern void dma2d_hndlr(void); -extern void lcd_tft_hndlr(void); -extern void lcd_tft_er_hndlr(void); -extern void gfxmmu_hndlr(void); -extern void dmamux1_ovr_hndlr(void); - -const uintptr_t vector_table_ext[] __attribute__((section(".ivt.ext"))) = { - (uintptr_t)&wwdg_hndlr, - (uintptr_t)&pvd_pvm_hndlr, - (uintptr_t)&tamp_stamp_hndlr, - (uintptr_t)&rtc_wkup_hndlr, - (uintptr_t)&flash_hndlr, - (uintptr_t)&rcc_hndlr, - (uintptr_t)&exti0_hndlr, - (uintptr_t)&exti1_hndlr, - (uintptr_t)&exti2_hndlr, - (uintptr_t)&exti3_hndlr, - (uintptr_t)&exti4_hndlr, - (uintptr_t)&dma1_ch1_hndlr, - (uintptr_t)&dma1_ch2_hndlr, - (uintptr_t)&dma1_ch3_hndlr, - (uintptr_t)&dma1_ch4_hndlr, - (uintptr_t)&dma1_ch5_hndlr, - (uintptr_t)&dma1_ch6_hndlr, - (uintptr_t)&dma1_ch7_hndlr, - (uintptr_t)&adc1_hndlr, - (uintptr_t)&can1_tx_hndlr, - (uintptr_t)&can1_rx0_hndlr, - (uintptr_t)&can1_rx1_hndlr, - (uintptr_t)&can1_sce_hndlr, - (uintptr_t)&exti9_5_hndlr, - (uintptr_t)&tim1_brk_tim15_hndlr, - (uintptr_t)&tim1_up_tim16_hndlr, - (uintptr_t)&tim1_trg_com_tim17_hndlr, - (uintptr_t)&tim1_cc_hndlr, - (uintptr_t)&tim2_hndlr, - (uintptr_t)&tim3_hndlr, - (uintptr_t)&tim4_hndlr, - (uintptr_t)&i2c1_ev_hndlr, - (uintptr_t)&i2c1_er_hndlr, - (uintptr_t)&i2c2_ev_hndlr, - (uintptr_t)&i2c2_er_hndlr, - (uintptr_t)&spi1_hndlr, - (uintptr_t)&spi2_hndlr, - (uintptr_t)&usart1_hndlr, - (uintptr_t)&usart2_hndlr, - (uintptr_t)&usart3_hndlr, - (uintptr_t)&exti15_10_hndlr, - (uintptr_t)&rtc_alarm_hndlr, - (uintptr_t)&dfsdm1_flt3_hndlr, - (uintptr_t)&tim8_brk_hndlr, - (uintptr_t)&tim8_up_hndlr, - (uintptr_t)&tim8_trg_com_hndlr, - (uintptr_t)&tim8_cc_hndlr, - 0, - (uintptr_t)&fmc_hndlr, - (uintptr_t)&sdmmc1_hndlr, - (uintptr_t)&tim5_hndlr, - (uintptr_t)&spi3_hndlr, - (uintptr_t)&uart4_hndlr, - (uintptr_t)&uart5_hndlr, - (uintptr_t)&tim6_dac_under_hndlr, - (uintptr_t)&tim7_hndlr, - (uintptr_t)&dma2_ch1_hndlr, - (uintptr_t)&dma2_ch2_hndlr, - (uintptr_t)&dma2_ch3_hndlr, - (uintptr_t)&dma2_ch4_hndlr, - (uintptr_t)&dma2_ch5_hndlr, - (uintptr_t)&dfsdm1_flt0_hndlr, - (uintptr_t)&dfsdm1_flt1_hndlr, - (uintptr_t)&dfsdm1_flt2_hndlr, - (uintptr_t)&comp_hndlr, - (uintptr_t)&lptim1_hndlr, - (uintptr_t)&lptim2_hndlr, - (uintptr_t)&otg_fs_hndlr, - (uintptr_t)&dma2_ch6_hndlr, - (uintptr_t)&dma2_ch7_hndlr, - (uintptr_t)&lpuart1_hndlr, - (uintptr_t)&octospi1_hndlr, - (uintptr_t)&i2c3_ev_hndlr, - (uintptr_t)&i2c3_er_hndlr, - (uintptr_t)&sai1_hndlr, - (uintptr_t)&sai2_hndlr, - (uintptr_t)&octospi2_hndlr, - (uintptr_t)&tsc_hndlr, - (uintptr_t)&dsihot_hndlr, - (uintptr_t)&aes_hndlr, - (uintptr_t)&rng_hndlr, - (uintptr_t)&fpu_hndlr, - (uintptr_t)&hash_crs_hndlr, - (uintptr_t)&i2c4_er_hndlr, - (uintptr_t)&i2c4_ev_hndlr, - (uintptr_t)&dcmi_hndlr, - 0, - 0, - 0, - 0, - (uintptr_t)&dma2d_hndlr, - (uintptr_t)&lcd_tft_hndlr, - (uintptr_t)&lcd_tft_er_hndlr, - (uintptr_t)&gfxmmu_hndlr, - (uintptr_t)&dmamux1_ovr_hndlr, -}; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 49f1b4a..5308803 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,8 +1,8 @@ use syn::parse_macro_input; -mod tree; -mod syscall; mod logging; +mod syscall; +mod tree; #[proc_macro_derive(TaggedLinks, attributes(rbtree, list))] pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -11,21 +11,29 @@ pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenS match tree::derive_tagged_links(&input) { Ok(tokens) => tokens, Err(e) => e.to_compile_error(), - }.into() + } + .into() } #[proc_macro_attribute] -pub fn fmt(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn fmt( + args: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(input as syn::DeriveInput); match logging::derive_fmt(&input) { Ok(tokens) => tokens, Err(e) => e.to_compile_error(), - }.into() + } + .into() } #[proc_macro_attribute] -pub fn app_main(input: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn app_main( + input: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { let item = syn::parse_macro_input!(item as syn::ItemFn); let block = &item.block; @@ -72,5 +80,3 @@ pub fn syscall_handler( let item = syn::parse_macro_input!(item as syn::ItemFn); syscall::syscall_handler_fn(&item).into() } - - diff --git a/macros/src/logging.rs b/macros/src/logging.rs index 9e857fe..f462d12 100644 --- a/macros/src/logging.rs +++ b/macros/src/logging.rs @@ -21,4 +21,4 @@ fn derive_fmt_debug(input: &DeriveInput) -> proc_macro2::TokenStream { #[derive(core::fmt::Debug)] #input } -} \ No newline at end of file +} diff --git a/macros/src/syscall.rs b/macros/src/syscall.rs index 45c071c..1e09d7c 100644 --- a/macros/src/syscall.rs +++ b/macros/src/syscall.rs @@ -1,5 +1,5 @@ -use quote::{ToTokens, format_ident}; use proc_macro2::TokenStream; +use quote::{ToTokens, format_ident}; pub const MAX_ARGS: usize = 4; @@ -75,9 +75,7 @@ pub fn syscall_handler_fn(item: &syn::ItemFn) -> TokenStream { if num_args > MAX_ARGS { return syn::Error::new( item.sig.ident.span(), - format!( - "syscall_handler: function {name} has too many arguments (max is {MAX_ARGS})", - ), + format!("syscall_handler: function {name} has too many arguments (max is {MAX_ARGS})",), ) .to_compile_error(); } diff --git a/macros/src/tree.rs b/macros/src/tree.rs index 9a37950..d5fb348 100644 --- a/macros/src/tree.rs +++ b/macros/src/tree.rs @@ -1,7 +1,5 @@ use quote::quote; -use syn::{ - spanned::Spanned, Data, DeriveInput, Error, Fields, Path, -}; +use syn::{Data, DeriveInput, Error, Fields, Path, spanned::Spanned}; pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result { let fields = match &input.data { @@ -11,14 +9,14 @@ pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result { return Err(Error::new( input.span(), "TaggedLinks can only be derived for structs", - )) + )); } }; @@ -31,14 +29,19 @@ pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result) -> syn::Result { +fn impl_rbtree( + input: &DeriveInput, + fields: &syn::punctuated::Punctuated, +) -> syn::Result { let struct_ident = &input.ident; let generics = &input.generics; let mut impls = Vec::new(); for field in fields { - let Some(field_ident) = field.ident.clone() else { continue }; + let Some(field_ident) = field.ident.clone() else { + continue; + }; if let (Some(tag_path), Some(idx_path)) = find_tagged(&field.attrs, "rbtree")? { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -63,14 +66,19 @@ fn impl_rbtree(input: &DeriveInput, fields: &syn::punctuated::Punctuated) -> syn::Result { +fn impl_list( + input: &DeriveInput, + fields: &syn::punctuated::Punctuated, +) -> syn::Result { let struct_ident = &input.ident; let generics = &input.generics; let mut impls = Vec::new(); for field in fields { - let Some(field_ident) = field.ident.clone() else { continue }; + let Some(field_ident) = field.ident.clone() else { + continue; + }; if let (Some(tag_path), Some(idx_path)) = find_tagged(&field.attrs, "list")? { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -95,7 +103,10 @@ fn impl_list(input: &DeriveInput, fields: &syn::punctuated::Punctuated syn::Result<(Option, Option)> { +fn find_tagged( + attrs: &[syn::Attribute], + attr_name: &str, +) -> syn::Result<(Option, Option)> { for attr in attrs { if !attr.path().is_ident(attr_name) { continue; diff --git a/presets/stm32l4r5zi_def.toml b/presets/stm32l4r5zi_def.toml index fc6a853..20cd196 100644 --- a/presets/stm32l4r5zi_def.toml +++ b/presets/stm32l4r5zi_def.toml @@ -1,10 +1,6 @@ # This is the default configuration for the STM32L4R5ZI MCU. [env] -# Hardware Selection -OSIRIS_ARM_HAL = "stm32l4xx" -OSIRIS_ARM_STM32L4XX_VARIANT = "r5zi" - # Debugging configuration OSIRIS_DEBUG_UART = "LPUART1" OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" @@ -12,6 +8,7 @@ OSIRIS_DEBUG_RUNTIMESYMBOLS = "false" # Tuning parameters OSIRIS_TUNING_ENABLEFPU = "false" OSIRIS_STACKPAGES = "1" +OSIRIS_TUNING_DTS = "nucleo_l4r5zi.dts" [build] target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index cc48035..5311ec2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,9 @@ //! Utility functions and definitions for the kernel. #![cfg_attr(feature = "nightly", feature(likely_unlikely))] +use core::fmt::Debug; use core::fmt::Display; use hal::mem::PhysAddr; -use core::fmt::Debug; /// These two definitions are copied from https://github.com/rust-lang/hashbrown #[cfg(not(feature = "nightly"))] @@ -176,4 +176,4 @@ impl From for Error { fn from(e: hal::Error) -> Self { Self::new(Kind::Hal(e)) } -} \ No newline at end of file +} diff --git a/src/idle.rs b/src/idle.rs index 940605e..8243b35 100644 --- a/src/idle.rs +++ b/src/idle.rs @@ -7,7 +7,11 @@ extern "C" fn entry() { } pub fn init() { - let attrs = sched::thread::Attributes { entry, fin: None, attrs: None }; + let attrs = sched::thread::Attributes { + entry, + fin: None, + attrs: None, + }; sched::with(|sched| { if let Err(e) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { panic!("failed to create idle thread. Error: {}", e); diff --git a/src/lib.rs b/src/lib.rs index 1218892..bf3197b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,11 +8,11 @@ mod macros; #[macro_use] mod error; mod faults; +mod idle; mod mem; +mod print; mod types; -mod idle; mod uspace; -mod print; mod sched; mod sync; @@ -22,7 +22,6 @@ mod time; pub mod uapi; use hal::Machinelike; -include!(concat!(env!("OUT_DIR"), "/device_tree.rs")); pub use hal; pub use proc_macros::app_main; @@ -53,13 +52,11 @@ pub unsafe extern "C" fn kernel_init() -> ! { kprintln!("Idle thread initialized."); let (cyc, ns) = hal::Machine::bench_end(); - kprintln!( - "Kernel init took {} cycles.", cyc - ); + kprintln!("Kernel init took {} cycles.", cyc); // Start the init application. uspace::init_app(); - + sched::enable(); loop {} diff --git a/src/macros.rs b/src/macros.rs index 78ddb33..2591744 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -34,9 +34,7 @@ macro_rules! error { #[macro_export] macro_rules! kprint { - ($($arg:tt)*) => ({ - - }); + ($($arg:tt)*) => {{}}; } #[macro_export] diff --git a/src/mem.rs b/src/mem.rs index dc1bcdf..c22042f 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -4,17 +4,17 @@ use crate::mem::pfa::PAGE_SIZE; use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region}; use crate::sync::spinlock::SpinLocked; use alloc::Allocator; -use hal::mem::{PhysAddr}; use core::ptr::NonNull; +use hal::mem::PhysAddr; pub mod alloc; -pub mod vmm; pub mod pfa; +pub mod vmm; pub const BITS_PER_PTR: usize = core::mem::size_of::() * 8; unsafe extern "C" { - unsafe static __stack_top: u8; + unsafe static __stack_top: u8; } /// The global memory allocator. @@ -28,7 +28,8 @@ static GLOBAL_ALLOCATOR: SpinLocked = /// Returns an error if the memory allocator could not be initialized. pub fn init_memory() -> vmm::AddressSpace { let stack_top = &raw const __stack_top as usize; - if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) { // TODO: Get this from the DeviceTree. + if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) { + // TODO: Get this from the DeviceTree. panic!("failed to initialize PFA. Error: {e}"); } @@ -36,12 +37,19 @@ pub fn init_memory() -> vmm::AddressSpace { let pgs = 10; let mut kaddr_space = vmm::AddressSpace::new(pgs).unwrap_or_else(|e| { - panic!("failed to create kernel address space. Error: {e}"); + panic!("failed to create kernel address space. Error: {e}"); }); - let begin = kaddr_space.map(Region::new(None, 2 * PAGE_SIZE, Backing::Zeroed, Perms::all())).unwrap_or_else(|e| { - panic!("failed to map kernel address space. Error: {e}"); - }); + let begin = kaddr_space + .map(Region::new( + None, + 2 * PAGE_SIZE, + Backing::Zeroed, + Perms::all(), + )) + .unwrap_or_else(|e| { + panic!("failed to map kernel address space. Error: {e}"); + }); { let mut allocator = GLOBAL_ALLOCATOR.lock(); diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index c7a7831..e9407a2 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -25,7 +25,11 @@ pub const MAX_ADDR: usize = usize::MAX; /// Each range added to the allocator must be valid for the whole lifetime of the allocator and must not overlap with any other range. /// The lifetime of any allocation is only valid as long as the allocator is valid. (A pointer must not be used after the allocator is dropped.) pub trait Allocator { - fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result>; + fn malloc( + &mut self, + size: usize, + align: usize, + request: Option, + ) -> Result>; unsafe fn free(&mut self, ptr: NonNull, size: usize); } - diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index 4732a85..734c50c 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -49,7 +49,7 @@ impl BestFitAllocator { // Check if the pointer is 128bit aligned. if !ptr.is_multiple_of(align_of::()) { - return Err(kerr!(InvalidArgument)); + return Err(kerr!(InvalidArgument)); } if range.end.diff(range.start) < Self::MIN_RANGE_SIZE { @@ -169,7 +169,11 @@ impl BestFitAllocator { } unsafe fn contains(meta: &BestFitMeta, target: PhysAddr, size: usize) -> bool { - let begin = unsafe { Self::user_ptr(NonNull::new_unchecked(meta as *const BestFitMeta as *mut u8)) }; + let begin = unsafe { + Self::user_ptr(NonNull::new_unchecked( + meta as *const BestFitMeta as *mut u8, + )) + }; debug_assert!(size > 0); if target >= begin.into() { @@ -194,7 +198,12 @@ impl super::Allocator for BestFitAllocator { /// `align` - The alignment of the block. /// /// Returns the user pointer to the block if successful, otherwise an error. - fn malloc(&mut self, size: usize, align: usize, request: Option) -> Result> { + fn malloc( + &mut self, + size: usize, + align: usize, + request: Option, + ) -> Result> { // Check if the alignment is valid. if align == 0 || align > align_of::() { return Err(kerr!(InvalidAlign)); @@ -204,7 +213,7 @@ impl super::Allocator for BestFitAllocator { if !request.is_multiple_of(align) { return Err(kerr!(InvalidAlign)); } - } + } // Check if the size is valid. if size == 0 { @@ -309,7 +318,9 @@ impl super::Allocator for BestFitAllocator { } if let Some(request) = request { - debug_assert!(unsafe { Self::contains(block.cast::().as_ref(), request, size) }); + debug_assert!(unsafe { + Self::contains(block.cast::().as_ref(), request, size) + }); } // Return the user pointer. @@ -328,7 +339,10 @@ impl super::Allocator for BestFitAllocator { meta.next = self.head; // Check if the size of the block is correct. - bug_on!(meta.size != super::super::align_up(size), "Invalid size in free()"); + bug_on!( + meta.size != super::super::align_up(size), + "Invalid size in free()" + ); // Set the size of the block. meta.size = size; @@ -344,8 +358,8 @@ impl super::Allocator for BestFitAllocator { mod tests { use crate::error::Kind; - use super::*; use super::super::*; + use super::*; fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; @@ -411,7 +425,11 @@ mod tests { let ptr = allocator.malloc::(128, 1, Some(request)).unwrap(); // Check that the returned pointer contains the requested address. - let meta = unsafe { BestFitAllocator::control_ptr(ptr).cast::().as_ref() }; + let meta = unsafe { + BestFitAllocator::control_ptr(ptr) + .cast::() + .as_ref() + }; assert!(unsafe { BestFitAllocator::contains(meta, request, 128) }); } diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs index e20f212..7390d3a 100644 --- a/src/mem/pfa.rs +++ b/src/mem/pfa.rs @@ -23,9 +23,9 @@ static PFA: SpinLocked>>> = SpinLocked::new(None); trait Allocator { /// Returns an initializer function that can be used to create an instance of the allocator. /// The initializer function takes a physical address and the amount of pages needed. - /// + /// /// Safety: - /// + /// /// - The returned function must only be called with a useable and valid physical address. fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>>; @@ -55,4 +55,4 @@ pub fn free_page(addr: PhysAddr, page_count: usize) { if let Some(pfa) = pfa.as_mut() { pfa.free(addr, page_count); } -} \ No newline at end of file +} diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 7dc5692..681ea8c 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -4,7 +4,8 @@ use core::ptr::NonNull; use hal::mem::PhysAddr; use crate::{ - error::Result, types::boxed::{self, Box} + error::Result, + types::boxed::{self, Box}, }; pub struct Allocator { @@ -51,7 +52,12 @@ impl super::Allocator for Allocator { PhysAddr::new((begin.as_usize() + super::PAGE_SIZE - 1) & !(super::PAGE_SIZE - 1)) }; // TODO: Subtract the needed pages from the available - unsafe { core::ptr::write(ptr.as_ptr(), Self::new(begin).ok_or(kerr!(InvalidArgument))?) }; + unsafe { + core::ptr::write( + ptr.as_ptr(), + Self::new(begin).ok_or(kerr!(InvalidArgument))?, + ) + }; // Safety: Ptr is properly aligned and non-null. The validity of the memory at that address is valid by the call contract. Ok(Pin::new(unsafe { boxed::Box::from_raw(ptr) })) @@ -124,7 +130,8 @@ impl super::Allocator for Allocator { let skip = start % Self::BITS_PER_WORD; let rem = len.min(Self::BITS_PER_WORD) - skip; - self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); + self.l1[idx] &= + !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); if len <= rem { return Some(self.begin + (start * super::PAGE_SIZE)); @@ -137,16 +144,17 @@ impl super::Allocator for Allocator { // Mark all bits in the middle words as used. { let mid_cnt = len / Self::BITS_PER_WORD; - + for i in 0..mid_cnt { self.l1[idx + i] = 0; } idx += mid_cnt; } - + // Mark the remaining bits in the last word as used. - self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); + self.l1[idx] &= !((!0usize) + .unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); return Some(self.begin + (start * super::PAGE_SIZE)); } } @@ -159,8 +167,10 @@ impl super::Allocator for Allocator { panic!("Address must be page aligned"); } - let mut idx = (addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE / Self::BITS_PER_WORD; - let mut bit_idx = ((addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE) % Self::BITS_PER_WORD; + let mut idx = + (addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE / Self::BITS_PER_WORD; + let mut bit_idx = + ((addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE) % Self::BITS_PER_WORD; // TODO: slow for _ in 0..page_count { @@ -224,4 +234,4 @@ mod tests { } } } -} \ No newline at end of file +} diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs index 6ddc7e9..987b021 100644 --- a/src/mem/vmm.rs +++ b/src/mem/vmm.rs @@ -32,12 +32,12 @@ pub struct Region { impl Region { /// Creates a new region. - /// + /// /// - `start` is the starting virtual address of the region. If `None`, the system will choose a suitable address. /// - `len` is the length of the region in bytes. /// - `backing` is the backing type of the region, which determines how the region is initialized and where its contents come from. /// - `perms` is the permissions of the region, which determines how the region can be accessed. - /// + /// pub fn new(start: Option, len: usize, backing: Backing, perms: Perms) -> Self { Self { start, @@ -62,7 +62,9 @@ impl Region { pub trait AddressSpacelike { // Size is the amount of pages in the address space. On nommu systems this will be reserved. - fn new(pages: usize) -> Result where Self: Sized; + fn new(pages: usize) -> Result + where + Self: Sized; fn map(&mut self, region: Region) -> Result; fn unmap(&mut self, region: &Region) -> Result<()>; fn protect(&mut self, region: &Region, perms: Perms) -> Result<()>; @@ -70,4 +72,4 @@ pub trait AddressSpacelike { fn phys_to_virt(&self, addr: PhysAddr) -> Option; fn end(&self) -> VirtAddr; fn activate(&self) -> Result<()>; -} \ No newline at end of file +} diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs index f169c14..3a97984 100644 --- a/src/mem/vmm/nommu.rs +++ b/src/mem/vmm/nommu.rs @@ -3,10 +3,11 @@ use core::ptr::copy_nonoverlapping; use hal::mem::{PhysAddr, VirtAddr}; use crate::{ - error::Result, mem::{ + error::Result, + mem::{ alloc::{Allocator, bestfit}, pfa, vmm, - } + }, }; pub struct AddressSpace { diff --git a/src/sched/dispch.rs b/src/sched/dispch.rs index 0f781b6..d3f0e3f 100644 --- a/src/sched/dispch.rs +++ b/src/sched/dispch.rs @@ -4,4 +4,4 @@ pub fn prepare(task: &mut Task) { if task.id.is_kernel() { // Change task priv. level in HAL. } -} \ No newline at end of file +} diff --git a/src/sched/rr.rs b/src/sched/rr.rs index 1e966c9..0209544 100644 --- a/src/sched/rr.rs +++ b/src/sched/rr.rs @@ -1,5 +1,7 @@ use crate::{ - error::Result, sched::thread::{self}, types::list::List + error::Result, + sched::thread::{self}, + types::list::List, }; pub struct Scheduler { @@ -13,11 +15,18 @@ pub struct Scheduler { impl Scheduler { pub const fn new() -> Self { // TODO: Make quantum configurable. - Self { queue: List::new(), current: None, current_left: 0, quantum: 1000 } + Self { + queue: List::new(), + current: None, + current_left: 0, + quantum: 1000, + } } pub fn enqueue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) -> Result<()> { - self.queue.push_back(uid, storage).map_err(|_| kerr!(InvalidArgument)) + self.queue + .push_back(uid, storage) + .map_err(|_| kerr!(InvalidArgument)) } pub fn put(&mut self, uid: thread::UId, dt: u32) { diff --git a/src/sched/rt.rs b/src/sched/rt.rs index 33799a6..5fe0b41 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -1,4 +1,14 @@ -use crate::{types::{rbtree::RbTree, traits::{Get, GetMut}, view::ViewMut}, sched::{ThreadMap, thread::{self}}}; +use crate::{ + sched::{ + ThreadMap, + thread::{self}, + }, + types::{ + rbtree::RbTree, + traits::{Get, GetMut}, + view::ViewMut, + }, +}; pub struct Scheduler { edf: RbTree, @@ -8,9 +18,7 @@ pub type ServerView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::RtSer impl Scheduler { pub const fn new() -> Self { - Self { - edf: RbTree::new(), - } + Self { edf: RbTree::new() } } pub fn enqueue(&mut self, uid: thread::UId, now: u64, storage: &mut ServerView) { @@ -36,10 +44,12 @@ impl Scheduler { } pub fn pick(&mut self, storage: &mut ServerView) -> Option<(thread::UId, u32)> { - self.edf.min().and_then(|id| storage.get(id).map(|s| (id, s.budget()))) + self.edf + .min() + .and_then(|id| storage.get(id).map(|s| (id, s.budget()))) } pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) { self.edf.remove(uid, storage); } -} \ No newline at end of file +} diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index 258574e..6e1338b 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -69,4 +69,3 @@ fn exit(code: usize) -> c_int { fn kick_thread(uid: usize) -> c_int { 0 } - diff --git a/src/time.rs b/src/time.rs index 44cc3a5..a5ff9d7 100644 --- a/src/time.rs +++ b/src/time.rs @@ -10,7 +10,7 @@ pub fn tick() -> u64 { pub fn mono_now() -> u64 { // TODO: This will break on SMP systems without native u64 atomic store. - sync::atomic::irq_free(|| hal::Machine::monotonic_now() ) + sync::atomic::irq_free(|| hal::Machine::monotonic_now()) } pub fn mono_freq() -> u64 { diff --git a/src/types.rs b/src/types.rs index 746bf6d..8b1befe 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,8 @@ - -pub mod boxed; pub mod array; +pub mod boxed; pub mod heap; +pub mod list; pub mod pool; pub mod rbtree; -pub mod list; pub mod traits; -pub mod view; \ No newline at end of file +pub mod view; diff --git a/src/types/array.rs b/src/types/array.rs index 15eae5b..ad9d651 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -3,26 +3,22 @@ use crate::error::Result; use super::{ - traits::{Get, GetMut, ToIndex}, boxed::Box, + traits::{Get, GetMut, ToIndex}, }; +use core::ops::{Index, IndexMut}; use core::{borrow::Borrow, mem::MaybeUninit}; -use core::{ - ops::{Index, IndexMut}, -}; /// This is a fixed-size map that can store up to N consecutive elements. #[proc_macros::fmt] -pub struct IndexMap -{ +pub struct IndexMap { data: [Option; N], phantom: core::marker::PhantomData, } #[allow(dead_code)] -impl IndexMap -{ +impl IndexMap { /// Create a new IndexMap. /// /// Returns a new IndexMap. @@ -74,11 +70,7 @@ impl IndexMap pub fn remove(&mut self, idx: &K) -> Option { let idx = K::to_index(Some(idx)); - if idx < N { - self.data[idx].take() - } else { - None - } + if idx < N { self.data[idx].take() } else { None } } /// Get an iterator over the elements in the map. @@ -140,8 +132,7 @@ impl IndexMap } } -impl Index for IndexMap -{ +impl Index for IndexMap { type Output = V; fn index(&self, index: K) -> &Self::Output { @@ -149,15 +140,13 @@ impl Index for IndexMap } } -impl IndexMut for IndexMap -{ +impl IndexMut for IndexMap { fn index_mut(&mut self, index: K) -> &mut Self::Output { self.get_mut::(index).unwrap() } } -impl Get for IndexMap -{ +impl Get for IndexMap { type Output = V; fn get>(&self, index: Q) -> Option<&Self::Output> { @@ -180,7 +169,11 @@ impl GetMut for IndexMap { } } - fn get2_mut>(&mut self, index1: Q, index2: Q) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { + fn get2_mut>( + &mut self, + index1: Q, + index2: Q, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { let idx1 = K::to_index(Some(index1.borrow())); let idx2 = K::to_index(Some(index2.borrow())); @@ -207,7 +200,11 @@ impl GetMut for IndexMap { index1: Q, index2: Q, index3: Q, - ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>) { + ) -> ( + Option<&mut Self::Output>, + Option<&mut Self::Output>, + Option<&mut Self::Output>, + ) { let idx1 = K::to_index(Some(index1.borrow())); let idx2 = K::to_index(Some(index2.borrow())); let idx3 = K::to_index(Some(index3.borrow())); @@ -646,7 +643,11 @@ impl GetMut for Vec { index1: Q, index2: Q, index3: Q, - ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>) { + ) -> ( + Option<&mut Self::Output>, + Option<&mut Self::Output>, + Option<&mut Self::Output>, + ) { self.at3_mut(*index1.borrow(), *index2.borrow(), *index3.borrow()) } } diff --git a/src/types/list.rs b/src/types/list.rs index be84923..e6ca214 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -74,9 +74,12 @@ impl List { match self.head { Some(old_head) => { let (new_node, old_head_node) = storage.get2_mut(id, old_head); - let (new_node, old_head_node) = (new_node.ok_or(kerr!(NotFound))?, old_head_node.unwrap_or_else(|| { - bug!("node linked from list does not exist in storage."); - })); + let (new_node, old_head_node) = ( + new_node.ok_or(kerr!(NotFound))?, + old_head_node.unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + }), + ); new_node.links_mut().prev = None; new_node.links_mut().next = Some(old_head); @@ -97,7 +100,7 @@ impl List { } /// Pushes `id` to the back of the list. If `id` is already in the list, it is moved to the back. - /// + /// /// Errors if `id` does not exist in `storage` or if the node corresponding to `id` is linked but not in the list. pub fn push_back + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> where @@ -108,9 +111,12 @@ impl List { match self.tail { Some(old_tail) => { let (new_node, old_tail_node) = storage.get2_mut(id, old_tail); - let (new_node, old_tail_node) = (new_node.ok_or(kerr!(NotFound))?, old_tail_node.unwrap_or_else(|| { - bug!("node linked from list does not exist in storage."); - })); + let (new_node, old_tail_node) = ( + new_node.ok_or(kerr!(NotFound))?, + old_tail_node.unwrap_or_else(|| { + bug!("node linked from list does not exist in storage."); + }), + ); new_node.links_mut().next = None; new_node.links_mut().prev = Some(old_tail); @@ -230,7 +236,10 @@ mod tests { use core::borrow::Borrow; use super::{Linkable, Links, List}; - use crate::types::{array::IndexMap, traits::{Get, ToIndex}}; + use crate::types::{ + array::IndexMap, + traits::{Get, ToIndex}, + }; #[proc_macros::fmt] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -360,4 +369,4 @@ mod tests { assert_eq!(list.pop_front(&mut s).unwrap(), None); assert!(list.is_empty()); } -} \ No newline at end of file +} diff --git a/src/types/rbtree.rs b/src/types/rbtree.rs index 3a5b0f0..8e089cf 100644 --- a/src/types/rbtree.rs +++ b/src/types/rbtree.rs @@ -1,4 +1,4 @@ -use core::{marker::PhantomData}; +use core::marker::PhantomData; use crate::error::Result; @@ -53,8 +53,7 @@ enum Color { } #[allow(dead_code)] -impl RbTree -{ +impl RbTree { pub const fn new() -> Self { Self { root: None, @@ -64,9 +63,10 @@ impl RbTree } /// Inserts `id` into the tree. If `id` already exists in the tree, it is first removed and then re-inserted. Errors if `id` does not exist in `storage`. - pub fn insert< - S: Get + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare,{ + pub fn insert + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> + where + >::Output: Linkable + Compare, + { let already_linked = { let node = storage.get(id).ok_or(kerr!(NotFound))?; let links = node.links(); @@ -116,7 +116,7 @@ impl RbTree if node.cmp(last) == core::cmp::Ordering::Less { last.links_mut().left = Some(id); } else { - last.links_mut().right = Some(id); + last.links_mut().right = Some(id); } } } @@ -138,7 +138,9 @@ impl RbTree } pub fn remove + GetMut>(&mut self, id: T, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare { + where + >::Output: Linkable + Compare, + { let (node_left, node_right, node_parent, node_is_red, linked) = { let node = storage.get(id).ok_or(kerr!(NotFound))?; let links = node.links(); @@ -254,7 +256,9 @@ impl RbTree } fn insert_fixup + GetMut>(&mut self, mut id: T, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare, { + where + >::Output: Linkable + Compare, + { while let Some(parent) = storage.get(id).and_then(|n| n.links().parent) && storage .get(parent) @@ -300,7 +304,10 @@ impl RbTree id = old_parent; } - let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(kerr!(NotFound))?; + let parent = storage + .get(id) + .and_then(|n| n.links().parent) + .ok_or(kerr!(NotFound))?; let grandparent = storage .get(parent) .and_then(|n| n.links().parent) @@ -346,7 +353,10 @@ impl RbTree id = old_parent; } - let parent = storage.get(id).and_then(|n| n.links().parent).ok_or(kerr!(NotFound))?; + let parent = storage + .get(id) + .and_then(|n| n.links().parent) + .ok_or(kerr!(NotFound))?; let grandparent = storage .get(parent) .and_then(|n| n.links().parent) @@ -381,7 +391,9 @@ impl RbTree mut parent: Option, storage: &mut S, ) -> Result<()> - where >::Output: Linkable + Compare, { + where + >::Output: Linkable + Compare, + { let is_red = |node_id: Option, storage: &S| -> bool { node_id .and_then(|id| storage.get(id)) @@ -389,7 +401,7 @@ impl RbTree }; let is_black = |node_id: Option, storage: &S| -> bool { !is_red(node_id, storage) }; - + while id != self.root && is_black(id, storage) { let parent_id = parent.unwrap_or_else(|| { bug!("node linked from tree does not have a parent."); @@ -572,7 +584,9 @@ impl RbTree } fn minimum>(&self, mut id: T, storage: &S) -> Result - where >::Output: Linkable + Compare, { + where + >::Output: Linkable + Compare, + { loop { let left = storage.get(id).ok_or(kerr!(NotFound))?.links().left; match left { @@ -582,8 +596,15 @@ impl RbTree } } - fn transplant + GetMut>(&mut self, u: T, v: Option, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare, { + fn transplant + GetMut>( + &mut self, + u: T, + v: Option, + storage: &mut S, + ) -> Result<()> + where + >::Output: Linkable + Compare, + { let u_parent = storage.get(u).and_then(|n| n.links().parent); match u_parent { @@ -612,8 +633,15 @@ impl RbTree Ok(()) } - fn rotate_right + GetMut>(&mut self, pivot: T, left: T, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare, { + fn rotate_right + GetMut>( + &mut self, + pivot: T, + left: T, + storage: &mut S, + ) -> Result<()> + where + >::Output: Linkable + Compare, + { if pivot == left { return Err(kerr!(NotFound)); } @@ -665,8 +693,15 @@ impl RbTree Ok(()) } - fn rotate_left + GetMut>(&mut self, pivot: T, right: T, storage: &mut S) -> Result<()> - where >::Output: Linkable + Compare, { + fn rotate_left + GetMut>( + &mut self, + pivot: T, + right: T, + storage: &mut S, + ) -> Result<()> + where + >::Output: Linkable + Compare, + { if pivot == right { return Err(kerr!(NotFound)); } diff --git a/src/types/traits.rs b/src/types/traits.rs index aa8c951..ac53af9 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -10,8 +10,21 @@ pub trait GetMut: Get { fn get_mut>(&mut self, index: K) -> Option<&mut Self::Output>; // Getting multiple disjoint mutable references at once - fn get2_mut>(&mut self, index1: K, index2: K) -> (Option<&mut Self::Output>, Option<&mut Self::Output>); - fn get3_mut>(&mut self, index1: K, index2: K, index3: K) -> (Option<&mut Self::Output>, Option<&mut Self::Output>, Option<&mut Self::Output>); + fn get2_mut>( + &mut self, + index1: K, + index2: K, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>); + fn get3_mut>( + &mut self, + index1: K, + index2: K, + index3: K, + ) -> ( + Option<&mut Self::Output>, + Option<&mut Self::Output>, + Option<&mut Self::Output>, + ); } pub trait ToIndex { @@ -27,4 +40,4 @@ impl ToIndex for usize { pub trait Project

{ fn project(&self) -> Option<&P>; fn project_mut(&mut self) -> Option<&mut P>; -} \ No newline at end of file +} diff --git a/src/types/view.rs b/src/types/view.rs index de56096..28c4a85 100644 --- a/src/types/view.rs +++ b/src/types/view.rs @@ -11,7 +11,7 @@ where _k: PhantomData, _proj: PhantomData

, } - + impl<'a, K: ?Sized + ToIndex, P, S: GetMut> ViewMut<'a, K, P, S> where S::Output: Project

, @@ -57,7 +57,12 @@ where ) } - fn get3_mut>(&mut self, idx1: Q, idx2: Q, idx3: Q) -> (Option<&mut P>, Option<&mut P>, Option<&mut P>) { + fn get3_mut>( + &mut self, + idx1: Q, + idx2: Q, + idx3: Q, + ) -> (Option<&mut P>, Option<&mut P>, Option<&mut P>) { let (a, b, c) = self.data.get3_mut(idx1, idx2, idx3); ( a.and_then(Project::project_mut), @@ -65,4 +70,4 @@ where c.and_then(Project::project_mut), ) } -} \ No newline at end of file +} diff --git a/src/uapi.rs b/src/uapi.rs index 60a2b0e..577ab52 100644 --- a/src/uapi.rs +++ b/src/uapi.rs @@ -1,3 +1,3 @@ pub mod print; pub mod sched; -pub mod time; \ No newline at end of file +pub mod time; diff --git a/src/uapi/print.rs b/src/uapi/print.rs index d148107..748de2f 100644 --- a/src/uapi/print.rs +++ b/src/uapi/print.rs @@ -21,4 +21,4 @@ impl Write for Printer { hal::Machine::print(s).map_err(|_| fmt::Error)?; Ok(()) } -} \ No newline at end of file +} diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index ef39306..321889c 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -1,16 +1,16 @@ use hal::stack::EntryFn; pub fn sleep(until: u64) -> isize { - hal::asm::syscall!(1, (until >> 32) as u32, until as u32) + hal::asm::syscall!(1, (until >> 32) as u32, until as u32) } - + pub fn sleep_for(duration: u64) -> isize { - hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) + hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) } pub fn yield_thread() -> isize { - let until = u64::MAX; - hal::asm::syscall!(1, (until >> 32) as u32, until as u32) + let until = u64::MAX; + hal::asm::syscall!(1, (until >> 32) as u32, until as u32) } #[repr(C)] @@ -35,4 +35,4 @@ pub fn exit(code: usize) -> ! { loop { hal::asm::nop!(); } -} \ No newline at end of file +} diff --git a/src/uapi/time.rs b/src/uapi/time.rs index 1b63ebb..c44781b 100644 --- a/src/uapi/time.rs +++ b/src/uapi/time.rs @@ -6,4 +6,4 @@ pub fn mono_now() -> u64 { pub fn tick() -> u64 { time::tick() -} \ No newline at end of file +} diff --git a/xtasks/crates/config/src/main.rs b/xtasks/crates/config/src/main.rs index 00f1244..7cf642c 100644 --- a/xtasks/crates/config/src/main.rs +++ b/xtasks/crates/config/src/main.rs @@ -62,9 +62,7 @@ pub fn main() { } fn ask_confirmation(prompt: &str) -> bool { - print!("{}\n\n(y/N): ", - prompt - ); + print!("{}\n\n(y/N): ", prompt); if let Err(_) = std::io::Write::flush(&mut std::io::stdout()) { return false; @@ -91,7 +89,8 @@ fn run_load_preset(preset_name: &str, no_confirm: bool, current_dir: &Path) -> R // Ask for confirmation if !no_confirm - && !ask_confirmation(&format!("\nApply preset '{preset_name}' to '{}'?\nThis overwrites all existing configuration options.", + && !ask_confirmation(&format!( + "\nApply preset '{preset_name}' to '{}'?\nThis overwrites all existing configuration options.", config_path.display() )) { diff --git a/xtasks/crates/dtgen/src/ldgen.rs b/xtasks/crates/dtgen/src/ldgen.rs index e0b5041..075378d 100644 --- a/xtasks/crates/dtgen/src/ldgen.rs +++ b/xtasks/crates/dtgen/src/ldgen.rs @@ -14,19 +14,26 @@ fn format_memory_section(regions: &[(&str, u64, u64)]) -> String { .collect::>() .join("\n"); - indoc::formatdoc! {" - /* This file is @generated by dtgen. Do not edit. */ - MEMORY {{ - {regions} - }} - ", regions = regions} + format!("MEMORY\n{{\n{regions}\n}}") } fn format_irq_provides(num: u32) -> String { format!("PROVIDE(__irq_{}_handler = default_handler);", num) } -fn coalesce_regions<'a>(name: &'a str, regions: Vec<(&'a str, u64, u64)>) -> Result, String> { +fn format_irq_section(num_irqs: u32) -> String { + let provides = (0..num_irqs) + .map(format_irq_provides) + .collect::>() + .join("\n"); + + format!("{provides}") +} + +fn coalesce_regions<'a>( + name: &'a str, + regions: Vec<(&'a str, u64, u64)>, +) -> Result, String> { regions .clone() .into_iter() @@ -80,5 +87,10 @@ pub fn generate_ld(dt: &DeviceTree) -> Result { return Err("No memory regions found in device tree".to_string()); } - Ok(format_memory_section(®ions)) + // TODO: Derive the number of IRQs from the device tree. + Ok(format!( + "{}\n\n{}", + format_memory_section(®ions), + format_irq_section(240) + )) } diff --git a/xtasks/crates/dtgen/src/lib.rs b/xtasks/crates/dtgen/src/lib.rs index aa3d761..026b763 100644 --- a/xtasks/crates/dtgen/src/lib.rs +++ b/xtasks/crates/dtgen/src/lib.rs @@ -3,8 +3,8 @@ mod codegen; pub mod ir; -mod parser; mod ldgen; +mod parser; use std::path::Path; From 79183bc156de731ea0e222864daf66ff80f8e413 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 17:38:17 +0000 Subject: [PATCH 19/28] fix warnings --- Cargo.toml | 1 + build.rs | 4 +--- examples/hello-world/Cargo.toml | 11 ----------- machine/testing/src/asm.rs | 2 +- macros/src/lib.rs | 4 ++-- macros/src/logging.rs | 2 +- src/error.rs | 2 ++ src/lib.rs | 2 +- src/mem.rs | 1 + src/mem/pfa.rs | 2 ++ src/mem/pfa/bitset.rs | 2 +- src/mem/vmm.rs | 5 +++++ src/mem/vmm/nommu.rs | 1 + src/sched.rs | 11 ++++++++--- src/sched/rr.rs | 6 +++--- src/sched/rt.rs | 4 ++-- src/sched/task.rs | 4 ++++ src/sched/thread.rs | 5 +++++ src/sync/atomic.rs | 3 +++ src/syscalls/sched.rs | 4 ++-- src/types/heap.rs | 2 ++ src/types/list.rs | 2 +- src/uapi/sched.rs | 22 +++++++++++----------- 23 files changed, 60 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e3d8f3..4fa38a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ default = [] nightly = [] no-atomic-cas = [] multi-core = [] +error-msg = [] defmt = ["dep:defmt", "dep:defmt-rtt"] [build-dependencies] diff --git a/build.rs b/build.rs index f37cb9a..9807319 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ -use core::panic; -use std::process::Command; -use std::{collections::HashMap, fs, fs::File, path::Path, path::PathBuf}; +use std::{collections::HashMap, fs::File, path::Path}; extern crate rand; extern crate syn; diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index a86e875..c26ba3a 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -15,14 +15,3 @@ osiris = { workspace = true } [build-dependencies] cfg_aliases = "0.2.1" - -[profile.dev] -panic = "abort" -strip = false -opt-level = 2 - -[profile.release] -panic = "abort" -opt-level = "z" -codegen-units = 1 -lto = true diff --git a/machine/testing/src/asm.rs b/machine/testing/src/asm.rs index fbd7dd5..eb0a94b 100644 --- a/machine/testing/src/asm.rs +++ b/machine/testing/src/asm.rs @@ -31,7 +31,7 @@ pub fn are_interrupts_enabled() -> bool { } #[inline(always)] -pub fn enable_irq_restr(state: usize) {} +pub fn enable_irq_restr(_state: usize) {} #[macro_export] macro_rules! __macro_startup_trampoline { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 5308803..d91c44a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -17,7 +17,7 @@ pub fn derive_tagged_links(input: proc_macro::TokenStream) -> proc_macro::TokenS #[proc_macro_attribute] pub fn fmt( - args: proc_macro::TokenStream, + _args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(input as syn::DeriveInput); @@ -31,7 +31,7 @@ pub fn fmt( #[proc_macro_attribute] pub fn app_main( - input: proc_macro::TokenStream, + _attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let item = syn::parse_macro_input!(item as syn::ItemFn); diff --git a/macros/src/logging.rs b/macros/src/logging.rs index f462d12..8e2cfcc 100644 --- a/macros/src/logging.rs +++ b/macros/src/logging.rs @@ -1,4 +1,4 @@ -use syn::{DeriveInput, ItemFn}; +use syn::DeriveInput; pub fn derive_fmt(input: &DeriveInput) -> syn::Result { // Check if the env variable "OSIRIS_DEBUG_DEFMT" is set. If it is, generate a defmt::Format implementation. Otherwise, generate a Debug implementation. diff --git a/src/error.rs b/src/error.rs index 5311ec2..80ccc3c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -56,6 +56,7 @@ macro_rules! bug_on { }}; } +#[allow(unused_macros)] macro_rules! warn_on { ($cond:expr) => {{ let cond = $cond; @@ -91,6 +92,7 @@ macro_rules! kerr { } #[proc_macros::fmt] +#[allow(dead_code)] #[derive(Clone, PartialEq, Eq)] pub enum Kind { InvalidAlign, diff --git a/src/lib.rs b/src/lib.rs index bf3197b..9f1a352 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ pub unsafe extern "C" fn kernel_init() -> ! { idle::init(); kprintln!("Idle thread initialized."); - let (cyc, ns) = hal::Machine::bench_end(); + let (cyc, _ns) = hal::Machine::bench_end(); kprintln!("Kernel init took {} cycles.", cyc); // Start the init application. diff --git a/src/mem.rs b/src/mem.rs index c22042f..708a7a0 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -11,6 +11,7 @@ pub mod alloc; pub mod pfa; pub mod vmm; +#[allow(dead_code)] pub const BITS_PER_PTR: usize = core::mem::size_of::() * 8; unsafe extern "C" { diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs index 7390d3a..57caa37 100644 --- a/src/mem/pfa.rs +++ b/src/mem/pfa.rs @@ -30,6 +30,7 @@ trait Allocator { fn initializer() -> unsafe fn(PhysAddr, usize) -> Result>>; fn alloc(&mut self, page_count: usize) -> Option; + #[allow(dead_code)] fn free(&mut self, addr: PhysAddr, page_count: usize); } @@ -50,6 +51,7 @@ pub fn alloc_page(page_count: usize) -> Option { pfa.as_mut()?.alloc(page_count) } +#[allow(dead_code)] pub fn free_page(addr: PhysAddr, page_count: usize) { let mut pfa = PFA.lock(); if let Some(pfa) = pfa.as_mut() { diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 681ea8c..4a242a5 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -194,7 +194,7 @@ mod tests { fn test_random_pattern() { const ITARATIONS: usize = 1000; - for i in 0..ITARATIONS { + for _ in 0..ITARATIONS { const N: usize = 1024; const BITS: usize = Allocator::::BITS_PER_WORD; const ALLOC_SIZE: usize = 100; diff --git a/src/mem/vmm.rs b/src/mem/vmm.rs index 987b021..240d334 100644 --- a/src/mem/vmm.rs +++ b/src/mem/vmm.rs @@ -16,6 +16,7 @@ bitflags::bitflags! { } #[derive(Clone)] +#[allow(dead_code)] pub enum Backing { Zeroed, Uninit, @@ -23,6 +24,7 @@ pub enum Backing { } #[derive(Clone)] +#[allow(dead_code)] pub struct Region { start: Option, len: usize, @@ -47,6 +49,7 @@ impl Region { } } + #[allow(dead_code)] pub fn start(&self) -> VirtAddr { self.start.unwrap_or_else(|| VirtAddr::new(0)) } @@ -55,11 +58,13 @@ impl Region { self.len } + #[allow(dead_code)] pub fn contains(&self, addr: VirtAddr) -> bool { self.start().saturating_add(self.len()) > addr && addr >= self.start() } } +#[allow(dead_code)] pub trait AddressSpacelike { // Size is the amount of pages in the address space. On nommu systems this will be reserved. fn new(pages: usize) -> Result diff --git a/src/mem/vmm/nommu.rs b/src/mem/vmm/nommu.rs index 3a97984..b94b1d5 100644 --- a/src/mem/vmm/nommu.rs +++ b/src/mem/vmm/nommu.rs @@ -12,6 +12,7 @@ use crate::{ pub struct AddressSpace { begin: PhysAddr, + #[allow(dead_code)] end: PhysAddr, allocator: bestfit::BestFitAllocator, } diff --git a/src/sched.rs b/src/sched.rs index 4e5f130..2e9e301 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -162,7 +162,7 @@ impl Scheduler { }); if let Some(throttle) = throttle { - self.sleep_until(throttle, now); + let _ = self.sleep_until(throttle, now); return true; } @@ -235,6 +235,7 @@ impl Scheduler { Ok(()) } + #[allow(dead_code)] pub fn kick(&mut self, uid: thread::UId) -> Result<()> { WaiterView::::with(&mut self.threads, |view| { self.wakeup.remove(uid, view)?; @@ -256,6 +257,7 @@ impl Scheduler { self.rr_scheduler.dequeue(uid, &mut self.threads); } + #[allow(dead_code)] pub fn create_task(&mut self, task: &task::Attributes) -> Result { let uid = task::UId::new(self.id_gen).ok_or(kerr!(InvalidArgument))?; self.id_gen += 1; @@ -273,7 +275,8 @@ impl Scheduler { self.rt_scheduler.dequeue(id, view); }); self.rr_scheduler.dequeue(id, &mut self.threads); - self.wakeup + let _ = self + .wakeup .remove(id, &mut WaiterView::::new(&mut self.threads)); if task.threads_mut().remove(id, &mut self.threads).is_err() { @@ -315,7 +318,8 @@ impl Scheduler { pub fn kill_thread(&mut self, uid: Option) -> Result<()> { let uid = uid.unwrap_or(self.current.ok_or(kerr!(InvalidArgument))?); self.dequeue(uid); - self.wakeup + let _ = self + .wakeup .remove(uid, &mut WaiterView::::new(&mut self.threads)); self.tasks @@ -363,6 +367,7 @@ pub fn needs_reschedule(now: u64) -> bool { } #[inline] +#[allow(dead_code)] pub fn disable() { DISABLED.store(true, Ordering::Release); } diff --git a/src/sched/rr.rs b/src/sched/rr.rs index 0209544..e537c09 100644 --- a/src/sched/rr.rs +++ b/src/sched/rr.rs @@ -41,8 +41,8 @@ impl Scheduler { match self.current { Some(current) if self.current_left > 0 => return Some((current, self.current_left)), Some(current) => { - self.queue.pop_front(storage); - self.queue.push_back(current, storage); + let _ = self.queue.pop_front(storage); + let _ = self.queue.push_back(current, storage); self.current = self.queue.head(); self.current_left = self.quantum; @@ -57,7 +57,7 @@ impl Scheduler { } pub fn dequeue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) { - self.queue.remove(uid, storage); + let _ = self.queue.remove(uid, storage); if self.current == Some(uid) { self.current = None; diff --git a/src/sched/rt.rs b/src/sched/rt.rs index 5fe0b41..5795dbb 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -25,7 +25,7 @@ impl Scheduler { if let Some(server) = storage.get_mut(uid) { // Threads are only enqueued when they are runnable. server.on_wakeup(now); - self.edf.insert(uid, storage); + let _ = self.edf.insert(uid, storage); } } @@ -50,6 +50,6 @@ impl Scheduler { } pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) { - self.edf.remove(uid, storage); + let _ = self.edf.remove(uid, storage); } } diff --git a/src/sched/task.rs b/src/sched/task.rs index 3c74835..76b4dc5 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -34,6 +34,7 @@ pub struct UId { } impl UId { + #[allow(dead_code)] pub fn new(uid: usize) -> Option { if uid == 0 { None } else { Some(Self { uid }) } } @@ -55,6 +56,7 @@ impl Display for UId { } } +#[allow(dead_code)] pub struct Attributes { pub resrv_pgs: Option>, } @@ -72,6 +74,7 @@ pub struct Task { } impl Task { + #[allow(dead_code)] pub fn new(id: UId, attrs: &Attributes) -> Result { // TODO: On MMU systems, the resrv_pgs attribute will be ignored, as memory will not be reserved. let resrv_pgs = attrs.resrv_pgs.ok_or(kerr!(InvalidArgument))?; @@ -130,6 +133,7 @@ impl Task { Ok(tid.get_uid(uid)) } + #[allow(dead_code)] pub fn tid_cntr(&self) -> usize { self.tid_cntr } diff --git a/src/sched/thread.rs b/src/sched/thread.rs index 80d01f1..d4e4ccb 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -165,6 +165,7 @@ impl RtServer { } } + #[allow(dead_code)] pub fn budget_left(&self) -> u32 { self.budget_left } @@ -185,6 +186,7 @@ impl RtServer { } } + #[allow(dead_code)] pub fn replenish(&mut self) { self.deadline = self.deadline + self.period as u64; self.budget_left += self.budget; @@ -200,10 +202,12 @@ impl RtServer { None } + #[allow(dead_code)] pub fn deadline(&self) -> u64 { self.deadline } + #[allow(dead_code)] pub fn uid(&self) -> UId { self.uid } @@ -247,6 +251,7 @@ impl Waiter { self.until } + #[allow(dead_code)] pub fn set_until(&mut self, until: u64) { self.until = until; } diff --git a/src/sync/atomic.rs b/src/sync/atomic.rs index a1f54be..4180dac 100644 --- a/src/sync/atomic.rs +++ b/src/sync/atomic.rs @@ -56,6 +56,7 @@ impl AtomicU8 { } /// Compares the value and exchanges it. + #[allow(dead_code)] pub fn compare_exchange( &self, current: u8, @@ -131,6 +132,7 @@ pub struct AtomicU64 { unsafe impl Sync for AtomicU64 {} #[cfg(not(target_has_atomic = "64"))] +#[allow(dead_code)] impl AtomicU64 { /// Creates a new atomic u64. pub const fn new(value: u64) -> Self { @@ -193,6 +195,7 @@ impl AtomicU64 { } /// Fetches a value, applies the function and writes it back atomically. + #[allow(dead_code)] pub fn fetch_update(&self, _: Ordering, _: Ordering, mut f: F) -> Result where F: FnMut(u64) -> Option, diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index 6e1338b..e207d1c 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -56,7 +56,7 @@ fn spawn_thread(func_ptr: usize, attrs: *const RtAttrs) -> c_int { } #[syscall_handler(num = 4)] -fn exit(code: usize) -> c_int { +fn exit(_code: usize) -> c_int { sched::with(|sched| { if sched.kill_thread(None).is_err() { bug!("failed to terminate thread."); @@ -66,6 +66,6 @@ fn exit(code: usize) -> c_int { } #[syscall_handler(num = 5)] -fn kick_thread(uid: usize) -> c_int { +fn kick_thread(_uid: usize) -> c_int { 0 } diff --git a/src/types/heap.rs b/src/types/heap.rs index a25b1f8..2202c10 100644 --- a/src/types/heap.rs +++ b/src/types/heap.rs @@ -6,10 +6,12 @@ use super::array::Vec; /// An array-based binary heap, with N elements stored inline. #[proc_macros::fmt] +#[allow(dead_code)] pub struct BinaryHeap { vec: Vec, } +#[allow(dead_code)] impl BinaryHeap { /// Create a new empty binary heap. pub const fn new() -> Self { diff --git a/src/types/list.rs b/src/types/list.rs index e6ca214..d111967 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -314,7 +314,7 @@ mod tests { let mut list = List::::new(); list.push_back(Id(1), &mut s).unwrap(); - list.remove(Id(1), &mut s); + let _ = list.remove(Id(1), &mut s); assert_eq!(list.head(), None); assert_eq!(list.tail(), None); diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index 321889c..dd63848 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -1,16 +1,16 @@ use hal::stack::EntryFn; -pub fn sleep(until: u64) -> isize { - hal::asm::syscall!(1, (until >> 32) as u32, until as u32) +pub fn sleep(_until: u64) -> isize { + hal::asm::syscall!(1, (_until >> 32) as u32, _until as u32) } -pub fn sleep_for(duration: u64) -> isize { - hal::asm::syscall!(2, (duration >> 32) as u32, duration as u32) +pub fn sleep_for(_duration: u64) -> isize { + hal::asm::syscall!(2, (_duration >> 32) as u32, _duration as u32) } pub fn yield_thread() -> isize { - let until = u64::MAX; - hal::asm::syscall!(1, (until >> 32) as u32, until as u32) + let _until = u64::MAX; + hal::asm::syscall!(1, (_until >> 32) as u32, _until as u32) } #[repr(C)] @@ -21,17 +21,17 @@ pub struct RtAttrs { pub budget: u32, } -pub fn spawn_thread(func_ptr: EntryFn, attrs: Option) -> isize { - let attr_ptr = if let Some(attrs) = attrs { +pub fn spawn_thread(_func_ptr: EntryFn, attrs: Option) -> isize { + let _attr_ptr = if let Some(attrs) = attrs { &attrs as *const RtAttrs as usize } else { 0 }; - hal::asm::syscall!(3, func_ptr as u32, attr_ptr) + hal::asm::syscall!(3, _func_ptr as u32, _attr_ptr) } -pub fn exit(code: usize) -> ! { - hal::asm::syscall!(4, code as u32); +pub fn exit(_code: usize) -> ! { + hal::asm::syscall!(4, _code as u32); loop { hal::asm::nop!(); } From e40cf625f56ab96f237e917636245415ebb20a3e Mon Sep 17 00:00:00 2001 From: Thomas Wachter <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 19:44:21 +0200 Subject: [PATCH 20/28] Delete config.toml --- config.toml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 config.toml diff --git a/config.toml b/config.toml deleted file mode 100644 index 0c03ce5..0000000 --- a/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[env] -OSIRIS_STACKPAGES = "1" - -[build] -target = "host-tuple" From 6ac8567ce6378d0788491dbee547e24930f41135 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 17:45:40 +0000 Subject: [PATCH 21/28] fix devcontainer --- .devcontainer/devcontainer.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 175a961..89b2ba7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -36,9 +36,7 @@ "runArgs": [ // Mount USB devices under Linux "--device", - "/dev/bus/usb:/dev/bus/usb", - "--group-add", - "keep-groups" + "/dev/bus/usb:/dev/bus/usb" ], "mounts": [ // Make ssh keys available From 7ad64ae3e6e88ae00f4f4c2bf96a7d9bfd5e6e97 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:00:31 +0000 Subject: [PATCH 22/28] fix verify and fix config selection. --- .cargo/config.toml | 2 +- justfile | 3 ++- src/error.rs | 6 ++++++ src/mem/alloc/bestfit.rs | 9 +++++---- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index eb378db..49bd255 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ include = [ - "../config.toml" + { path = "../config.toml", optional = true } ] [alias] diff --git a/justfile b/justfile index 6e4afb1..9c55d85 100644 --- a/justfile +++ b/justfile @@ -12,7 +12,8 @@ fmt *args: cargo fmt {{args}} verify *args: - cargo kani -Z concrete-playback --concrete-playback=print -Z stubbing {{args}} + # This is giga hacky. But we need it until kani updates to the next version of cargo. + OSIRIS_STACKPAGES=1 cargo kani -Z concrete-playback --concrete-playback=print -Z stubbing {{args}} test *args: cargo test {{args}} diff --git a/src/error.rs b/src/error.rs index 80ccc3c..c8ebc8b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -179,3 +179,9 @@ impl From for Error { Self::new(Kind::Hal(e)) } } + +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind + } +} diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index 734c50c..9cf0f5e 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -682,7 +682,8 @@ mod tests { #[cfg(kani)] mod verification { use super::*; - use core::{alloc::Layout, ptr}; + use crate::mem::alloc::MAX_ADDR; + use crate::mem::alloc::Allocator; fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; @@ -692,14 +693,14 @@ mod verification { assert_eq!(meta.next, next); } - fn alloc_range(length: usize) -> Option> { + fn alloc_range(length: usize) -> Option> { let alloc_range = std::alloc::Layout::from_size_align(length, align_of::()).unwrap(); let ptr = unsafe { std::alloc::alloc(alloc_range) }; if ptr.is_null() || ((ptr as usize) >= isize::MAX as usize - length) { None } else { - Some(ptr as usize..ptr as usize + length) + Some(PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize + length)) } } @@ -717,7 +718,7 @@ mod verification { if let Some(range) = alloc_range(larger_size) { unsafe { - assert_eq!(allocator.add_range(range), Ok(())); + assert_eq!(allocator.add_range(&range), Ok(())); } let ptr = allocator.malloc(size, 1, None).unwrap(); From 33d720790845dbd335fa5b6dc0b908c11e7fe58d Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:12:41 +0000 Subject: [PATCH 23/28] format --- src/mem/alloc/bestfit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index 9cf0f5e..ac079ff 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -682,8 +682,8 @@ mod tests { #[cfg(kani)] mod verification { use super::*; - use crate::mem::alloc::MAX_ADDR; use crate::mem::alloc::Allocator; + use crate::mem::alloc::MAX_ADDR; fn verify_block(user_ptr: NonNull, size: usize, next: Option>) { let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) }; From 93aa42dae78bdc0e66228e6ae8b87638c4599576 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:23:37 +0000 Subject: [PATCH 24/28] update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c769c48..96c1f43 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Osiris -An RTOS for embedded systems. This is still a very early work in progress. +An RTOS for embedded systems. + +**⚠️ WARNING: This is very experimental software. Use at your own risk. ⚠️** ## Project Structure From 99bdf3797047363e57eb3a1ef62aa783874404c4 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:13:41 +0000 Subject: [PATCH 25/28] fix bugs. --- src/mem/alloc/bestfit.rs | 37 +++++++++++++++++++---- src/mem/pfa/bitset.rs | 13 ++++++++- src/types/array.rs | 63 ++++++++++++++++++++++------------------ src/uapi/sched.rs | 21 ++++++++++---- 4 files changed, 95 insertions(+), 39 deletions(-) diff --git a/src/mem/alloc/bestfit.rs b/src/mem/alloc/bestfit.rs index ac079ff..584bc3a 100644 --- a/src/mem/alloc/bestfit.rs +++ b/src/mem/alloc/bestfit.rs @@ -340,13 +340,12 @@ impl super::Allocator for BestFitAllocator { // Check if the size of the block is correct. bug_on!( - meta.size != super::super::align_up(size), - "Invalid size in free()" + size > meta.size, + "allocation size {} is larger than block size {}", + size, + meta.size ); - // Set the size of the block. - meta.size = size; - // Set the block as the new head. self.head = Some(block); } @@ -357,6 +356,7 @@ impl super::Allocator for BestFitAllocator { #[cfg(test)] mod tests { use crate::error::Kind; + use crate::mem::align_up; use super::super::*; use super::*; @@ -644,6 +644,33 @@ mod tests { verify_ptrs_not_overlaping(ptrs.as_slice()); } + #[test] + fn free_corrupts_metadata() { + let mut allocator = BestFitAllocator::new(); + const SIZE: usize = 17; + const ALIGNED: usize = 32; + assert!(align_up(SIZE) == ALIGNED); + + let range = alloc_range(ALIGNED + size_of::() + BestFitAllocator::align_up()); + unsafe { + allocator.add_range(&range).unwrap(); + } + + let ptr1: core::ptr::NonNull = allocator.malloc(SIZE, 1, None).unwrap(); + + unsafe { + allocator.free(ptr1, SIZE); + } + + let ptr2: core::ptr::NonNull = allocator + .malloc(SIZE, 1, None) + .expect("second malloc should succeed via fallback path"); + + unsafe { + allocator.free(ptr2, SIZE); + } + } + #[test] fn multi_range_oom() { // This function allocates multiple ranges and then frees one of them randomly. And only then there is no oom. diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 4a242a5..442b38c 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -128,7 +128,7 @@ impl super::Allocator for Allocator { // Mark all bits in the first word as used. { let skip = start % Self::BITS_PER_WORD; - let rem = len.min(Self::BITS_PER_WORD) - skip; + let rem = (Self::BITS_PER_WORD - skip).min(len); self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); @@ -190,6 +190,17 @@ impl super::Allocator for Allocator { mod tests { use super::*; + #[test] + fn last_bit_underflow() { + // Only the last page in word 0 is free + let mut allocator = Allocator::<1>::new(PhysAddr::new(0)).unwrap(); + allocator.l1[0] = 1; + + let result = super::super::Allocator::alloc(&mut allocator, 1); + + assert!(result.is_some()); + } + #[test] fn test_random_pattern() { const ITARATIONS: usize = 1000; diff --git a/src/types/array.rs b/src/types/array.rs index ad9d651..daefc06 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -80,31 +80,6 @@ impl IndexMap { self.data.iter() } - /// Get an cycling iterator over the elements in the map, starting from the given index. - /// - /// `index` - The index to start the iterator from. - /// - /// Returns an iterator over the elements in the map. - pub fn iter_from_cycle(&self, idx: Option<&K>) -> impl Iterator> { - self.data.iter().cycle().skip(K::to_index(idx) + 1) - } - - /// Get the next index that contains a value (this will cycle). - /// - /// `index` - The index to start the search from. - /// - /// Returns the next index (potentially < index) that contains a value, otherwise `None`. - pub fn next(&self, idx: Option<&K>) -> Option { - for (i, elem) in self.iter_from_cycle(idx).enumerate() { - if elem.is_some() { - let idx = K::to_index(idx); - return Some((idx + i + 1) % N); - } - } - - None - } - pub fn raw_at(&self, idx: usize) -> Option<&V> { if idx < N { self.data[idx].as_ref() @@ -259,7 +234,7 @@ impl Vec { } // If we don't have enough space, we need to grow the extra storage. - let grow = additional - N + len_extra; + let grow = self.len + additional - N; let mut new_extra = Box::new_slice_uninit(grow)?; // Check that the new extra storage has the requested length. @@ -446,7 +421,7 @@ impl Vec { /// Returns `Some(&T)` if the index is in-bounds, otherwise `None`. pub fn at(&self, index: usize) -> Option<&T> { // Check if the index is in-bounds. - if index > self.len - 1 { + if index >= self.len { return None; } @@ -594,7 +569,7 @@ impl Drop for Vec { } // Drop all elements in the extra storage. - for elem in &mut (*self.extra)[0..self.len - N] { + for elem in &mut (*self.extra)[0..self.len - min] { // Safety: the elements until self.len - N are initialized. unsafe { elem.assume_init_drop(); @@ -651,3 +626,35 @@ impl GetMut for Vec { self.at3_mut(*index1.borrow(), *index2.borrow(), *index3.borrow()) } } + +#[cfg(test)] +mod tests { + use super::Vec; + + #[test] + fn no_length_underflow() { + let vec = Vec::::new(); + assert!(vec.len() == 0); + assert_eq!(vec.at(0), None); + } + + #[test] + fn reserve_works() { + let mut vec = Vec::::new(); + for i in 0..7usize { + vec.push(i).unwrap(); + } + assert_eq!(vec.len(), 7); + + let _ = vec.reserve(2); + } + + #[test] + fn drop_underflow() { + let mut vec = Vec::::new(); + for i in 0..7usize { + vec.push(i).unwrap(); + } + drop(vec); + } +} diff --git a/src/uapi/sched.rs b/src/uapi/sched.rs index dd63848..e3f2601 100644 --- a/src/uapi/sched.rs +++ b/src/uapi/sched.rs @@ -22,12 +22,23 @@ pub struct RtAttrs { } pub fn spawn_thread(_func_ptr: EntryFn, attrs: Option) -> isize { - let _attr_ptr = if let Some(attrs) = attrs { - &attrs as *const RtAttrs as usize + if let Some(attrs) = attrs { + if attrs.budget == 0 || attrs.period == 0 { + return -1; // Invalid attributes + } + + if attrs.budget > attrs.period { + return -1; // Budget cannot exceed period + } + + if attrs.budget > u32::MAX / 2 || attrs.period > u32::MAX / 2 { + return -1; // Prevent potential overflow in calculations + } + + hal::asm::syscall!(3, _func_ptr as u32, &attrs as *const RtAttrs as usize) } else { - 0 - }; - hal::asm::syscall!(3, _func_ptr as u32, _attr_ptr) + -1 + } } pub fn exit(_code: usize) -> ! { From 33086c7685084d1497e88f75c2c9044d7b3b40d5 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:10:40 +0000 Subject: [PATCH 26/28] fix bugs, inconsistencies and add more docs. --- src/mem/pfa/bitset.rs | 188 ++-------------------------------- src/sched.rs | 158 +++++++++++++++++++---------- src/sched/rr.rs | 18 +++- src/sched/rt.rs | 15 ++- src/sched/task.rs | 61 +++++------ src/sched/thread.rs | 6 +- src/syscalls/sched.rs | 3 +- src/types.rs | 1 + src/types/array.rs | 185 ++++++++++++++++++++++++++++------ src/types/bitset.rs | 228 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 552 insertions(+), 311 deletions(-) create mode 100644 src/types/bitset.rs diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 442b38c..b24432b 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -5,17 +5,18 @@ use hal::mem::PhysAddr; use crate::{ error::Result, - types::boxed::{self, Box}, + types::{ + bitset::BitAlloc, + boxed::{self, Box}, + }, }; pub struct Allocator { begin: PhysAddr, - l1: [usize; N], + bitalloc: BitAlloc, } impl Allocator { - const BITS_PER_WORD: usize = usize::BITS as usize; - pub fn new(begin: PhysAddr) -> Option { if !begin.is_multiple_of(super::PAGE_SIZE) { return None; @@ -27,7 +28,7 @@ impl Allocator { Some(Self { begin, - l1: [!0; N], // All bits are set to 1, meaning all pages are free. + bitalloc: BitAlloc::new(N * BitAlloc::::BITS_PER_WORD)?, }) } } @@ -65,184 +66,15 @@ impl super::Allocator for Allocator { } fn alloc(&mut self, page_count: usize) -> Option { - // If a bit is 1 the page is free. If a bit is 0 the page is allocated. - let mut start = 0; - let mut len = 0usize; - - let rem = page_count.saturating_sub(Self::BITS_PER_WORD); - let mask = (!0usize).unbounded_shl((Self::BITS_PER_WORD.saturating_sub(page_count)) as u32); - - for idx in 0..N { - if self.l1[idx] == 0 { - len = 0; - continue; - } - - let mut byte = self.l1[idx]; - - let mut shift = if len > 0 { - 0usize - } else { - byte.leading_zeros() as usize - }; - - byte <<= shift; - - while shift < Self::BITS_PER_WORD { - // Make the mask smaller if we already have some contiguous bits. - let mask = if rem.saturating_sub(len) == 0 { - mask << (len - rem) - } else { - mask - }; - - // We shifted byte to MSB, mask is already aligned to the left. - // We compare them via and and shift to the right to shift out extra bits from the mask that would overflow into the next word. - let mut found = (byte & mask) >> shift; - - // We also need to shift the mask to the right so that we can compare mask and found. - if found == (mask >> shift) { - if len == 0 { - start = idx * Self::BITS_PER_WORD + shift; - } - - // Shift completely to the right. - found >>= found.trailing_zeros(); - - // As all found bits are now on the right we can just count them to get the amount we found. - len += found.trailing_ones() as usize; - // Continue to the next word if we haven't found enough bits yet. - break; - } else { - len = 0; - } - - shift += 1; - byte <<= 1; - } - - if len >= page_count { - // Mark the allocated pages as used. - let mut idx = start / Self::BITS_PER_WORD; - - // Mark all bits in the first word as used. - { - let skip = start % Self::BITS_PER_WORD; - let rem = (Self::BITS_PER_WORD - skip).min(len); - - self.l1[idx] &= - !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); - - if len <= rem { - return Some(self.begin + (start * super::PAGE_SIZE)); - } - - len -= rem; - idx += 1; - } - - // Mark all bits in the middle words as used. - { - let mid_cnt = len / Self::BITS_PER_WORD; - - for i in 0..mid_cnt { - self.l1[idx + i] = 0; - } - - idx += mid_cnt; - } - - // Mark the remaining bits in the last word as used. - self.l1[idx] &= !((!0usize) - .unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); - return Some(self.begin + (start * super::PAGE_SIZE)); - } - } - - None + let idx = self.bitalloc.alloc(page_count)?; + Some(self.begin + (idx * super::PAGE_SIZE)) } fn free(&mut self, addr: PhysAddr, page_count: usize) { if !addr.is_multiple_of(super::PAGE_SIZE) { panic!("Address must be page aligned"); } - - let mut idx = - (addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE / Self::BITS_PER_WORD; - let mut bit_idx = - ((addr.as_usize() - self.begin.as_usize()) / super::PAGE_SIZE) % Self::BITS_PER_WORD; - - // TODO: slow - for _ in 0..page_count { - self.l1[idx] |= 1 << (Self::BITS_PER_WORD - 1 - bit_idx); - - bit_idx += 1; - - if bit_idx == Self::BITS_PER_WORD { - bit_idx = 0; - idx += 1; - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn last_bit_underflow() { - // Only the last page in word 0 is free - let mut allocator = Allocator::<1>::new(PhysAddr::new(0)).unwrap(); - allocator.l1[0] = 1; - - let result = super::super::Allocator::alloc(&mut allocator, 1); - - assert!(result.is_some()); - } - - #[test] - fn test_random_pattern() { - const ITARATIONS: usize = 1000; - - for _ in 0..ITARATIONS { - const N: usize = 1024; - const BITS: usize = Allocator::::BITS_PER_WORD; - const ALLOC_SIZE: usize = 100; - - let mut allocator = Allocator::::new(PhysAddr::new(0x0)).unwrap(); - - // Generate a random bit pattern. - for i in 0..N { - let is_zero = rand::random::(); - - if is_zero { - allocator.l1[i / BITS] &= !(1 << ((BITS - 1) - (i % BITS))); - } - } - - // Place a run of ALLOC_SIZE contiguous bits set to 1 at a random position. - let start = rand::random::() % (N - ALLOC_SIZE); - for i in start..(start + ALLOC_SIZE) { - allocator.l1[i / BITS] |= 1 << ((BITS - 1) - (i % BITS)); - } - - let pre = allocator.l1.clone(); - - let addr = super::super::Allocator::alloc(&mut allocator, ALLOC_SIZE).unwrap(); - let idx = addr.as_usize() / super::super::PAGE_SIZE; - - // Check that the bits in returned addresses is all ones in pre. - for i in 0..ALLOC_SIZE { - let bit = (pre[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; - assert_eq!(bit, 1, "Bit at index {} is not set", idx + i); - } - - // Check that the bits in returned addresses is all zeros in allocator.l1. - for i in 0..ALLOC_SIZE { - let bit = (allocator.l1[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; - assert_eq!(bit, 0, "Bit at index {} is not cleared", idx + i); - } - } + let idx = addr.diff(self.begin) / super::PAGE_SIZE; + self.bitalloc.free(idx, page_count); } } diff --git a/src/sched.rs b/src/sched.rs index 2e9e301..8ebfecc 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -20,15 +20,15 @@ use crate::{ sync::{self, atomic::AtomicU64, spinlock::SpinLocked}, time::{self}, types::{ - array::IndexMap, + array::BitReclaimMap, rbtree::RbTree, traits::{Get, GetMut}, view::ViewMut, }, }; -type ThreadMap = IndexMap; -type TaskMap = IndexMap; +type ThreadMap = BitReclaimMap; +type TaskMap = BitReclaimMap; type GlobalScheduler = Scheduler<32>; @@ -42,7 +42,6 @@ type WaiterView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::Waiter, T pub struct Scheduler { threads: ThreadMap, tasks: TaskMap, - id_gen: usize, rt_scheduler: rt::Scheduler, rr_scheduler: rr::Scheduler, @@ -53,12 +52,26 @@ pub struct Scheduler { last_tick: u64, } +/// We define dequeue as a macro in order to avoid borrow checker issues. +macro_rules! dequeue { + ($self:expr, $uid:expr) => { + rt::ServerView::::with(&mut $self.threads, |view| { + $self.rt_scheduler.dequeue($uid, view) + }) + .or_else(|_| $self.rr_scheduler.dequeue($uid, &mut $self.threads)) + .or_else(|_| { + $self + .wakeup + .remove($uid, &mut WaiterView::::new(&mut $self.threads)) + }) + }; +} + impl Scheduler { - pub const fn new() -> Self { + const fn new() -> Self { Self { - threads: IndexMap::new(), - tasks: IndexMap::new(), - id_gen: 1, + threads: ThreadMap::new(), + tasks: TaskMap::new(), rt_scheduler: rt::Scheduler::new(), rr_scheduler: rr::Scheduler::new(), wakeup: RbTree::new(), @@ -83,9 +96,7 @@ impl Scheduler { } if let Some(task_id) = kill { - self.dequeue(current); - self.current = None; - if self.kill_task(task_id).is_err() { + if self.kill_by_task(task_id).is_err() { // Should not be possible. The thread exists, so the task must exist. bug!("failed to kill task {}", task_id); } @@ -94,6 +105,10 @@ impl Scheduler { } /// Triggers a reschedule at *latest* when we hit timepoint `next`. + /// Note that we may reschedule earlier than `next` if another thread wakes up or is enqueued, but we will never reschedule later than `next`. + /// + /// `now` - The current timepoint, in ticks. + /// `next` - The next timepoint to reschedule at, in ticks. fn next_resched(now: u64, next: u64) { let old = NEXT_TICK.load(Ordering::Acquire); @@ -104,12 +119,18 @@ impl Scheduler { NEXT_TICK.store(next, Ordering::Release); } + /// Enqueues a thread into the scheduler. This will trigger a reschedule. + /// + /// `uid` - The UID of the thread to enqueue. + /// `now` - The current timepoint, in ticks. This is used for RT threads to calculate their deadlines. + /// + /// Returns an error if the thread does not exist. pub fn enqueue(&mut self, now: u64, uid: thread::UId) -> Result<()> { let thread = self.threads.get(uid).ok_or(kerr!(InvalidArgument))?; if thread.rt_server().is_some() { let mut view = rt::ServerView::::new(&mut self.threads); - self.rt_scheduler.enqueue(uid, now, &mut view); + self.rt_scheduler.enqueue(uid, now, &mut view)?; } else { if self.rr_scheduler.enqueue(uid, &mut self.threads).is_err() { // This should not be possible. @@ -179,7 +200,8 @@ impl Scheduler { .unwrap_or((thread::IDLE_THREAD, 1000)) } - pub fn do_sched(&mut self, now: u64) -> Option<(*mut c_void, &mut task::Task)> { + /// Picks the next thread to run and returns its context and task. This should only be called by sched_enter after land. + fn do_sched(&mut self, now: u64) -> Option<(*mut c_void, &mut task::Task)> { // Sync the new state to the scheduler. if self.sync_to_sched(now) { // Trigger reschedule after interrupts are enabled. @@ -205,6 +227,12 @@ impl Scheduler { Some((ctx, task)) } + /// Puts the current thread to sleep until the specified timepoint. This will trigger a reschedule. + /// + /// `until` - The timepoint to sleep until, in ticks. This is an absolute time, not a relative time. + /// `now` - The current timepoint, in ticks. + /// + /// Returns an error if there is no current thread, it is not enqueued, or if the specified timepoint is in the past. pub fn sleep_until(&mut self, until: u64, now: u64) -> Result<()> { if until <= now { return Ok(()); @@ -221,6 +249,8 @@ impl Scheduler { ); } + dequeue!(self, uid)?; + if self .wakeup .insert(uid, &mut WaiterView::::new(&mut self.threads)) @@ -230,54 +260,51 @@ impl Scheduler { bug!("failed to insert thread {} into wakeup tree.", uid); } - self.dequeue(uid); reschedule(); Ok(()) } - #[allow(dead_code)] + /// If the thread is currently sleeping, this will trigger a wakeup on the next reschedule. Note this does not trigger an immediate reschedule. + /// + /// Returns an error if the thread does not exist, or if the thread is not currently sleeping. pub fn kick(&mut self, uid: thread::UId) -> Result<()> { WaiterView::::with(&mut self.threads, |view| { self.wakeup.remove(uid, view)?; let thread = view.get_mut(uid).unwrap_or_else(|| { + // This should not be possible. The thread must exist since it's in the wakeup tree. bug!("failed to get thread {} from wakeup tree.", uid); }); thread.set_until(0); self.wakeup.insert(uid, view).unwrap_or_else(|_| { + // This should not be possible. The thread exists and we just removed it from the wakeup tree, so it must be able to be re-inserted. bug!("failed to re-insert thread {} into wakeup tree.", uid); }); Ok(()) }) } - pub fn dequeue(&mut self, uid: thread::UId) { - rt::ServerView::::with(&mut self.threads, |view| { - self.rt_scheduler.dequeue(uid, view); - }); - self.rr_scheduler.dequeue(uid, &mut self.threads); + /// This will just remove the thread from the scheduler, but it will not trigger a reschedule, even if the thread is currently running. + /// + /// Returns an error if the thread does not exist, or if the thread is not currently enqueued in any scheduler. + pub fn dequeue(&mut self, uid: thread::UId) -> Result<()> { + dequeue!(self, uid) } - #[allow(dead_code)] - pub fn create_task(&mut self, task: &task::Attributes) -> Result { - let uid = task::UId::new(self.id_gen).ok_or(kerr!(InvalidArgument))?; - self.id_gen += 1; - - self.tasks.insert(&uid, task::Task::new(uid, task)?)?; - Ok(uid) + pub fn create_task(&mut self, attrs: task::Attributes) -> Result { + self.tasks.insert_with(|idx| { + let task = task::Task::new(task::UId::new(idx), attrs); + task.map(|t| (task::UId::new(idx), t)) + }) } - pub fn kill_task(&mut self, uid: task::UId) -> Result<()> { + /// Dequeues all threads of the task and removes the task. If the current thread belongs to the task, reschedule will be triggered. + /// + /// If the task does not exist, an error will be returned. + pub fn kill_by_task(&mut self, uid: task::UId) -> Result<()> { let task = self.tasks.get_mut(uid).ok_or(kerr!(InvalidArgument))?; while let Some(id) = task.threads().head() { - // Borrow checker... - rt::ServerView::::with(&mut self.threads, |view| { - self.rt_scheduler.dequeue(id, view); - }); - self.rr_scheduler.dequeue(id, &mut self.threads); - let _ = self - .wakeup - .remove(id, &mut WaiterView::::new(&mut self.threads)); + dequeue!(self, id)?; if task.threads_mut().remove(id, &mut self.threads).is_err() { // This should not be possible. The thread ID is from the thread list of the task, so it must exist. @@ -309,18 +336,28 @@ impl Scheduler { None => self.current.ok_or(kerr!(InvalidArgument))?.owner(), }; let task = self.tasks.get_mut(task).ok_or(kerr!(InvalidArgument))?; - let uid = task.create_thread(self.id_gen, attrs, &mut self.threads)?; - self.id_gen += 1; - Ok(uid) + self.threads + .insert_with(|idx| { + let uid = task.allocate_tid().get_uid(idx); + let stack = task.allocate_stack(attrs)?; + let thread = thread::Thread::new(uid, stack, attrs.attrs); + Ok((uid, thread)) + }) + .and_then(|k| { + task.register_thread(k, &mut self.threads)?; + Ok(k) + }) } - pub fn kill_thread(&mut self, uid: Option) -> Result<()> { + /// Dequeues a thread and removes it from its corresponding task. If the thread is currently running, reschedule will be triggered. + /// + /// `uid` - The UID of the thread to kill, or None to kill the current thread. + /// + /// If the thread does not exist, or if `uid` is None and there is no current thread, an error will be returned. + pub fn kill_by_thread(&mut self, uid: Option) -> Result<()> { let uid = uid.unwrap_or(self.current.ok_or(kerr!(InvalidArgument))?); - self.dequeue(uid); - let _ = self - .wakeup - .remove(uid, &mut WaiterView::::new(&mut self.threads)); + self.dequeue(uid)?; self.tasks .get_mut(uid.tid().owner()) @@ -338,6 +375,8 @@ impl Scheduler { } } +/// This function provides safe access to the global scheduler. +/// It disables interrupts and locks the scheduler. Use with caution! pub fn with T>(f: F) -> T { sync::atomic::irq_free(|| { let mut sched = SCHED.lock(); @@ -345,19 +384,27 @@ pub fn with T>(f: F) -> T { }) } +/// Initializes the scheduler. This should be called once during kernel initialization, before any threads are created. +/// +/// `kaddr_space` - The address space of the kernel task. This is used to create the kernel task, which is required for the scheduler to function. +/// +/// If the kernel task cannot be created, this function will panic. Note that the kernel task is essential for the system to function, so we cannot continue without it. pub fn init(kaddr_space: mem::vmm::AddressSpace) { with(|sched| { - let uid = task::KERNEL_TASK; - if let Ok(task) = task::Task::from_addr_space(uid, kaddr_space) { - if sched.tasks.insert(&uid, task).is_err() { - panic!("failed to create kernel task."); - } - } else { - panic!("failed to create kernel address space."); - } + let attrs = task::Attributes { + resrv_pgs: None, + address_space: Some(kaddr_space), + }; + + sched.create_task(attrs).unwrap_or_else(|e| { + panic!("failed to create kernel task: {}", e); + }); }) } +/// This should be called on each timer tick, and if it returns true, sched_enter should be called to reschedule. +/// +/// `now` - The current timepoint, in ticks. pub fn needs_reschedule(now: u64) -> bool { if DISABLED.load(Ordering::Acquire) { return false; @@ -366,6 +413,7 @@ pub fn needs_reschedule(now: u64) -> bool { now >= NEXT_TICK.load(Ordering::Acquire) } +/// This will disable rescheduling until the next call to enable. Use with caution! #[inline] #[allow(dead_code)] pub fn disable() { @@ -377,7 +425,8 @@ pub fn enable() { DISABLED.store(false, Ordering::Release); } -/// Reschedule the tasks. +/// Triggers a reschedule immediately, when interrupts are enabled. +/// This must be called after enqueueing a thread, or after waking up a thread, or putting the current thread to sleep. pub fn reschedule() { if DISABLED.load(Ordering::Acquire) { return; @@ -386,8 +435,7 @@ pub fn reschedule() { hal::Machine::trigger_reschedule(); } -/// cbindgen:ignore -/// cbindgen:no-export +/// This will be called by the architecture-specific code to enter the scheduler. It will land the current thread, pick the next thread to run, and return its context and task. #[unsafe(no_mangle)] pub extern "C" fn sched_enter(mut ctx: *mut c_void) -> *mut c_void { with(|sched| { diff --git a/src/sched/rr.rs b/src/sched/rr.rs index e537c09..e75a678 100644 --- a/src/sched/rr.rs +++ b/src/sched/rr.rs @@ -41,8 +41,16 @@ impl Scheduler { match self.current { Some(current) if self.current_left > 0 => return Some((current, self.current_left)), Some(current) => { - let _ = self.queue.pop_front(storage); - let _ = self.queue.push_back(current, storage); + if self.queue.pop_front(storage).is_err() { + // If this happens, it means the internal state was corrupted. + // We cannot meaningfully continue, so we panic. + bug!("current thread not found in queue"); + } + if self.queue.push_back(current, storage).is_err() { + // We popped the current thread from the queue, so it must be able to be pushed back. + // The scheduler is run exclusively so the space cannot be taken by another thread. + bug!("cannot push current thread back to queue"); + } self.current = self.queue.head(); self.current_left = self.quantum; @@ -56,11 +64,13 @@ impl Scheduler { self.current.map(|id| (id, self.current_left)) } - pub fn dequeue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) { - let _ = self.queue.remove(uid, storage); + pub fn dequeue(&mut self, uid: thread::UId, storage: &mut super::ThreadMap) -> Result<()> { + self.queue.remove(uid, storage)?; if self.current == Some(uid) { self.current = None; } + + Ok(()) } } diff --git a/src/sched/rt.rs b/src/sched/rt.rs index 5795dbb..2b416cd 100644 --- a/src/sched/rt.rs +++ b/src/sched/rt.rs @@ -1,4 +1,5 @@ use crate::{ + error::Result, sched::{ ThreadMap, thread::{self}, @@ -21,12 +22,18 @@ impl Scheduler { Self { edf: RbTree::new() } } - pub fn enqueue(&mut self, uid: thread::UId, now: u64, storage: &mut ServerView) { + pub fn enqueue( + &mut self, + uid: thread::UId, + now: u64, + storage: &mut ServerView, + ) -> Result<()> { if let Some(server) = storage.get_mut(uid) { // Threads are only enqueued when they are runnable. server.on_wakeup(now); - let _ = self.edf.insert(uid, storage); + self.edf.insert(uid, storage)?; } + Ok(()) } /// This should be called on each do_schedule call, to update the internal scheduler state. @@ -49,7 +56,7 @@ impl Scheduler { .and_then(|id| storage.get(id).map(|s| (id, s.budget()))) } - pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) { - let _ = self.edf.remove(uid, storage); + pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView) -> Result<()> { + self.edf.remove(uid, storage) } } diff --git a/src/sched/task.rs b/src/sched/task.rs index 76b4dc5..f7209cd 100644 --- a/src/sched/task.rs +++ b/src/sched/task.rs @@ -4,14 +4,13 @@ use core::fmt::Display; use core::num::NonZero; use envparse::parse_env; -use hal::Stack; use hal::stack::Stacklike; use crate::error::Result; +use crate::mem; use crate::sched::{ThreadMap, thread}; use crate::types::list; -use crate::{mem, sched}; use crate::mem::vmm::AddressSpacelike; use crate::types::traits::ToIndex; @@ -34,9 +33,8 @@ pub struct UId { } impl UId { - #[allow(dead_code)] - pub fn new(uid: usize) -> Option { - if uid == 0 { None } else { Some(Self { uid }) } + pub fn new(uid: usize) -> Self { + Self { uid } } pub fn is_kernel(&self) -> bool { @@ -59,6 +57,7 @@ impl Display for UId { #[allow(dead_code)] pub struct Attributes { pub resrv_pgs: Option>, + pub address_space: Option, } /// The struct representing a task. @@ -74,15 +73,15 @@ pub struct Task { } impl Task { - #[allow(dead_code)] - pub fn new(id: UId, attrs: &Attributes) -> Result { - // TODO: On MMU systems, the resrv_pgs attribute will be ignored, as memory will not be reserved. - let resrv_pgs = attrs.resrv_pgs.ok_or(kerr!(InvalidArgument))?; - let address_space = mem::vmm::AddressSpace::new(resrv_pgs.get())?; - Self::from_addr_space(id, address_space) - } + pub fn new(id: UId, attrs: Attributes) -> Result { + let address_space = match attrs.address_space { + Some(addr_space) => addr_space, + None => { + let resrv_pgs = attrs.resrv_pgs.ok_or(kerr!(InvalidArgument))?; + mem::vmm::AddressSpace::new(resrv_pgs.get())? + } + }; - pub fn from_addr_space(id: UId, address_space: mem::vmm::AddressSpace) -> Result { Ok(Self { id, address_space, @@ -91,14 +90,13 @@ impl Task { }) } - fn allocate_tid(&mut self) -> sched::thread::Id { + pub fn allocate_tid(&mut self) -> thread::Id { let tid = self.tid_cntr; self.tid_cntr += 1; - - sched::thread::Id::new(tid, self.id) + thread::Id::new(tid, self.id) } - fn allocate_stack(&mut self, attrs: &thread::Attributes) -> Result { + pub fn allocate_stack(&mut self, attrs: &thread::Attributes) -> Result { let size = DEFAULTS.stack_pages * mem::pfa::PAGE_SIZE; let region = mem::vmm::Region::new( None, @@ -108,29 +106,22 @@ impl Task { ); let pa = self.address_space.map(region)?; - Ok(hal::stack::Descriptor { - top: pa + size, - size: NonZero::new(size).unwrap(), - entry: attrs.entry, - fin: attrs.fin, + Ok(unsafe { + hal::Stack::new(hal::stack::Descriptor { + top: pa + size, + size: NonZero::new(size).unwrap(), + entry: attrs.entry, + fin: attrs.fin, + })? }) } - pub fn create_thread( + pub fn register_thread( &mut self, - uid: usize, - attrs: &thread::Attributes, + uid: thread::UId, storage: &mut ThreadMap, - ) -> Result { - let stack = self.allocate_stack(attrs)?; - - let stack = unsafe { Stack::new(stack) }?; - let tid = self.allocate_tid(); - let new = sched::thread::Thread::new(tid.get_uid(uid), stack, attrs.attrs); - storage.insert(&tid.get_uid(uid), new)?; - self.threads.push_back(tid.get_uid(uid), storage)?; - - Ok(tid.get_uid(uid)) + ) -> Result<()> { + self.threads.push_back(uid, storage) } #[allow(dead_code)] diff --git a/src/sched/thread.rs b/src/sched/thread.rs index d4e4ccb..c426d51 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -17,7 +17,7 @@ use crate::types::{ use crate::uapi; pub const IDLE_THREAD: UId = UId { - uid: 1, + uid: 0, tid: Id { id: 0, owner: KERNEL_TASK, @@ -64,6 +64,10 @@ pub struct UId { #[allow(dead_code)] impl UId { + pub fn new(uid: usize, tid: Id) -> Self { + Self { uid, tid } + } + pub fn tid(&self) -> Id { self.tid } diff --git a/src/syscalls/sched.rs b/src/syscalls/sched.rs index e207d1c..d65b28b 100644 --- a/src/syscalls/sched.rs +++ b/src/syscalls/sched.rs @@ -58,7 +58,7 @@ fn spawn_thread(func_ptr: usize, attrs: *const RtAttrs) -> c_int { #[syscall_handler(num = 4)] fn exit(_code: usize) -> c_int { sched::with(|sched| { - if sched.kill_thread(None).is_err() { + if sched.kill_by_thread(None).is_err() { bug!("failed to terminate thread."); } }); @@ -67,5 +67,6 @@ fn exit(_code: usize) -> c_int { #[syscall_handler(num = 5)] fn kick_thread(_uid: usize) -> c_int { + // TODO: Implement a way to retrieve the thread UID from the usize uid. 0 } diff --git a/src/types.rs b/src/types.rs index 8b1befe..677f279 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,5 @@ pub mod array; +pub mod bitset; pub mod boxed; pub mod heap; pub mod list; diff --git a/src/types/array.rs b/src/types/array.rs index daefc06..9a3113a 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -1,12 +1,13 @@ //! This module implements static and dynamic arrays for in-kernel use. -use crate::error::Result; +use crate::{error::Result, types::bitset::BitAlloc}; use super::{ boxed::Box, traits::{Get, GetMut, ToIndex}, }; +use core::mem::ManuallyDrop; use core::ops::{Index, IndexMut}; use core::{borrow::Borrow, mem::MaybeUninit}; @@ -46,22 +47,6 @@ impl IndexMap { } } - /// Insert a value at the next available index. - /// - /// `value` - The value to insert. - /// - /// Returns `Ok(index)` if the value was inserted, otherwise `Err(KernelError::OutOfMemory)`. - pub fn insert_next(&mut self, value: V) -> Result { - for (i, slot) in self.data.iter_mut().enumerate() { - if slot.is_none() { - *slot = Some(value); - return Ok(i); - } - } - - Err(kerr!(OutOfMemory)) - } - /// Remove the value at the given index. /// /// `index` - The index to remove the value from. @@ -73,11 +58,17 @@ impl IndexMap { if idx < N { self.data[idx].take() } else { None } } - /// Get an iterator over the elements in the map. - /// - /// Returns an iterator over the elements in the map. - pub fn iter(&self) -> impl Iterator> { - self.data.iter() + pub fn raw_insert(&mut self, idx: usize, value: V) -> Result<()> { + if idx < N { + self.data[idx] = Some(value); + Ok(()) + } else { + Err(kerr!(OutOfMemory)) + } + } + + pub fn raw_remove(&mut self, idx: usize) -> Option { + if idx < N { self.data[idx].take() } else { None } } pub fn raw_at(&self, idx: usize) -> Option<&V> { @@ -95,16 +86,6 @@ impl IndexMap { None } } - - pub fn find_empty(&self) -> Option { - for (i, slot) in self.data.iter().enumerate() { - if slot.is_none() { - return Some(i); - } - } - - None - } } impl Index for IndexMap { @@ -220,6 +201,31 @@ impl Vec { } } + pub const fn from_array(arr: [T; N]) -> Self { + let arr = ManuallyDrop::new(arr); + let mut data = [const { MaybeUninit::uninit() }; N]; + + let src: *const [T; N] = &arr as *const ManuallyDrop<[T; N]> as *const [T; N]; + let dst: *mut [MaybeUninit; N] = &mut data; + + let mut i = 0; + while i < N { + unsafe { + let value = core::ptr::read((src as *const T).add(i)); + (dst as *mut MaybeUninit) + .add(i) + .write(MaybeUninit::new(value)); + } + i += 1; + } + + Self { + len: N, + data, + extra: Box::new_slice_empty(), + } + } + /// Reserve additional space in the Vec. /// /// `additional` - The additional space to reserve. @@ -442,7 +448,7 @@ impl Vec { /// Returns `Some(&mut T)` if the index is in-bounds, otherwise `None`. pub fn at_mut(&mut self, index: usize) -> Option<&mut T> { // Check if the index is in-bounds. - if index > self.len - 1 { + if index >= self.len { return None; } @@ -578,6 +584,33 @@ impl Drop for Vec { } } +impl Clone for Vec +where + T: Clone + Copy, +{ + fn clone(&self) -> Self { + let mut new_vec = Self::new(); + let min = core::cmp::min(self.len, N); + + // Clone the elements in the inline storage. + for i in 0..min { + // Safety: the elements until self.len are initialized. + let value = unsafe { self.data[i].assume_init_ref() }; + new_vec.data[i].write(*value); + } + + // Clone the elements in the extra storage. + for i in 0..self.len - min { + // Safety: the elements until self.len - N are initialized. + let value = unsafe { self.extra[i].assume_init_ref() }; + new_vec.extra[i].write(*value); + } + + new_vec.len = self.len; + new_vec + } +} + impl Index for Vec { type Output = T; @@ -627,6 +660,92 @@ impl GetMut for Vec { } } +/// This is an IndexMap that additionally tracks which indices are occupied through a bitset. +pub struct BitReclaimMap { + map: IndexMap, + free: BitAlloc, +} + +impl BitReclaimMap { + pub const fn new() -> Self { + Self { + map: IndexMap::new(), + free: BitAlloc::from_array([!0usize; N]), + } + } + + #[allow(dead_code)] + pub fn insert(&mut self, value: V) -> Result { + let idx = self.free.alloc(1).ok_or(kerr!(OutOfMemory))?; + self.map.raw_insert(idx, value)?; + Ok(idx) + } + + pub fn remove(&mut self, idx: &K) -> Option { + self.map.remove(idx).inspect(|_| { + self.free.free(K::to_index(Some(idx)), 1); + }) + } +} + +impl BitReclaimMap { + pub fn insert_with(&mut self, f: impl FnOnce(usize) -> Result<(K, V)>) -> Result { + let idx = self.free.alloc(1).ok_or(kerr!(OutOfMemory))?; + let (key, value) = f(idx)?; + self.map.raw_insert(idx, value)?; + Ok(key) + } +} + +impl Index for BitReclaimMap { + type Output = V; + + fn index(&self, index: K) -> &Self::Output { + self.get::(index).unwrap() + } +} + +impl IndexMut for BitReclaimMap { + fn index_mut(&mut self, index: K) -> &mut Self::Output { + self.get_mut::(index).unwrap() + } +} + +impl Get for BitReclaimMap { + type Output = V; + + fn get>(&self, index: Q) -> Option<&Self::Output> { + self.map.get(index) + } +} + +impl GetMut for BitReclaimMap { + fn get_mut>(&mut self, index: Q) -> Option<&mut Self::Output> { + self.map.get_mut(index) + } + + fn get2_mut>( + &mut self, + index1: Q, + index2: Q, + ) -> (Option<&mut Self::Output>, Option<&mut Self::Output>) { + self.map.get2_mut(index1, index2) + } + + fn get3_mut>( + &mut self, + index1: Q, + index2: Q, + index3: Q, + ) -> ( + Option<&mut Self::Output>, + Option<&mut Self::Output>, + Option<&mut Self::Output>, + ) { + self.map.get3_mut(index1, index2, index3) + } +} + #[cfg(test)] mod tests { use super::Vec; diff --git a/src/types/bitset.rs b/src/types/bitset.rs new file mode 100644 index 0000000..f2380d5 --- /dev/null +++ b/src/types/bitset.rs @@ -0,0 +1,228 @@ +//! A simple bitset allocator that can be used to allocate contiguous runs of bits. + +use crate::types::array::Vec; + +pub struct BitAlloc { + l1: Vec, +} + +impl BitAlloc { + pub const BITS_PER_WORD: usize = usize::BITS as usize; + + pub fn new(free_count: usize) -> Option { + let mut l1 = Vec::new(); + let words = free_count.div_ceil(Self::BITS_PER_WORD); + + for i in 0..words { + let rem = free_count.saturating_sub(i * Self::BITS_PER_WORD); + if rem >= Self::BITS_PER_WORD { + l1.push(!0usize).ok()?; + } else { + l1.push((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32)) + .ok()?; + } + } + + Some(Self { l1 }) + } + + pub const fn from_array(arr: [usize; N]) -> Self { + Self { + l1: Vec::from_array(arr), + } + } + + pub fn alloc(&mut self, bit_count: usize) -> Option { + // If a bit is 1 the bit is free. If a bit is 0 the bit is allocated. + let mut start = 0; + let mut len = 0usize; + + let rem = bit_count.saturating_sub(Self::BITS_PER_WORD); + let mask = (!0usize).unbounded_shl((Self::BITS_PER_WORD.saturating_sub(bit_count)) as u32); + + for idx in 0..N { + if self.l1[idx] == 0 { + len = 0; + continue; + } + + let mut byte = self.l1[idx]; + + let mut shift = if len > 0 { + 0usize + } else { + byte.leading_zeros() as usize + }; + + byte <<= shift; + + while shift < Self::BITS_PER_WORD { + // Make the mask smaller if we already have some contiguous bits. + let mask = if rem.saturating_sub(len) == 0 { + mask << (len - rem) + } else { + mask + }; + + // We shifted byte to MSB, mask is already aligned to the left. + // We compare them via and and shift to the right to shift out extra bits from the mask that would overflow into the next word. + let mut found = (byte & mask) >> shift; + + // We also need to shift the mask to the right so that we can compare mask and found. + if found == (mask >> shift) { + if len == 0 { + start = idx * Self::BITS_PER_WORD + shift; + } + + // Shift completely to the right. + found >>= found.trailing_zeros(); + + // As all found bits are now on the right we can just count them to get the amount we found. + len += found.trailing_ones() as usize; + // Continue to the next word if we haven't found enough bits yet. + break; + } else { + len = 0; + } + + shift += 1; + byte <<= 1; + } + + if len >= bit_count { + // Mark the allocated pages as used. + let mut idx = start / Self::BITS_PER_WORD; + + // Mark all bits in the first word as used. + { + let skip = start % Self::BITS_PER_WORD; + let rem = (Self::BITS_PER_WORD - skip).min(len); + + self.l1[idx] &= + !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); + + if len <= rem { + return Some(start); + } + + len -= rem; + idx += 1; + } + + // Mark all bits in the middle words as used. + { + let mid_cnt = len / Self::BITS_PER_WORD; + + for i in 0..mid_cnt { + self.l1[idx + i] = 0; + } + + idx += mid_cnt; + } + + // Mark the remaining bits in the last word as used. + self.l1[idx] &= !((!0usize) + .unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); + return Some(start); + } + } + + None + } + + pub fn free(&mut self, bit: usize, bit_count: usize) { + let mut idx = bit / Self::BITS_PER_WORD; + let mut bit_idx = bit % Self::BITS_PER_WORD; + + // TODO: slow + for _ in 0..bit_count { + self.l1[idx] |= 1 << (Self::BITS_PER_WORD - 1 - bit_idx); + + bit_idx += 1; + + if bit_idx == Self::BITS_PER_WORD { + bit_idx = 0; + idx += 1; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lsb_no_underflow_works() { + let mut alloc = BitAlloc::<1>::new(1).unwrap(); + // Only the LSB in word 0 is free + alloc.l1[0] = 1; + let result = alloc.alloc(1); + + assert!(result.is_some()); + } + + #[test] + fn msb_no_underflow_works() { + let mut alloc = BitAlloc::<1>::new(1).unwrap(); + // Only the MSB in word 0 is free + alloc.l1[0] = 1 << (BitAlloc::<1>::BITS_PER_WORD - 1); + let result = alloc.alloc(1); + + assert!(result.is_some()); + } + + #[test] + fn test_random_pattern() { + const ITARATIONS: usize = 10000; + + for _ in 0..ITARATIONS { + const N: usize = 1024; + const BITS: usize = BitAlloc::::BITS_PER_WORD; + + let alloc_size = rand::random::() % (N / 2) + 1; + + let mut alloc = BitAlloc::::new(N).unwrap(); + + // Generate a random bit pattern. + for i in 0..N { + let is_zero = rand::random::(); + + if is_zero { + alloc.l1[i / BITS] &= !(1 << ((BITS - 1) - (i % BITS))); + } + } + + // Place a run of alloc_size contiguous bits set to 1 at a random position. + let start = rand::random::() % (N - alloc_size); + for i in start..(start + alloc_size) { + alloc.l1[i / BITS] |= 1 << ((BITS - 1) - (i % BITS)); + } + + let pre = alloc.l1.clone(); + let idx = alloc.alloc(alloc_size).expect("Failed to allocate bits"); + + // Check that the bits in returned indices is all ones in pre. + for i in 0..alloc_size { + let bit = (pre[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; + assert_eq!(bit, 1, "Bit at index {} is not set", idx + i); + } + + // Check that the bits in returned indices is all zeros in allocator.l1. + for i in 0..alloc_size { + let bit = (alloc.l1[(idx + i) / BITS] >> ((BITS - 1) - ((idx + i) % BITS))) & 1; + assert_eq!(bit, 0, "Bit at index {} is not cleared", idx + i); + } + + // Check that the bits in other indices are unchanged. + for i in 0..N { + if i >= idx && i < idx + alloc_size { + continue; + } + let pre_bit = (pre[i / BITS] >> ((BITS - 1) - (i % BITS))) & 1; + let post_bit = (alloc.l1[i / BITS] >> ((BITS - 1) - (i % BITS))) & 1; + assert_eq!(pre_bit, post_bit, "Bit at index {} was modified", i); + } + } + } +} From 04b7156b1bb600e2ce1bc186e7724989a5fdc9b0 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:48:40 +0000 Subject: [PATCH 27/28] add objcopy back. --- justfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/justfile b/justfile index 9c55d85..094e067 100644 --- a/justfile +++ b/justfile @@ -7,6 +7,10 @@ config *args: example name *args: (build args) cargo build -p {{name}} {{args}} + cargo xtask --release injector Cargo.toml + # TODO: This does override the injector binary + # Post build steps should be target specific, this is just a temporary hack + cargo objcopy -p {{name}} {{args}} -- -O binary {{name}}.bin fmt *args: cargo fmt {{args}} From 28d59f8e6fb2bc6465a20765ad8fd0c0f5372214 Mon Sep 17 00:00:00 2001 From: thomasw04 <35061939+thomasw04@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:29:35 +0000 Subject: [PATCH 28/28] actually fix bugs. --- machine/arm/src/sched.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/machine/arm/src/sched.rs b/machine/arm/src/sched.rs index 5a4f98d..2f840dd 100644 --- a/machine/arm/src/sched.rs +++ b/machine/arm/src/sched.rs @@ -60,9 +60,9 @@ pub struct ArmStack { /// The top of the stack (highest address). /// Safety: NonNull can safely be covariant over u32. top: NonNull, - /// The current offset from the top of the stack (in 4 byte steps). + /// The current offset from the top of the stack sp: StackPtr, - /// The size of the stack (in 4 byte steps). + /// The size of the stack size: NonZero, } @@ -161,15 +161,10 @@ impl ArmStack { Self::push(&mut write_index, EXEC_RETURN_THREAD_PSP); // R12 (dummy), R11 - R10 - for _ in 0..3 { + for _ in 0..4 { Self::push(&mut write_index, 0); } - // R9, fixed - let r9: u32; - core::arch::asm!("mov {}, r9", out(reg) r9); - Self::push(&mut write_index, r9); - // R8 - R4 for _ in 0..5 { Self::push(&mut write_index, 0); @@ -224,7 +219,7 @@ impl hal_api::stack::Stacklike for ArmStack { Err(hal_api::Error::OutOfBoundsPtr( ptr as usize, Range { - start: self.top.as_ptr() as usize - self.size.get() * size_of::(), + start: self.top.as_ptr() as usize - self.size.get(), end: self.top.as_ptr() as usize, }, ))