diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index e0f8175..0000000 --- a/.drone.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -kind: pipeline -type: docker -name: notify-hydra - -steps: - - name: Notify hydra - image: nixery.dev/shell/curl - commands: - - curl --get --data-urlencode "jobsets=darkkirb:rkb1-firmware" https://hydra.chir.rs/api/push -trigger: - event: - - push - branch: - - main diff --git a/Cargo.lock b/Cargo.lock index 550f8f5..3420571 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,10 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinyptr" +version = "0.1.0" + [[package]] name = "unicode-ident" version = "1.0.1" diff --git a/Cargo.nix b/Cargo.nix index 68586ef..404b491 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -4,6 +4,7 @@ args@{ release ? true, rootFeatures ? [ + "tinyptr/default" "rkbfirm/default" ], rustPackages, @@ -41,6 +42,7 @@ in { cargo2nixVersion = "0.11.0"; workspace = { + tinyptr = rustPackages.unknown.tinyptr."0.1.0"; rkbfirm = rustPackages.unknown.rkbfirm."0.1.0"; }; "registry+https://github.com/rust-lang/crates.io-index".aho-corasick."0.7.18" = overridableMkRustCrate (profileName: rec { @@ -801,6 +803,13 @@ in }; }); + "unknown".tinyptr."0.1.0" = overridableMkRustCrate (profileName: rec { + name = "tinyptr"; + version = "0.1.0"; + registry = "unknown"; + src = fetchCrateLocal (workspaceSrc + "/lib/tinyptr"); + }); + "registry+https://github.com/rust-lang/crates.io-index".unicode-ident."1.0.1" = overridableMkRustCrate (profileName: rec { name = "unicode-ident"; version = "1.0.1"; diff --git a/Cargo.toml b/Cargo.toml index f531d72..0031c77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ name = "rkbfirm" version = "0.1.0" edition = "2021" +[workspace] +members = [ + "lib/tinyptr" +] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/flake.nix b/flake.nix index 319be6c..a975896 100644 --- a/flake.nix +++ b/flake.nix @@ -45,6 +45,7 @@ cargo2nix.packages.${system}.cargo2nix elf2uf2-rs cargo-embed + llvmPackages_latest.bintools ]; }; packages = rec { diff --git a/lib/tinyptr/Cargo.toml b/lib/tinyptr/Cargo.toml new file mode 100644 index 0000000..f8a41d1 --- /dev/null +++ b/lib/tinyptr/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tinyptr" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/lib/tinyptr/src/lib.rs b/lib/tinyptr/src/lib.rs new file mode 100644 index 0000000..4c5aca4 --- /dev/null +++ b/lib/tinyptr/src/lib.rs @@ -0,0 +1,123 @@ +//! 16 bit pointer library +//! +//! This library provides small pointers with a range of 64 kiB. This is useful for memory-limited +//! microcontrollers. +//! +//! It uses a const generic parameter to set the base address of the pointer. This allows multiple +//! small memory pools to coexist. +#![feature(coerce_unsized)] +#![feature(mixed_integer_ops)] +#![feature(never_type)] +#![feature(ptr_metadata)] +#![feature(slice_ptr_get)] +#![feature(slice_ptr_len)] +#![feature(strict_provenance)] +#![feature(unsize)] +#![no_std] + +use core::hash::Hash; + +pub mod ptr; + +/// Trait that defines valid destination types for a pointer. +pub trait Pointable { + /// The pointer metadata. + type PointerMeta; + /// The tiny version of the pointer metadata. + type PointerMetaTiny: Copy + Eq + Ord + Hash; + /// Conversion error. + type ConversionError: core::fmt::Display + core::fmt::Debug + Clone; + + /// Try reduce the pointer metadata to a tiny version. + /// + /// # Errors + /// This function returns an error if the pointer metadata does not fit into a tiny version. + fn try_tiny(meta: Self::PointerMeta) -> Result; + /// Reduce the pointer metadata to a tiny version. + /// + /// # Panics + /// This function panics if it cannot convert the pointer metadata to a tiny version. + fn tiny(meta: Self::PointerMeta) -> Self::PointerMetaTiny { + Self::try_tiny(meta).unwrap() + } + /// Reduce the pointer metadata to a tiny version, without checking + /// + /// # Safety + /// This function is unsafe because it does not check if the pointer metadata fits into a tiny version. + unsafe fn tiny_unchecked(meta: Self::PointerMeta) -> Self::PointerMetaTiny { + Self::tiny(meta) + } + /// Convert a tiny version of the pointer metadata to the full version. + fn huge(meta: Self::PointerMetaTiny) -> Self::PointerMeta; + + /// Returns an address and the pointer metadata for a pointer + fn extract_parts(ptr: *const Self) -> (usize, Self::PointerMeta); + + /// Returns a pointer to an address in a specific address space + fn create_ptr(base_ptr: *const (), address: usize, meta: Self::PointerMeta) -> *const Self; + + /// Returns a mutable pointer to an address in a specific address space + fn create_ptr_mut(base_ptr: *mut (), address: usize, meta: Self::PointerMeta) -> *mut Self; +} + +impl Pointable for T { + type PointerMeta = (); + type PointerMetaTiny = (); + type ConversionError = !; + + fn try_tiny(_: ()) -> Result<(), !> { + Ok(()) + } + fn tiny(_: ()) -> () {} + fn huge(_: ()) -> () {} + + fn extract_parts(ptr: *const Self) -> (usize, ()) { + (ptr.addr(), ()) + } + fn create_ptr(base_ptr: *const (), address: usize, _: ()) -> *const Self { + base_ptr.with_addr(address).cast() + } + fn create_ptr_mut(base_ptr: *mut (), address: usize, _: ()) -> *mut Self { + base_ptr.with_addr(address).cast() + } +} + +impl Pointable for [T] { + type PointerMeta = usize; + type PointerMetaTiny = u16; + type ConversionError = >::Error; + + fn try_tiny(meta: usize) -> Result { + meta.try_into() + } + unsafe fn tiny_unchecked(meta: usize) -> u16 { + meta as u16 + } + fn huge(meta: u16) -> usize { + meta.into() + } + fn extract_parts(ptr: *const Self) -> (usize, usize) { + (ptr.as_ptr().addr(), ptr.len()) + } + fn create_ptr(base_ptr: *const (), address: usize, meta: usize) -> *const Self { + core::ptr::from_raw_parts(base_ptr.with_addr(address), meta) + } + fn create_ptr_mut(base_ptr: *mut (), address: usize, meta: usize) -> *mut Self { + core::ptr::from_raw_parts_mut(base_ptr.with_addr(address), meta) + } +} + +pub(crate) fn base_ptr() -> *const () { + core::ptr::from_exposed_addr(BASE) +} +pub(crate) fn base_ptr_mut() -> *mut () { + core::ptr::from_exposed_addr_mut(BASE) +} + +#[derive(Debug, Clone)] +pub enum PointerConversionError { + /// The pointer is not in 16 bit address space + NotInAddressSpace(>::Error), + /// The pointer metadata cannot be reduced in size + CannotReduceMeta(::ConversionError), +} diff --git a/lib/tinyptr/src/ptr/const_ptr.rs b/lib/tinyptr/src/ptr/const_ptr.rs new file mode 100644 index 0000000..75b066d --- /dev/null +++ b/lib/tinyptr/src/ptr/const_ptr.rs @@ -0,0 +1,286 @@ +//! Constant pointer + +use core::{marker::{PhantomData, Unsize}, ops::CoerceUnsized, cmp::Ordering, fmt, hash::{Hash, Hasher}}; + +use crate::{base_ptr, Pointable, PointerConversionError}; + +/// A tiny constant pointer +pub struct ConstPtr { + pub(crate) ptr: u16, + pub(crate) meta: ::PointerMetaTiny, + pub(crate) _marker: PhantomData<*const T>, +} + +impl ConstPtr { + /// Create a new constant pointer from raw parts + pub const fn from_raw_parts(ptr: u16, meta: ::PointerMetaTiny) -> Self { + Self { + ptr, + meta, + _marker: PhantomData, + } + } + /// Creates a tiny pointer unchecked + /// + /// # Safety + /// This is unsafe because the address of the pointer may change. + pub unsafe fn new_unchecked(ptr: *const T) -> Self { + let (addr, meta) = T::extract_parts(ptr); + let addr = if ptr.is_null() { + 0 + } else { + addr.wrapping_sub(BASE) + }; + Self::from_raw_parts(addr as u16, T::tiny_unchecked(meta)) + } + /// Tries to create a tiny pointer from a pointer + /// + /// # Errors + /// Returns an error if the pointer does not fit in the address space + pub fn new(ptr: *const T) -> Result> { + let (addr, meta) = T::extract_parts(ptr); + let addr = if ptr.is_null() { + 0 + } else { + addr.wrapping_sub(BASE) + }; + let addr = addr + .try_into() + .map_err(PointerConversionError::NotInAddressSpace)?; + let meta = T::try_tiny(meta).map_err(PointerConversionError::CannotReduceMeta)?; + Ok(Self::from_raw_parts(addr, meta)) + } + /// Widens the pointer + pub fn wide(self) -> *const T { + let addr = if self.ptr == 0 { + 0 + } else { + usize::from(self.ptr).wrapping_add(BASE) + }; + T::create_ptr(base_ptr::(), addr, T::huge(self.meta)) + } + /// Returns `true` if the pointer is null + pub const fn is_null(self) -> bool { + self.ptr == 0 + } + /// Casts to a pointer of another type + pub const fn cast>(self) -> ConstPtr + where + T: Pointable, + { + ConstPtr::from_raw_parts(self.ptr, self.meta) + } + /// Use the pointer value in a new pointer of another type + pub const fn with_metadata_of( + self, + val: ConstPtr, + ) -> ConstPtr { + ConstPtr::from_raw_parts(self.ptr, val.meta) + } + // TODO: as_mut + /// Gets the address portion of the pointer + pub const fn addr(self) -> u16 + where + T: Sized, + { + self.ptr + } + /// Gets the address portion of the pointer and exposeses the provenenance part + pub const fn expose_addr(self) -> u16 + where + T: Sized, + { + self.ptr + } + /// Creates a new pointer with the given address + pub const fn with_addr(self, addr: u16) -> Self + where + T: Sized, + { + Self::from_raw_parts(addr, self.meta) + } + /// Creates a new pointer by mapping self’s address to a new one + pub fn map_addr(self, f: impl FnOnce(u16) -> u16) -> Self + where + T: Sized, + { + self.with_addr(f(self.addr())) + } + /// Decompose a pointer into its address and metadata + pub fn to_raw_parts(self) -> (ConstPtr<(), BASE>, ::PointerMetaTiny) { + (ConstPtr::from_raw_parts(self.ptr, ()), self.meta) + } + // TODO: as_ref + // TODO: as_ref_unchecked + // TODO: as_uninit_ref + /// Calculates the offset from a pointer + pub const unsafe fn offset(self, count: i16) -> Self + where + T: Sized, + { + self.wrapping_offset(count) + } + /// Calculates the offset from a pointer using wrapping arithmetic + pub const fn wrapping_offset(mut self, count: i16) -> Self + where + T: Sized, + { + self.ptr = self + .ptr + .wrapping_add_signed(count.wrapping_mul(core::mem::size_of::() as i16)); + self + } + /// Calculates the distance between two pointers + pub const unsafe fn offset_from(self, origin: Self) -> i16 + where + T: Sized, + { + self.wrapping_offset_from(origin) + } + /// Calculates the distance between two pointers using wrapping arithmetic + pub const fn wrapping_offset_from(self, origin: Self) -> i16 + where + T: Sized, + { + (origin.ptr as i16) + .wrapping_sub(self.ptr as i16) + .wrapping_div(core::mem::size_of::() as i16) + } + /// calculates the distance between two pointers where it is known that self is equal or + /// greater than origin + pub unsafe fn sub_ptr(self, origin: Self) -> u16 + where + T: Sized, + { + u16::try_from(self.wrapping_offset_from(origin)).unwrap_unchecked() + } + /// Calculates the offset from a pointer + pub const unsafe fn add(self, count: u16) -> Self + where + T: Sized, + { + self.offset(count as i16) + } + /// Calculates the offset from a pointer + pub const unsafe fn sub(self, count: u16) -> Self + where + T: Sized, + { + self.offset((count as i16).wrapping_neg()) + } + /// Calculates the offset from a pointer using wrapping arithmetic + pub const fn wrapping_add(self, count: u16) -> Self + where + T: Sized, + { + self.wrapping_offset(count as i16) + } + /// Calculates the offset from a pointer using wrapping arithmetic + pub const fn wrapping_sub(self, count: u16) -> Self + where + T: Sized, + { + self.wrapping_offset((count as i16).wrapping_neg()) + } + /// Reads the value from self without moving it. this leaves the memory in self unchanged. + pub unsafe fn read(self) -> T + where + T: Sized, + { + self.wide().read() + } + /// Performs a volatile read of the value from self without moving it. this leaves the memory in self unchanged. + pub unsafe fn read_volatile(self) -> T + where + T: Sized, + { + self.wide().read_volatile() + } + /// Reads the value from self without moving it. this leaves the memory in self unchanged. + pub unsafe fn read_unaligned(self) -> T + where + T: Sized, + { + self.wide().read_unaligned() + } + // TODO: copy_to + // TODO: copy_to_nonoverlapping + pub const fn align_offset(self, align: u16) -> u16 + where + T: Sized, + { + if !align.is_power_of_two() { + panic!("align must be a power of two"); + } + (self.ptr.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)) + .wrapping_sub(self.ptr) + .wrapping_div(core::mem::size_of::() as u16) + } +} + +impl, const BASE: usize> ConstPtr<[T], BASE> { + pub const fn len(self) -> u16 { + self.meta + } + pub const fn as_ptr(self) -> ConstPtr { + ConstPtr::from_raw_parts(self.ptr, ()) + } + // TODO: as_uninit_slice +} + +impl PartialEq for ConstPtr +{ + fn eq(&self, other: &Self) -> bool { + (self.ptr == other.ptr) && (self.meta == other.meta) + } +} + +impl Eq for ConstPtr {} + +impl Ord for ConstPtr +{ + fn cmp(&self, other: &Self) -> Ordering { + self.ptr.cmp(&other.ptr) + } +} + +impl PartialOrd for ConstPtr +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl, U: Pointable, const BASE: usize> CoerceUnsized> for ConstPtr +where ::PointerMetaTiny: CoerceUnsized<::PointerMetaTiny> +{ +} + +impl Clone for ConstPtr { + fn clone(&self) -> Self { + *self + } +} +impl Copy for ConstPtr {} + +impl fmt::Debug for ConstPtr +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(self, f) + } +} + +impl Hash for ConstPtr { + fn hash(&self, state: &mut H) { + state.write_usize(BASE); + state.write_u16(self.ptr); + self.meta.hash(state); + } +} + +impl fmt::Pointer for ConstPtr +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.wide(), f) + } +} diff --git a/lib/tinyptr/src/ptr/mod.rs b/lib/tinyptr/src/ptr/mod.rs new file mode 100644 index 0000000..34c02aa --- /dev/null +++ b/lib/tinyptr/src/ptr/mod.rs @@ -0,0 +1,5 @@ +//! Raw pointers + +mod const_ptr; +#[doc(inline)] +pub use const_ptr::*;