From fa00bea0a025508937c5b271ccb0234cdaac0c45 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 10:30:21 -0600 Subject: [PATCH 01/39] zephyr::kconfig: Document module Move the module wrapper into `lib.rs` so that we can add documentation to the module itself. Signed-off-by: David Brown --- zephyr-build/src/lib.rs | 14 ++++++-------- zephyr/src/lib.rs | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/zephyr-build/src/lib.rs b/zephyr-build/src/lib.rs index 285262a..bec7134 100644 --- a/zephyr-build/src/lib.rs +++ b/zephyr-build/src/lib.rs @@ -54,24 +54,22 @@ pub fn build_kconfig_mod() { let gen_path = Path::new(&outdir).join("kconfig.rs"); let mut f = File::create(&gen_path).unwrap(); - writeln!(&mut f, "pub mod kconfig {{").unwrap(); let file = File::open(&dotconfig).expect("Unable to open dotconfig"); for line in BufReader::new(file).lines() { let line = line.expect("reading line from dotconfig"); if let Some(caps) = config_hex.captures(&line) { - writeln!(&mut f, " #[allow(dead_code)]").unwrap(); - writeln!(&mut f, " pub const {}: usize = {};", + writeln!(&mut f, "#[allow(dead_code)]").unwrap(); + writeln!(&mut f, "pub const {}: usize = {};", &caps[1], &caps[2]).unwrap(); } else if let Some(caps) = config_int.captures(&line) { - writeln!(&mut f, " #[allow(dead_code)]").unwrap(); - writeln!(&mut f, " pub const {}: isize = {};", + writeln!(&mut f, "#[allow(dead_code)]").unwrap(); + writeln!(&mut f, "pub const {}: isize = {};", &caps[1], &caps[2]).unwrap(); } else if let Some(caps) = config_str.captures(&line) { - writeln!(&mut f, " #[allow(dead_code)]").unwrap(); - writeln!(&mut f, " pub const {}: &'static str = {};", + writeln!(&mut f, "#[allow(dead_code)]").unwrap(); + writeln!(&mut f, "pub const {}: &'static str = {};", &caps[1], &caps[2]).unwrap(); } } - writeln!(&mut f, "}}").unwrap(); } diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index ce71e46..da2fd37 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -13,7 +13,20 @@ pub mod sys; pub mod time; // Bring in the generated kconfig module -include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); +pub mod kconfig { + //! Zephyr Kconfig values. + //! + //! This module contains an auto-generated set of constants corresponding to the values of + //! various Kconfig values during the build. + //! + //! **Note**: Unless you are viewing docs generated for a specific build, the values below are + //! unlikely to directly correspond to those in a given build. + + // Don't enforce doc comments on the bindgen, as it isn't enforced within Zephyr. + #![allow(missing_docs)] + + include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); +} // Ensure that Rust is enabled. #[cfg(not(CONFIG_RUST))] From 823b48a2d4c5a8b0d6de0502704f133163b34574 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 10:46:27 -0600 Subject: [PATCH 02/39] zephyr: Add some missing doc comments Document a handful of functions so that we can enforce documentation. Signed-off-by: David Brown --- zephyr/src/printk.rs | 22 ++++++++++++++++++++++ zephyr/src/sys.rs | 10 ++++++++++ zephyr/src/time.rs | 6 +++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/zephyr/src/printk.rs b/zephyr/src/printk.rs index a8183ae..f68fc5b 100644 --- a/zephyr/src/printk.rs +++ b/zephyr/src/printk.rs @@ -12,6 +12,15 @@ use core::fmt::{ write, }; +/// Print to Zephyr's console, without a newline. +/// +/// This macro uses the same syntax as std's [`format!`], but writes to the Zephyr console instead. +/// +/// if `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation, +/// and due to private accessors in the Zephyr printk implementation, the lock is only over groups +/// of a small buffer size. This buffer must be kept fairly small, as it resides on the stack. +/// +/// [`format!`]: alloc::format #[macro_export] macro_rules! printk { ($($arg:tt)*) => {{ @@ -19,6 +28,17 @@ macro_rules! printk { }}; } +/// Print to Zephyr's console, with a newline. +/// +/// This macro uses the same syntax as std's +/// [`format!`], but writes to the Zephyr console +/// instead. See `std::fmt` for more information. +/// +/// If `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation, +/// and due to private accessors in the Zephyr printk implementation, the lock is only over groups +/// of a small buffer size. This buffer must be kept fairly small, as it resides on the stack. +/// +/// [`format!`]: alloc::format #[macro_export] macro_rules! printkln { ($($arg:tt)*) => {{ @@ -93,6 +113,7 @@ impl Write for Context { } } +#[doc(hidden)] pub fn printk(args: Arguments<'_>) { let mut context = Context { count: 0, @@ -102,6 +123,7 @@ pub fn printk(args: Arguments<'_>) { context.flush(); } +#[doc(hidden)] pub fn printkln(args: Arguments<'_>) { let mut context = Context { count: 0, diff --git a/zephyr/src/sys.rs b/zephyr/src/sys.rs index d7342a6..0648e30 100644 --- a/zephyr/src/sys.rs +++ b/zephyr/src/sys.rs @@ -14,5 +14,15 @@ use zephyr_sys::k_timeout_t; // These two constants are not able to be captured by bindgen. It is unlikely that these values // would change in the Zephyr headers, but there will be an explicit test to make sure they are // correct. + +/// Represents a timeout with an infinite delay. +/// +/// Low-level Zephyr constant. Calls using this value will wait as long as necessary to perform +/// the requested operation. pub const K_FOREVER: k_timeout_t = k_timeout_t { ticks: -1 }; + +/// Represents a null timeout delay. +/// +/// Low-level Zephyr Constant. Calls using this value will not wait if the operation cannot be +/// performed immediately. pub const K_NO_WAIT: k_timeout_t = k_timeout_t { ticks: 0 }; diff --git a/zephyr/src/time.rs b/zephyr/src/time.rs index 7e6228e..e1d6eaf 100644 --- a/zephyr/src/time.rs +++ b/zephyr/src/time.rs @@ -66,7 +66,11 @@ pub type Instant = fugit::Instant; // The absolute time offset is only implemented when time is a 64-bit value. This also means that // "Instant" isn't available when time is defined as a 32-bit value. -// Wrapper around the timeout type, so we can implement From/Info. +/// Wrapper around the timeout type, so we can implement From/Info. +/// +/// This wrapper allows us to implement `From` and `Info` from the Fugit types into the Zephyr +/// types. This allows various methods to accept either value, as well as the `Forever` and +/// `NoWait` values defined here. pub struct Timeout(pub k_timeout_t); // `From` allows methods to take a time of various types and convert it into a Zephyr timeout. From 45192f35cd5ad948f4e16c2891d03c42c2b5498c Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 10:47:07 -0600 Subject: [PATCH 03/39] zephyr: Enforce documentation Add a directive to consider missing documentation on public symbols to be a compliation error. Signed-off-by: David Brown --- zephyr/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index da2fd37..2ca1588 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -8,6 +8,7 @@ #![no_std] #![allow(unexpected_cfgs)] +#![deny(missing_docs)] pub mod sys; pub mod time; From ed875c2b0ee447daa932dc3ac095817aef8a0485 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 23 Sep 2024 11:24:26 -0600 Subject: [PATCH 04/39] zephyr: Add Error and Result types Create a simple error type to handle Zephyr errors. This can be expanded in the future to better be able to distinguish the errors, but this allows us to properly return values from wrapped functions. Signed-off-by: David Brown --- zephyr-sys/build.rs | 1 + zephyr/src/error.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++ zephyr/src/lib.rs | 3 +++ 3 files changed, 60 insertions(+) create mode 100644 zephyr/src/error.rs diff --git a/zephyr-sys/build.rs b/zephyr-sys/build.rs index dffac13..1bcc327 100644 --- a/zephyr-sys/build.rs +++ b/zephyr-sys/build.rs @@ -71,6 +71,7 @@ fn main() -> Result<()> { .allowlist_function("k_.*") .allowlist_function("gpio_.*") .allowlist_function("sys_.*") + .allowlist_item("E.*") // Deprecated .blocklist_function("sys_clock_timeout_end_calc") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) diff --git a/zephyr/src/error.rs b/zephyr/src/error.rs new file mode 100644 index 0000000..1cd2b82 --- /dev/null +++ b/zephyr/src/error.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +//! # Zephyr errors +//! +//! This module contains an `Error` and `Result` type for use in wrapped Zephyr calls. Many +//! operations in Zephyr return an int result where negative values correspond with errnos. +//! Convert those to a `Result` type where the `Error` condition maps to errnos. +//! +//! Initially, this will just simply wrap the numeric error code, but it might make sense to make +//! this an enum itself, however, it would probably be better to auto-generate this enum instead of +//! trying to maintain the list manually. + +use core::ffi::c_int; +use core::fmt; + +// This is a little messy because the constants end up as u32 from bindgen, although the values are +// negative. + +/// A Zephyr error. +/// +/// Represents an error result returned within Zephyr. +pub struct Error(pub u32); + +impl core::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "zephyr error errno:{}", self.0) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "zephyr error errno:{}", self.0) + } +} + +/// Wraps a value with a possible Zephyr error. +pub type Result = core::result::Result; + +/// Map a return result from Zephyr into an Result. +/// +/// Negative return results being considered errors. +pub fn to_result(code: c_int) -> Result { + if code < 0 { + Err(Error(-code as u32)) + } else { + Ok(code) + } +} + +/// Map a return result, with a void result. +pub fn to_result_void(code: c_int) -> Result<()> { + to_result(code).map(|_| ()) +} diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 2ca1588..04c2d7a 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -10,9 +10,12 @@ #![allow(unexpected_cfgs)] #![deny(missing_docs)] +pub mod error; pub mod sys; pub mod time; +pub use error::{Error, Result}; + // Bring in the generated kconfig module pub mod kconfig { //! Zephyr Kconfig values. From e1196a043eebdff762acc08d9aef98e821e6a05e Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 17 Sep 2024 17:03:51 -0600 Subject: [PATCH 05/39] zephyr: Make symbols from portable-atomic available Provide the atomic types from portable-atomic in `zephyr::sync::atomic`, and, if allocation is enabled, provide `zephyr::sync::Arc`. The `alloc::arc` will only be available on Zephyr targets that have atomic intrinsics, and the portable one can be supported on any Zephyr target. There are some limitations described in the documentation. Signed-off-by: David Brown --- zephyr/src/lib.rs | 1 + zephyr/src/sync.rs | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 zephyr/src/sync.rs diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 04c2d7a..44c3828 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -11,6 +11,7 @@ #![deny(missing_docs)] pub mod error; +pub mod sync; pub mod sys; pub mod time; diff --git a/zephyr/src/sync.rs b/zephyr/src/sync.rs new file mode 100644 index 0000000..bdce4e2 --- /dev/null +++ b/zephyr/src/sync.rs @@ -0,0 +1,24 @@ +//! Higher level synchronization primitives. +//! +//! These are modeled after the synchronization primitives in +//! [`std::sync`](https://doc.rust-lang.org/stable/std/sync/index.html) and those from +//! [`crossbeam-channel`](https://docs.rs/crossbeam-channel/latest/crossbeam_channel/), in as much +//! as it makes sense. + +pub mod atomic { + //! Re-export portable atomic. + //! + //! Although `core` contains a + //! [`sync::atomic`](https://doc.rust-lang.org/stable/core/sync/atomic/index.html) module, + //! these are dependent on the target having atomic instructions, and the types are missing + //! when the platform cannot support them. Zephyr, however, does provide atomics on platforms + //! that don't support atomic instructions, using spinlocks. In the Rust-embedded world, this + //! is done through the [`portable-atomic`](https://crates.io/crates/portable-atomic) crate, + //! which will either just re-export the types from core, or provide an implementation using + //! spinlocks when those aren't available. + + pub use portable_atomic::*; +} + +#[cfg(CONFIG_RUST_ALLOC)] +pub use portable_atomic_util::Arc; From 594bbc4223d33b2359619a2b19bb925cb25717e4 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 18 Sep 2024 13:36:55 -0600 Subject: [PATCH 06/39] zephyr: Introduce static kobject support Create wrappers for static kernel objects. This is done through a few wrapper types. Generally, a StaticKernelObject which contains a 'value' which is an unsafe cell containing the actual zephyr-typed kernel object, and then some traits to use these, specifically `KobjGet` to fetch the underlying pointer, and `KobjInit` which supports initialization and getting a higher-level wrapper around the underlying object pointer. Signed-off-by: David Brown --- zephyr/src/lib.rs | 1 + zephyr/src/object.rs | 148 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 zephyr/src/object.rs diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 44c3828..30968da 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -11,6 +11,7 @@ #![deny(missing_docs)] pub mod error; +pub mod object; pub mod sync; pub mod sys; pub mod time; diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs new file mode 100644 index 0000000..8b1cc5e --- /dev/null +++ b/zephyr/src/object.rs @@ -0,0 +1,148 @@ +//! # Zephyr Kernel Objects +//! +//! Zephyr has a concept of a 'kernel object' that is handled a bit magically. In kernel mode +//! threads, these are just pointers to the data structures that Zephyr uses to manage that item. +//! In userspace, they are still pointers, but those data structures aren't accessible to the +//! thread. When making syscalls, the kernel validates that the objects are both valid kernel +//! objects and that the are supposed to be accessible to this thread. +//! +//! In many Zephyr apps, the kernel objects in the app are defined as static, using special macros. +//! These macros make sure that the objects get registered so that they are accessible to userspace +//! (at least after that access is granted). +//! +//! There are also kernel objects that are synthesized as part of the build. Most notably, there +//! are ones generated by the device tree. +//! +//! There are some funny rules about references and mutable references to memory that is +//! inaccessible. Basically, it is never valid to have a reference to something that isn't +//! accessible. However, we can have `*mut ktype` or `*const ktype`. In Rust, having multiple +//! `*mut` pointers live is undefined behavior. Zephyr makes extensive use of shared mutable +//! pointers (or sometimes immutable). We will not dereference these in Rust code, but merely pass +//! them to APIs in Zephyr that require them. +//! +//! Most kernel objects use mutable pointers, and it makes sense to require the wrapper structure +//! to be mutable to these accesses. There a few cases, mostly generated ones that live in +//! read-only memory, notably device instances, that need const pointers. These will be +//! represented by a separate wrapper. +//! +//! # Initialization tracking +//! +//! The Kconfig `CONFIG_RUST_CHECK_KOBJ_INIT` enabled extra checking in Rust-based kernel objects. +//! This will result in a panic if the objects are used before the underlying object has been +//! initialized. The initialization must happen through the `StaticKernelObject::init_help` +//! method. +//! +//! TODO: Document how the wrappers work once we figure out how to implement them. + +use core::{cell::UnsafeCell, mem}; + +use crate::sync::atomic::{AtomicUsize, Ordering}; + +/// A kernel object represented statically in Rust code. +/// +/// These should not be declared directly by the user, as they generally need linker decorations to +/// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type +/// T, and the wrapper type W. +/// +/// TODO: Handling const-defined alignment for these. +pub struct StaticKernelObject { + #[allow(dead_code)] + /// The underlying zephyr kernel object. + value: UnsafeCell, + /// Initialization status of this object. Most objects will start uninitialized and be + /// initialized manually. + init: AtomicUsize, +} + +/// Each can be wrapped appropriately. The wrapped type is the instance that holds the raw pointer. +pub trait Wrapped { + /// The wrapped type. This is what `take()` on the StaticKernelObject will return after + /// initialization. + type T; + + /// Initialize this kernel object, and return the wrapped pointer. + fn get_wrapped(&self) -> Self::T; +} + +/// A state indicating an uninitialized kernel object. +/// +/// This must be zero, as kernel objects will +/// be represetned as zero-initialized memory. +pub const KOBJ_UNINITIALIZED: usize = 0; + +/// A state indicating a kernel object that is being initialized. +pub const KOBJ_INITING: usize = 1; + +/// A state indicating a kernel object that has completed initialization. This also means that the +/// take has been called. And shouldn't be allowed additional times. +pub const KOBJ_INITIALIZED: usize = 2; + +impl StaticKernelObject +where + StaticKernelObject: Wrapped, +{ + /// Construct an empty of these objects, with the zephyr data zero-filled. This is safe in the + /// sense that Zephyr we track the initialization, and they start in the uninitialized state. + pub const fn new() -> StaticKernelObject { + StaticKernelObject { + value: unsafe { mem::zeroed() }, + init: AtomicUsize::new(KOBJ_UNINITIALIZED), + } + } + + /// Get the instance of the kernel object. + /// + /// Will return a single wrapped instance of this object. This will invoke the initialization, + /// and return `Some` for the wrapped containment type. + pub fn take(&self) -> Option<::T> { + if let Err(_) = self.init.compare_exchange( + KOBJ_UNINITIALIZED, + KOBJ_INITING, + Ordering::AcqRel, + Ordering::Acquire) + { + return None; + } + let result = self.get_wrapped(); + self.init.store(KOBJ_INITIALIZED, Ordering::Release); + Some(result) + } +} + +/// Declare a static kernel object. This helps declaring static values of Zephyr objects. +/// +/// This can typically be used as: +/// ``` +/// kobj_define! { +/// static A_MUTEX: StaticMutex; +/// static MUTEX_ARRAY: [StaticMutex; 4]; +/// } +/// ``` +#[macro_export] +macro_rules! kobj_define { + ($v:vis static $name:ident: $type:tt; $($rest:tt)*) => { + $crate::_kobj_rule!($v, $name, $type); + $crate::kobj_define!($($rest)*); + }; + ($v:vis static $name:ident: $type:tt<$size:ident>; $($rest:tt)*) => { + $crate::_kobj_rule!($v, $name, $type<$size>); + $crate::kobj_define!($($rest)*); + }; + ($v:vis static $name:ident: $type:tt<$size:literal>; $($rest:tt)*) => { + $crate::_kobj_rule!($v, $name, $type<$size>); + $crate::kobj_define!($($rest)*); + }; + ($v:vis static $name:ident: $type:tt<{$size:expr}>; $($rest:tt)*) => { + $crate::_kobj_rule!($v, $name, $type<{$size}>); + $crate::kobj_define!($($rest)*); + }; + () => {}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _kobj_rule { + () => { + compile_errro!("TODO: Add kobj rules"); + }; +} From 3e362a7e03810d7652d3c4f3322776d3f1c741da Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 11:10:57 -0600 Subject: [PATCH 07/39] zephyr: include portable atomic The atomic operations in Rust's core crate only work on platforms that have atomic intrinsics. Zephyr supports atomics on other platforms as well, generally implementing them with spinlocks. Use the portable-atomics library, and configure it so use the spinlock provided by the critical section crate. --- zephyr/Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/zephyr/Cargo.toml b/zephyr/Cargo.toml index 977aea2..d4dbcd9 100644 --- a/zephyr/Cargo.toml +++ b/zephyr/Cargo.toml @@ -15,6 +15,22 @@ zephyr-sys = { version = "0.1.0", path = "../zephyr-sys" } [dependencies.fugit] version = "0.3.7" +[dependencies.portable-atomic] +version = "1.7.0" +# We assume that the instances where portable atomic must provide its own implementation (target +# does not have atomic instructions), are only single CPU. This is the case currently with Zephyr, +# so this should only match the same implementation. +# features = ["fallback", "unsafe-assume-single-core"] +features = ["fallback", "critical-section"] + +# Provides an implementation of Arc that either directs to 'alloc', or implements using portable +# atomic. +[dependencies.portable-atomic-util] +version = "0.2.2" +# Technically, this should only be used if the Rust feature for allocation is defined. But, it +# should be safe to build the crate even if the Rust code doesn't use it because of configs. +features = ["alloc"] + # These are needed at build time. # Whether these need to be vendored is an open question. They are not # used by the core Zephyr tree, but are needed by zephyr applications. From 67e456bef3bf08d08e4a6debcda36fb788713dd9 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 12:57:30 -0600 Subject: [PATCH 08/39] zephyr: Add sys::Sempahore The `sys::Semaphore` is a wrapper for Zephyr's `k_sem` type. This adds rules to `kobj_define!` to statically declare these, which will have a `.take()` method, giving an instance that can be used to coordinate between threads. Signed-off-by: David Brown --- zephyr-sys/build.rs | 1 + zephyr/src/object.rs | 16 +++++- zephyr/src/sys.rs | 2 + zephyr/src/sys/sync.rs | 124 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 zephyr/src/sys/sync.rs diff --git a/zephyr-sys/build.rs b/zephyr-sys/build.rs index 1bcc327..5f158a2 100644 --- a/zephyr-sys/build.rs +++ b/zephyr-sys/build.rs @@ -72,6 +72,7 @@ fn main() -> Result<()> { .allowlist_function("gpio_.*") .allowlist_function("sys_.*") .allowlist_item("E.*") + .allowlist_item("K_.*") // Deprecated .blocklist_function("sys_clock_timeout_end_calc") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 8b1cc5e..e91e4c6 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -48,7 +48,7 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; pub struct StaticKernelObject { #[allow(dead_code)] /// The underlying zephyr kernel object. - value: UnsafeCell, + pub(crate) value: UnsafeCell, /// Initialization status of this object. Most objects will start uninitialized and be /// initialized manually. init: AtomicUsize, @@ -142,7 +142,17 @@ macro_rules! kobj_define { #[doc(hidden)] #[macro_export] macro_rules! _kobj_rule { - () => { - compile_errro!("TODO: Add kobj rules"); + // static NAME: StaticSemaphore; + ($v:vis, $name:ident, StaticSemaphore) => { + #[link_section = concat!("._k_sem.static.", stringify!($name), ".", file!(), line!())] + $v static $name: $crate::sys::sync::StaticSemaphore = + unsafe { ::core::mem::zeroed() }; + }; + + // static NAMES: [StaticSemaphore; COUNT]; + ($v:vis, $name:ident, [StaticSemaphore; $size:expr]) => { + #[link_section = concat!("._k_sem.static.", stringify!($name), ".", file!(), line!())] + $v static $name: [$crate::sys::sync::StaticSemaphore; $size] = + unsafe { ::core::mem::zeroed() }; }; } diff --git a/zephyr/src/sys.rs b/zephyr/src/sys.rs index 0648e30..56b716b 100644 --- a/zephyr/src/sys.rs +++ b/zephyr/src/sys.rs @@ -11,6 +11,8 @@ use zephyr_sys::k_timeout_t; +pub mod sync; + // These two constants are not able to be captured by bindgen. It is unlikely that these values // would change in the Zephyr headers, but there will be an explicit test to make sure they are // correct. diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs new file mode 100644 index 0000000..399cd42 --- /dev/null +++ b/zephyr/src/sys/sync.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +//! # Zephyr low-level synchronization primities. +//! +//! The `zephyr-sys` crate contains direct calls into the Zephyr C API. This interface, however, +//! cannot be used from safe Rust. This crate attempts to be as direct an interface to some of +//! these synchronization mechanisms, but without the need for unsafe. The other module +//! `crate::sync` provides higher level interfaces that help manage synchronization in coordination +//! with Rust's borrowing and sharing rules, and will generally provide much more usable +//! interfaces. +//! +//! # Kernel objects +//! +//! Zephyr's primitives work with the concept of a kernel object. These are the data structures +//! that are used by the Zephyr kernel to coordinate the operation of the primitives. In addition, +//! they are where the protection barrier provided by `CONFIG_USERSPACE` is implemented. In order +//! to use these primitives from a userspace thread two things must happen: +//! +//! - The kernel objects must be specially declared. All kernel objects in Zephyr will be built, +//! at compile time, into a perfect hash table that is used to validate them. The special +//! declaration will take care of this. +//! - The objects must be granted permission to be used by the userspace thread. This can be +//! managed either by specifically granting permission, or by using inheritance when creating the +//! thread. +//! +//! At this time, only the first mechanism is implemented, and all kernel objects should be +//! declared using the `crate::kobj_define!` macro. These then must be initialized, and then the +//! special method `.get()` called, to retrieve the Rust-style value that is used to manage them. +//! Later, there will be a pool mechanism to allow these kernel objects to be allocated and freed +//! from a pool, although the objects will still be statically allocated. + +use crate::{ + error::{Result, to_result_void}, + object::{StaticKernelObject, Wrapped}, + raw::{ + k_sem, + k_sem_init, + k_sem_take, + k_sem_give, + k_sem_reset, + k_sem_count_get, + K_SEM_MAX_LIMIT, + }, + time::Timeout, +}; + +/// A zephyr `k_sem` usable from safe Rust code. +#[derive(Clone)] +pub struct Semaphore { + /// The raw Zephyr `k_sem`. + item: *mut k_sem, +} + +/// By nature, Semaphores are both Sync and Send. Safety is handled by the underlying Zephyr +/// implementation (which is why Clone is also implemented). +unsafe impl Sync for Semaphore {} +unsafe impl Send for Semaphore {} + +impl Semaphore { + /// Take a semaphore. + /// + /// Can be called from ISR if called with [`NoWait`]. + pub fn take(&mut self, timeout: T) -> Result<()> + where T: Into, + { + let timeout: Timeout = timeout.into(); + let ret = unsafe { + k_sem_take(self.item, timeout.0) + }; + to_result_void(ret) + } + + /// Give a semaphore. + /// + /// This routine gives to the semaphore, unless the semaphore is already at its maximum + /// permitted count. + pub fn give(&mut self) { + unsafe { + k_sem_give(self.item) + } + } + + /// Resets a semaphor's count to zero. + /// + /// This resets the count to zero. Any outstanding [`take`] calls will be aborted with + /// `Error(EAGAIN)`. + pub fn reset(&mut self) { + unsafe { + k_sem_reset(self.item) + } + } + + /// Get a semaphore's count. + /// + /// Returns the current count. + pub fn count_get(&mut self) -> usize { + unsafe { + k_sem_count_get(self.item) as usize + } + } +} + +/// A static Zephyr `k_sem`. +/// +/// This is intended to be used from within the `kobj_define!` macro. It declares a static ksem +/// that will be properly registered with the Zephyr kernel object system. Call [`take`] to get the +/// [`Semaphore`] that is represents. +pub type StaticSemaphore = StaticKernelObject; + +impl Wrapped for StaticKernelObject { + type T = Semaphore; + + // TODO: Thoughts about how to give parameters to the initialzation. + fn get_wrapped(&self) -> Semaphore { + let ptr = self.value.get(); + unsafe { + k_sem_init(ptr, 0, K_SEM_MAX_LIMIT); + } + Semaphore { + item: ptr, + } + } +} From d8fbb57a96fbca6bb55851ce479bc74748b9008f Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 13:08:40 -0600 Subject: [PATCH 09/39] zephyr: `take` in static kernel object takes args Add a dependent type to the Wrapped trait to indicate the arguments that 'take' and hence init needs. These will typically be either `()`, or a tuple with the Zephyr object's parameters. Signed-off-by: David Brown --- zephyr/src/object.rs | 10 +++++++--- zephyr/src/sys/sync.rs | 13 ++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index e91e4c6..d99bf0f 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -60,8 +60,12 @@ pub trait Wrapped { /// initialization. type T; + /// The wrapped type also requires zero or more initializers. Which are represented by this + /// type. + type I; + /// Initialize this kernel object, and return the wrapped pointer. - fn get_wrapped(&self) -> Self::T; + fn get_wrapped(&self, args: Self::I) -> Self::T; } /// A state indicating an uninitialized kernel object. @@ -94,7 +98,7 @@ where /// /// Will return a single wrapped instance of this object. This will invoke the initialization, /// and return `Some` for the wrapped containment type. - pub fn take(&self) -> Option<::T> { + pub fn take(&self, args: ::I) -> Option<::T> { if let Err(_) = self.init.compare_exchange( KOBJ_UNINITIALIZED, KOBJ_INITING, @@ -103,7 +107,7 @@ where { return None; } - let result = self.get_wrapped(); + let result = self.get_wrapped(args); self.init.store(KOBJ_INITIALIZED, Ordering::Release); Some(result) } diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs index 399cd42..c67f878 100644 --- a/zephyr/src/sys/sync.rs +++ b/zephyr/src/sys/sync.rs @@ -30,6 +30,8 @@ //! Later, there will be a pool mechanism to allow these kernel objects to be allocated and freed //! from a pool, although the objects will still be statically allocated. +use core::ffi::c_uint; + use crate::{ error::{Result, to_result_void}, object::{StaticKernelObject, Wrapped}, @@ -40,11 +42,12 @@ use crate::{ k_sem_give, k_sem_reset, k_sem_count_get, - K_SEM_MAX_LIMIT, }, time::Timeout, }; +pub use crate::raw::K_SEM_MAX_LIMIT; + /// A zephyr `k_sem` usable from safe Rust code. #[derive(Clone)] pub struct Semaphore { @@ -111,11 +114,15 @@ pub type StaticSemaphore = StaticKernelObject; impl Wrapped for StaticKernelObject { type T = Semaphore; + /// The initializer for Semaphores is the initial count, and the count limit (which can be + /// K_SEM_MAX_LIMIT, re-exported here. + type I = (c_uint, c_uint); + // TODO: Thoughts about how to give parameters to the initialzation. - fn get_wrapped(&self) -> Semaphore { + fn get_wrapped(&self, arg: Self::I) -> Semaphore { let ptr = self.value.get(); unsafe { - k_sem_init(ptr, 0, K_SEM_MAX_LIMIT); + k_sem_init(ptr, arg.0, arg.1); } Semaphore { item: ptr, From aed43fdb7530d98d3f01343b7f5343f0293a0066 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 17 Sep 2024 17:10:56 -0600 Subject: [PATCH 10/39] zephyr: Provide critical-section for Zephyr Implement a general critical section handler, using spinlocks, for Rust users to be able to use the `critical-section` crate. This crate is used by other crates as well. Signed-off-by: David Brown --- zephyr/Cargo.toml | 4 ++++ zephyr/src/sys.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/zephyr/Cargo.toml b/zephyr/Cargo.toml index d4dbcd9..dc8e0ab 100644 --- a/zephyr/Cargo.toml +++ b/zephyr/Cargo.toml @@ -15,6 +15,10 @@ zephyr-sys = { version = "0.1.0", path = "../zephyr-sys" } [dependencies.fugit] version = "0.3.7" +[dependencies.critical-section] +version = "1.1.2" +features = ["restore-state-u32"] + [dependencies.portable-atomic] version = "1.7.0" # We assume that the instances where portable atomic must provide its own implementation (target diff --git a/zephyr/src/sys.rs b/zephyr/src/sys.rs index 56b716b..d7d84c1 100644 --- a/zephyr/src/sys.rs +++ b/zephyr/src/sys.rs @@ -28,3 +28,35 @@ pub const K_FOREVER: k_timeout_t = k_timeout_t { ticks: -1 }; /// Low-level Zephyr Constant. Calls using this value will not wait if the operation cannot be /// performed immediately. pub const K_NO_WAIT: k_timeout_t = k_timeout_t { ticks: 0 }; + +pub mod critical { + //! Zephyr implementation of critical sections. + //! + //! Critical sections from Rust are handled with a single Zephyr spinlock. This doesn't allow + //! any nesting, but neither does the `critical-section` crate. + + use core::{ffi::c_int, ptr::addr_of_mut}; + + use critical_section::RawRestoreState; + use zephyr_sys::{k_spinlock, k_spin_lock, k_spin_unlock, k_spinlock_key_t}; + + struct ZephyrCriticalSection; + critical_section::set_impl!(ZephyrCriticalSection); + + // The critical section shares a single spinlock. + static mut LOCK: k_spinlock = unsafe { core::mem::zeroed() }; + + unsafe impl critical_section::Impl for ZephyrCriticalSection { + unsafe fn acquire() -> RawRestoreState { + let res = k_spin_lock(addr_of_mut!(LOCK)); + res.key as RawRestoreState + } + + unsafe fn release(token: RawRestoreState) { + k_spin_unlock(addr_of_mut!(LOCK), + k_spinlock_key_t { + key: token as c_int, + }); + } + } +} From 6598cd17b09baac4e3133434819451fc1fea2576 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 23 Sep 2024 10:40:31 -0600 Subject: [PATCH 11/39] zephyr: Add alignment helper The Rust attribute that indicates structure alignment only supports alignment values that are numeric constants. This makes it difficult to set alignment based on a value coming from the build environment, which is commonly done. Implement a bit of a workaround with an `AlignAs` type that is parameterized with the alignment. Parameters can be compile-time constants not just numeric constants. This works by having instances for each of the desired alignments that each have the numeric constant as the alignment. The instances can be expanded if necessary. Signed-off-by: David Brown --- zephyr/src/align.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++ zephyr/src/lib.rs | 1 + 2 files changed, 50 insertions(+) create mode 100644 zephyr/src/align.rs diff --git a/zephyr/src/align.rs b/zephyr/src/align.rs new file mode 100644 index 0000000..8247d7a --- /dev/null +++ b/zephyr/src/align.rs @@ -0,0 +1,49 @@ +//! Alignment +//! +//! Natively, the align attribute in rust does not allow anything other than an integer literal. +//! However, Zephyr will define the external alignment based on numeric constants. This defines a +//! bit of a trick to enforce alignment of structs to values by defined constants. +//! +//! Thanks to Chayim Refael Friedman for help with this. + +#[doc(hidden)] +pub struct AlignAsStruct; + +#[doc(hidden)] +pub trait AlignAsTrait { + type Aligned; +} + +macro_rules! impl_alignas { + ( $($align:literal),* $(,)? ) => { + $( + const _: () = { + #[repr(align($align))] + pub struct Aligned; + impl AlignAsTrait<$align> for AlignAsStruct { + type Aligned = Aligned; + } + }; + )* + }; +} +// This can be expanded as needed. +impl_alignas!(1, 2, 4, 8, 16, 32, 64, 128, 256); + +/// Align a given struct to a given alignment. To use this, just include `AlignAs` as the first +/// member of the struct. +#[repr(transparent)] +pub struct AlignAs([>::Aligned; 0]) + where + AlignAsStruct: AlignAsTrait; + +impl AlignAs + where AlignAsStruct: AlignAsTrait +{ + /// Construct a new AlignAs. + /// + /// It is zero bytes, but needs a constructor as the field is private. + pub const fn new() -> AlignAs { + AlignAs([]) + } +} diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 30968da..bf4759f 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -10,6 +10,7 @@ #![allow(unexpected_cfgs)] #![deny(missing_docs)] +pub mod align; pub mod error; pub mod object; pub mod sync; From a9ea9a1fec1195b0060b60e55691835ef26635e9 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 15:09:12 -0600 Subject: [PATCH 12/39] zephyr: Add allocator support Create a config `CONFIG_RUST_ALLOC` that will hook Rust's allocation system into the `malloc`/`free` allocator provided on Zephyr. This will allow the `alloc` crate in rust to be used. Signed-off-by: David Brown --- Kconfig | 12 ++++++ zephyr/src/alloc_impl.rs | 92 ++++++++++++++++++++++++++++++++++++++++ zephyr/src/lib.rs | 5 +++ 3 files changed, 109 insertions(+) create mode 100644 zephyr/src/alloc_impl.rs diff --git a/Kconfig b/Kconfig index 0d99cb4..9a3ed88 100644 --- a/Kconfig +++ b/Kconfig @@ -19,4 +19,16 @@ config RUST help This option enables the use of applications written in Rust. +if RUST + +config RUST_ALLOC + bool "Support an allocator in Rust code" + help + If enabled, the Rust zephyr support library will include support for + an allocator. This allocator will use the currently configured + Zephyr allocator (malloc/free). This this enabled, Rust + applications can use the `alloc` crate. + +endif # RUST + endmenu diff --git a/zephyr/src/alloc_impl.rs b/zephyr/src/alloc_impl.rs new file mode 100644 index 0000000..359dde6 --- /dev/null +++ b/zephyr/src/alloc_impl.rs @@ -0,0 +1,92 @@ +//! A Rust global allocator that uses the stdlib allocator in Zephyr +//! +//! The zephyr runtime is divided into three crates: +//! - [core](https://doc.rust-lang.org/stable/core/) is the "dependency-free" foundation of the +//! standard library. It is all of the parts of the standard library that have no dependencies +//! outside of the language and the architecture itself. +//! - [alloc](https://doc.rust-lang.org/stable/alloc/) provides the parts of the standard library +//! that depend on memory allocation. This includes various types of smart pointers, atomically +//! referenced counted pointers, and various collections. This depends on the platform providing +//! an allocator. +//! - [std](https://doc.rust-lang.org/stable/std/) is the rest of the standard library. It include +//! both core and alloc, and then everthing else, including filesystem access, networking, etc. It +//! is notable, however, that the Rust standard library is fairly minimal. A lot of functionality +//! that other languages might include will be relegated to other crates, and the ecosystem and +//! tooling around cargo make it as easy to use these as the standard library. +//! +//! For running application code on Zephyr, the core library (mostly) just works (the a caveat of a +//! little work needed to use atomics on platforms Zephyr supports but don't have atomic +//! instructions). The std library is somewhat explicitly _not_ supported. Although the intent is +//! to provide much of the functionality from std, Zephyr is different enough from the conventional +//! operating system std was built around that just porting it doesn't really give practical +//! results. The result is either to complicated to make work, or too different from what is +//! typically done on Zephyr. Supporting std could be a future project. +//! +//! This leaves alloc, which is mostly independent but is required to know about an allocator to +//! use. This module provides an allocator for Rust that uses the underlying memory allocator +//! configured into Zephyr. +//! +//! Because a given embedded application may or may not want memory allocation, this is controlled +//! by the `CONFIG_RUST_ALLOC` Kconfig. When this config is enabled, the alloc crate becomes +//! available to applications. +//! +//! Since alloc is typically used on Rust as a part of the std library, building in a no-std +//! environment requires that it be access explicitly. Generally, alloc must be explicitly added +//! to every module that needs it. +//! +//! ``` +//! extern crate alloc; +//! +//! use alloc::boxed::Box; +//! +//! let item = Box::new(5); +//! printkln!("box value {}", item); +//! ``` +//! +//! The box holding the value 5 will be allocated by `Box::new`, and freed when the `item` goes out +//! of scope. + +// This entire module is only used if CONFIG_RUST_ALLOC is enabled. +extern crate alloc; + +use core::alloc::{GlobalAlloc, Layout}; + +use alloc::alloc::handle_alloc_error; + +/// Define size_t, as it isn't defined within the FFI. +#[allow(non_camel_case_types)] +type c_size_t = usize; + +extern "C" { + fn malloc(size: c_size_t) -> *mut u8; + fn free(ptr: *mut u8); +} + +/// An allocator that uses Zephyr's allocation primitives. +/// +/// This is exported for documentation purposes, this module does contain an instance of the +/// allocator as well. +pub struct ZephyrAllocator; + +unsafe impl GlobalAlloc for ZephyrAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + // The C allocation library assumes an alignment of 8. For now, just panic if this cannot + // be satistifed. + if align > 8 { + handle_alloc_error(layout); + } + + malloc(size) + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + free(ptr) + } +} + +/// The global allocator built around the Zephyr malloc/free. +#[global_allocator] +pub static ZEPHYR_ALLOCATOR: ZephyrAllocator = ZephyrAllocator; diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index bf4759f..822d30e 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -73,3 +73,8 @@ pub mod raw { pub mod _export { pub use core::format_args; } + +// Mark this as `pub` so the docs can be read. +// If allocation has been requested, provide the allocator. +#[cfg(CONFIG_RUST_ALLOC)] +pub mod alloc_impl; From 59907d706363f87db32a9af2bd9bf77595eeb2c3 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 23 Sep 2024 10:42:46 -0600 Subject: [PATCH 13/39] zephyr-sys: Export stack alignment constants Bindgen only exports defined values that are clearly numeric constants, which is thrown off when the defined value has a cast. Work around this by defining wrapper constants that bindgen is able to expose. We use the `ZR_` prefix (Zephyr Rust) to avoid conflicts with any other symbols. Signed-off-by: David Brown --- zephyr-sys/build.rs | 1 + zephyr-sys/wrapper.h | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/zephyr-sys/build.rs b/zephyr-sys/build.rs index 5f158a2..7107369 100644 --- a/zephyr-sys/build.rs +++ b/zephyr-sys/build.rs @@ -73,6 +73,7 @@ fn main() -> Result<()> { .allowlist_function("sys_.*") .allowlist_item("E.*") .allowlist_item("K_.*") + .allowlist_item("ZR_.*") // Deprecated .blocklist_function("sys_clock_timeout_end_calc") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) diff --git a/zephyr-sys/wrapper.h b/zephyr-sys/wrapper.h index f620d4b..b68b7fa 100644 --- a/zephyr-sys/wrapper.h +++ b/zephyr-sys/wrapper.h @@ -31,4 +31,15 @@ extern int errno; #endif #include +#include #include + +/* + * bindgen will output #defined constant that resolve to simple numbers. There are some symbols + * that we want exported that, at least in some situations, are more complex, usually with a type + * case. + * + * We'll use the prefix "ZR_" to avoid conflicts with other symbols. + */ +const uintptr_t ZR_STACK_ALIGN = Z_KERNEL_STACK_OBJ_ALIGN; +const uintptr_t ZR_STACK_RESERVED = K_KERNEL_STACK_RESERVED; From 6b3e8315c159bed06b95fa84db7a061c9d1319c3 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 15:02:58 -0600 Subject: [PATCH 14/39] zephyr: Add sys::thread support These are wrappers that alloc Zephyr threads to be statically declared as well as started, from safe code. Signed-off-by: David Brown --- zephyr/Cargo.toml | 3 + zephyr/src/object.rs | 71 ++++++- zephyr/src/sys.rs | 1 + zephyr/src/sys/thread.rs | 423 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 496 insertions(+), 2 deletions(-) create mode 100644 zephyr/src/sys/thread.rs diff --git a/zephyr/Cargo.toml b/zephyr/Cargo.toml index dc8e0ab..e0da63f 100644 --- a/zephyr/Cargo.toml +++ b/zephyr/Cargo.toml @@ -12,6 +12,9 @@ Functionality for Rust-based applications that run on Zephyr. [dependencies] zephyr-sys = { version = "0.1.0", path = "../zephyr-sys" } +# Although paste is brought in, it is a compile-time macro, and is not linked into the application. +paste = "1.0" + [dependencies.fugit] version = "0.3.7" diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index d99bf0f..77e9f9d 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -51,7 +51,7 @@ pub struct StaticKernelObject { pub(crate) value: UnsafeCell, /// Initialization status of this object. Most objects will start uninitialized and be /// initialized manually. - init: AtomicUsize, + pub(crate) init: AtomicUsize, } /// Each can be wrapped appropriately. The wrapped type is the instance that holds the raw pointer. @@ -98,7 +98,7 @@ where /// /// Will return a single wrapped instance of this object. This will invoke the initialization, /// and return `Some` for the wrapped containment type. - pub fn take(&self, args: ::I) -> Option<::T> { + pub fn init_once(&self, args: ::I) -> Option<::T> { if let Err(_) = self.init.compare_exchange( KOBJ_UNINITIALIZED, KOBJ_INITING, @@ -159,4 +159,71 @@ macro_rules! _kobj_rule { $v static $name: [$crate::sys::sync::StaticSemaphore; $size] = unsafe { ::core::mem::zeroed() }; }; + + // static THREAD: staticThread; + ($v:vis, $name:ident, StaticThread) => { + // Since the static object has an atomic that we assume is initialized, we cannot use the + // default linker section Zephyr uses for Thread, as that is uninitialized. This will put + // it in .bss, where it is zero initialized. + $v static $name: $crate::sys::thread::StaticThread = + unsafe { ::core::mem::zeroed() }; + }; + + // static THREAD: [staticThread; COUNT]; + ($v:vis, $name:ident, [StaticThread; $size:expr]) => { + // Since the static object has an atomic that we assume is initialized, we cannot use the + // default linker section Zephyr uses for Thread, as that is uninitialized. This will put + // it in .bss, where it is zero initialized. + $v static $name: [$crate::sys::thread::StaticThread; $size] = + unsafe { ::core::mem::zeroed() }; + }; + + // Use indirection on stack initializers to handle some different cases in the Rust syntax. + ($v:vis, $name:ident, ThreadStack<$size:literal>) => { + $crate::_kobj_stack!($v, $name, $size); + }; + ($v:vis, $name:ident, ThreadStack<$size:ident>) => { + $crate::_kobj_stack!($v, $name, $size); + }; + ($v:vis, $name:ident, ThreadStack<{$size:expr}>) => { + $crate::_kobj_stack!($v, $name, $size); + }; + + // Array of stack object versions. + ($v:vis, $name:ident, [ThreadStack<$size:literal>; $asize:expr]) => { + $crate::_kobj_stack!($v, $name, $size, $asize); + }; + ($v:vis, $name:ident, [ThreadStack<$size:ident>; $asize:expr]) => { + $crate::_kobj_stack!($v, $name, $size, $asize); + }; + ($v:vis, $name:ident, [ThreadStack<{$size:expr}>; $asize:expr]) => { + $crate::_kobj_stack!($v, $name, $size, $asize); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! _kobj_stack { + ($v:vis, $name: ident, $size:expr) => { + ::paste::paste! { + // The actual stack itself goes into the no-init linker section. We'll use the user_name, + // with _REAL appended, to indicate the real stack. + #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())] + $v static [< $name _REAL >] $crate::sys::thread::RealThreadStack<{$crate::sys::thread::stack_len($size)}> = + unsafe { ::core::mem::zeroed() }; + + // The proxy object used to ensure initialization is placed in initialized memory. + $v static $name: $crate::object::StaticKernelObject<$crate::object::StaticThreadStack> = StaticKernelObject { + value: ::core::cell::UnsafeCell::new($crate::object::StaticThreadStack { + base: [< $name _REAL >].data.get() as *mut $crate::raw::z_thread_stack_element, + size: $size, + }), + init: $crate::atomic::AtomicUsize::new(0), + }; + } + }; + + ($v:vis, $name: ident, $size:expr, $asize:expr) => { + compile_error!("TODO: Stack initializer array"); + } } diff --git a/zephyr/src/sys.rs b/zephyr/src/sys.rs index d7d84c1..d772f5c 100644 --- a/zephyr/src/sys.rs +++ b/zephyr/src/sys.rs @@ -12,6 +12,7 @@ use zephyr_sys::k_timeout_t; pub mod sync; +pub mod thread; // These two constants are not able to be captured by bindgen. It is unlikely that these values // would change in the Zephyr headers, but there will be an explicit test to make sure they are diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs new file mode 100644 index 0000000..3bd074a --- /dev/null +++ b/zephyr/src/sys/thread.rs @@ -0,0 +1,423 @@ +//! Zephyr low level threads +//! +//! This is a fairly low level (but still safe) interface to Zephyr threads. This is intended to +//! work the same way as threads are typically done on Zephyr systems, where the threads and their +//! stacks are statically allocated, a code is called to initialize them. +//! +//! In addition, there are some convenience operations available that require allocation to be +//! available. +//! +//! ## Usage +//! +//! Each thread needs a stack associated with it. The stack and the thread should be defined as +//! follows: +//! ``` +//! kobj_defined! { +//! static MY_THREAD: StaticThread; +//! static MY_THREAD_STACK: StaticThreadStack<2048>; +//! } +//! ``` +//! +//! Each of these has a `.take(...)` method that returns the single usable instance. The +//! StaticThread takes the stack retrieved by take as its argument. This will return a +//! ThreadStarter, where various options can be set on the thread, and then it started with one of +//! `spawn`, or `simple_spawn` (spawn requires `CONFIG_RUST_ALLOC`). +//! +//! Provided that `CONFIG_RUST_ALLOC` has been enabled (recommended): the read can be initialized as +//! follows: +//! ``` +//! let mut thread = MY_THREAD.take(MY_THREAD_STACK.take().unwrap()).unwrap(); +//! thread.set_priority(5); +//! let child = thread.spawn(|| move { +//! // thread code... +//! }); +//! ``` + +extern crate alloc; + +use alloc::boxed::Box; +use core::{cell::UnsafeCell, ffi::{c_int, c_void}}; + +use zephyr_sys::{ + k_thread, + k_thread_entry_t, + k_thread_create, + z_thread_stack_element, + ZR_STACK_ALIGN, + ZR_STACK_RESERVED, +}; +use super::K_NO_WAIT; + +use crate::{align::AlignAs, object::{StaticKernelObject, Wrapped}}; + +/// Adjust the stack size for alignment. Note that, unlike the C code, we don't include the +/// reservation in this, as it has its own fields in the struct. +pub const fn stack_len(size: usize) -> usize { + size.next_multiple_of(ZR_STACK_ALIGN) +} + +/// A Zephyr stack declaration. +/// +/// It isn't meant to be used directly, as it needs additional decoration about linker sections and +/// such. Unlike the C declaration, the reservation is a separate field. As long as the SIZE is +/// properly aligned, this should work without padding between the fields. +pub struct RealStaticThreadStack { + #[allow(dead_code)] + align: AlignAs, + #[allow(dead_code)] + data: UnsafeCell<[z_thread_stack_element; SIZE]>, + #[allow(dead_code)] + extra: [z_thread_stack_element; ZR_STACK_RESERVED], +} + +unsafe impl Sync for RealStaticThreadStack {} + +/// The dynamic stack value, which wraps the underlying stack. +pub struct ThreadStack { + base: *mut z_thread_stack_element, + size: usize, +} + +#[doc(hidden)] +pub struct StaticThreadStack { + base: *mut z_thread_stack_element, + size: usize, +} + +unsafe impl Sync for StaticKernelObject {} + +/* +// Let's make sure I can declare some of this. +pub static TEST_STACK: RealStaticThreadStack<1024> = unsafe { ::core::mem::zeroed() }; +pub static TEST: StaticKernelObject = StaticKernelObject { + value: UnsafeCell::new(StaticThreadStack { + base: TEST_STACK.data.get() as *mut z_thread_stack_element, + size: 1024, + }), + init: AtomicUsize::new(0), +}; + +pub fn doit() { + TEST.init_once(()); +} +*/ + +impl Wrapped for StaticKernelObject { + type T = ThreadStack; + type I = (); + fn get_wrapped(&self, _args: Self::I) -> Self::T { + // This is a bit messy. Whee. + let stack = self.value.get(); + let stack = unsafe { &*stack }; + ThreadStack { + base: stack.base, + size: stack.size, + } + } +} + +/// A single Zephyr thread. +/// +/// This wraps a `k_thread` type within Rust. This value is returned from +/// [`StaticThread::init_once`] and represents an initialized thread that hasn't been started. +pub struct Thread { + raw: *mut k_thread, + stack: ThreadStack, + + /// The initial priority of this thread. + priority: c_int, + /// Options given to thread creation. + options: u32, +} + +impl Wrapped for StaticKernelObject { + type T = Thread; + type I = ThreadStack; + fn get_wrapped(&self, stack: Self::I) -> Self::T { + Thread { + raw: self.value.get(), + stack, + priority: 0, + options: 0, + } + } +} + +impl Thread { + /// Set the priority the thread will be created at. + pub fn set_priority(&mut self, priority: c_int) { + self.priority = priority; + } + + /// Set the value of the options passed to thread creation. + pub fn set_options(&mut self, options: u32) { + self.options = options; + } + + /// Simple thread spawn. This is unsafe because of the raw values being used. This can be + /// useful in systems without an allocator defined. + pub unsafe fn simple_spawn(self, + child: k_thread_entry_t, + p1: *mut c_void, + p2: *mut c_void, + p3: *mut c_void) + { + k_thread_create( + self.raw, + self.stack.base, + self.stack.size, + child, + p1, + p2, + p3, + self.priority, + self.options, + K_NO_WAIT); + } + + #[cfg(CONFIG_RUST_ALLOC)] + /// Spawn a thread, with a closure. + /// + /// This requires allocation to be able to safely pass the closure to the other thread. + pub fn spawn(&self, child: F) { + use core::ptr::null_mut; + + let child: closure::Closure = Box::new(child); + let child = Box::into_raw(Box::new(closure::ThreadData { + closure: child, + })); + unsafe { + k_thread_create( + self.raw, + self.stack.base, + self.stack.size, + Some(closure::child), + child as *mut c_void, + null_mut(), + null_mut(), + self.priority, + self.options, + K_NO_WAIT); + } + } +} + +/* +use zephyr_sys::{ + k_thread, k_thread_create, k_thread_start, z_thread_stack_element, ZR_STACK_ALIGN, ZR_STACK_RESERVED +}; + +use core::{cell::UnsafeCell, ffi::c_void, ptr::null_mut}; + +use crate::{align::AlignAs, object::{KobjInit, StaticKernelObject}}; + +#[cfg(CONFIG_RUST_ALLOC)] +extern crate alloc; +#[cfg(CONFIG_RUST_ALLOC)] +use alloc::boxed::Box; +#[cfg(CONFIG_RUST_ALLOC)] +use core::mem::ManuallyDrop; + +use super::K_FOREVER; + +/// Adjust the stack size for alignment. Note that, unlike the C code, we don't include the +/// reservation in this, as it has its own fields in the struct. +pub const fn stack_len(size: usize) -> usize { + size.next_multiple_of(ZR_STACK_ALIGN) +} + +/// A Zephyr stack declaration. It isn't meant to be used directly, as it needs additional +/// decoration about linker sections and such. Unlike the C declaration, the reservation is a +/// separate field. As long as the SIZE is properly aligned, this should work without padding +/// between the fields. +pub struct ThreadStack { + #[allow(dead_code)] + align: AlignAs, + data: UnsafeCell<[z_thread_stack_element; SIZE]>, + #[allow(dead_code)] + extra: [z_thread_stack_element; ZR_STACK_RESERVED], +} + +unsafe impl Sync for ThreadStack {} + +impl ThreadStack { + /// Get the size of this stack. This is the size, minus any reservation. This is called `size` + /// to avoid any confusion with `len` which might return the actual size of the stack. + pub fn size(&self) -> usize { + SIZE + } + + /// Return the stack base needed as the argument to various zephyr calls. + pub fn base(&self) -> *mut z_thread_stack_element { + self.data.get() as *mut z_thread_stack_element + } + + /// Return the token information for this stack, which is a base and size. + pub fn token(&self) -> StackToken { + StackToken { base: self.base(), size: self.size() } + } +} + +/// Declare a variable, of a given name, representing the stack for a thread. +#[macro_export] +macro_rules! kernel_stack_define { + ($name:ident, $size:expr) => { + #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())] + static $name: $crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}> + = unsafe { ::core::mem::zeroed() }; + }; +} + +/// A single Zephyr thread. +/// +/// This wraps a `k_thread` type within Zephyr. This value is returned +/// from the `StaticThread::spawn` method, to allow control over the start +/// of the thread. The [`start`] method should be used to start the +/// thread. +/// +/// [`start`]: Thread::start +pub struct Thread { + raw: *mut k_thread, +} + +unsafe impl Sync for StaticKernelObject { } + +impl KobjInit for StaticKernelObject { + fn wrap(ptr: *mut k_thread) -> Thread { + Thread { raw: ptr } + } +} + +// Public interface to threads. +impl Thread { + /// Start execution of the given thread. + pub fn start(&self) { + unsafe { k_thread_start(self.raw) } + } +} + +/// Declare a global static representing a thread variable. +#[macro_export] +macro_rules! kernel_thread_define { + ($name:ident) => { + // Since the static object has an atomic that we assume is initialized, let the compiler put + // this in the data section it finds appropriate (probably .bss if it is initialized to zero). + // This only matters when the objects are being checked. + // TODO: This doesn't seem to work with the config. + // #[cfg_attr(not(CONFIG_RUST_CHECK_KOBJ_INIT), + // link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!()))] + static $name: $crate::object::StaticKernelObject<$crate::raw::k_thread> = + $crate::object::StaticKernelObject::new(); + // static $name: $crate::sys::thread::Thread = unsafe { ::core::mem::zeroed() }; + }; +} + +/// For now, this "token" represents the somewhat internal information about thread. +/// What we really want is to make sure that stacks and threads go together. +pub struct StackToken { + base: *mut z_thread_stack_element, + size: usize, +} + +// This isn't really safe at all, as these can be initialized. It is unclear how, if even if it is +// possible to implement safe static threads and other data structures in Zephyr. + +/// A Statically defined Zephyr `k_thread` object to be used from Rust. +/// +/// This should be used in a manner similar to: +/// ``` +/// const MY_STACK_SIZE: usize = 4096; +/// +/// kobj_define! { +/// static MY_THREAD: StaticThread; +/// static MY_STACK: ThreadStack; +/// } +/// +/// let thread = MY_THREAD.spawn(MY_STACK.token(), move || { +/// // Body of thread. +/// }); +/// thread.start(); +/// ``` +pub type StaticThread = StaticKernelObject; + +// The thread itself assumes we've already initialized, so this method is on the wrapper. +impl StaticThread { + /// Spawn this thread to the given external function. This is a simplified version that doesn't + /// take any arguments. The child runs immediately. + pub fn simple_spawn(&self, stack: StackToken, child: fn() -> ()) -> Thread { + self.init_help(|raw| { + unsafe { + k_thread_create( + raw, + stack.base, + stack.size, + Some(simple_child), + child as *mut c_void, + null_mut(), + null_mut(), + 5, + 0, + K_FOREVER, + ); + } + }); + self.get() + } + + #[cfg(CONFIG_RUST_ALLOC)] + /// Spawn a thread, running a closure. The closure will be boxed to give to the new thread. + /// The new thread runs immediately. + pub fn spawn(&self, stack: StackToken, child: F) -> Thread { + let child: closure::Closure = Box::new(child); + let child = Box::into_raw(Box::new(closure::ThreadData { + closure: ManuallyDrop::new(child), + })); + self.init_help(move |raw| { + unsafe { + k_thread_create( + raw, + stack.base, + stack.size, + Some(closure::child), + child as *mut c_void, + null_mut(), + null_mut(), + 5, + 0, + K_FOREVER, + ); + } + }); + self.get() + } +} + +unsafe extern "C" fn simple_child( + arg: *mut c_void, + _p2: *mut c_void, + _p3: *mut c_void, +) { + let child: fn() -> () = core::mem::transmute(arg); + (child)(); +} +*/ + +#[cfg(CONFIG_RUST_ALLOC)] +/// Handle the closure case. This invokes a double box to rid us of the fat pointer. I'm not sure +/// this is actually necessary. +mod closure { + use super::Box; + use core::ffi::c_void; + + pub type Closure = Box; + + pub struct ThreadData { + pub closure: Closure, + } + + pub unsafe extern "C" fn child(child: *mut c_void, _p2: *mut c_void, _p3: *mut c_void) { + let thread_data: Box = unsafe { Box::from_raw(child as *mut ThreadData) }; + let closure = (*thread_data).closure; + closure(); + } +} From e71bd6acc135d1c34b407ad00c91083f38be8b12 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 15:04:15 -0600 Subject: [PATCH 15/39] samples: philosophers: Dining philosophers example The dining philosophers example, in Rust. This is generalized, and provides multiple implementations, exercising various different synchronization primivites in Zephyr Signed-off-by: David Brown --- samples/philosophers/CMakeLists.txt | 8 ++ samples/philosophers/Cargo.toml | 25 ++++ samples/philosophers/Kconfig | 18 +++ samples/philosophers/boards/rpi_pico.conf | 7 + samples/philosophers/build.rs | 9 ++ samples/philosophers/prj.conf | 15 +++ samples/philosophers/sample.yaml | 29 ++++ samples/philosophers/src/lib.rs | 155 ++++++++++++++++++++++ samples/philosophers/src/semsync.rs | 58 ++++++++ 9 files changed, 324 insertions(+) create mode 100644 samples/philosophers/CMakeLists.txt create mode 100644 samples/philosophers/Cargo.toml create mode 100644 samples/philosophers/Kconfig create mode 100644 samples/philosophers/boards/rpi_pico.conf create mode 100644 samples/philosophers/build.rs create mode 100644 samples/philosophers/prj.conf create mode 100644 samples/philosophers/sample.yaml create mode 100644 samples/philosophers/src/lib.rs create mode 100644 samples/philosophers/src/semsync.rs diff --git a/samples/philosophers/CMakeLists.txt b/samples/philosophers/CMakeLists.txt new file mode 100644 index 0000000..8856160 --- /dev/null +++ b/samples/philosophers/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(philosophers) + +rust_cargo_application() diff --git a/samples/philosophers/Cargo.toml b/samples/philosophers/Cargo.toml new file mode 100644 index 0000000..bb0cdf3 --- /dev/null +++ b/samples/philosophers/Cargo.toml @@ -0,0 +1,25 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +[package] +# This must be rustapp for now. +name = "rustapp" +version = "3.7.0" +edition = "2021" +description = "A sample hello world application in Rust" +license = "Apache-2.0 or MIT" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +zephyr = "3.7.0" + +# Dependencies that are used by build.rs. +[build-dependencies] +zephyr-build = "3.7.0" + +[profile.release] +debug-assertions = true +overflow-checks = true +debug = true diff --git a/samples/philosophers/Kconfig b/samples/philosophers/Kconfig new file mode 100644 index 0000000..2e48170 --- /dev/null +++ b/samples/philosophers/Kconfig @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +mainmenu "Rust Dining Philosphers" + +source "Kconfig.zephyr" + +choice + prompt "Select Synchronization implementation" + default SYNC_SYS_SEMAPHORE + + config SYNC_SYS_SEMAPHORE + bool "Use sys::Semaphore to synchronize forks" + help + Use to have the dining philosophers sample use sys::Semaphore, with one per form, + to synchronize. + +endchoice diff --git a/samples/philosophers/boards/rpi_pico.conf b/samples/philosophers/boards/rpi_pico.conf new file mode 100644 index 0000000..94c0843 --- /dev/null +++ b/samples/philosophers/boards/rpi_pico.conf @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +# This board doesn't have a serial console, so use RTT. +CONFIG_UART_CONSOLE=n +CONFIG_RTT_CONSOLE=y +CONFIG_USE_SEGGER_RTT=y diff --git a/samples/philosophers/build.rs b/samples/philosophers/build.rs new file mode 100644 index 0000000..22233f1 --- /dev/null +++ b/samples/philosophers/build.rs @@ -0,0 +1,9 @@ +// Copyright (c) 2023 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +// This crate needs access to kconfig variables. This is an example of how to do that. The +// zephyr-build must be a build dependency. + +fn main() { + zephyr_build::export_bool_kconfig(); +} diff --git a/samples/philosophers/prj.conf b/samples/philosophers/prj.conf new file mode 100644 index 0000000..5dfcb89 --- /dev/null +++ b/samples/philosophers/prj.conf @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RUST=y +CONFIG_RUST_ALLOC=y +CONFIG_MAIN_STACK_SIZE=8192 + +# CONFIG_USERSPACE=y + +# Some debugging +CONFIG_THREAD_MONITOR=y +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_USE_PRINTK=y +CONFIG_THREAD_ANALYZER_AUTO=n +# CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=15 diff --git a/samples/philosophers/sample.yaml b/samples/philosophers/sample.yaml new file mode 100644 index 0000000..b393344 --- /dev/null +++ b/samples/philosophers/sample.yaml @@ -0,0 +1,29 @@ +sample: + description: Philosphers, in Rust + name: philosophers rust +common: + harness: console + harness_config: + type: one_line + regex: + # Match the statistics, and make sure that each philosopher has at least 10 (two digits) + # meals. + - "^\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]" + tags: rust + filter: CONFIG_RUST_SUPPORTED +tests: + sample.rust.philosopher.sysmutex: + tags: introduction + min_ram: 32 + extra_configs: + - CONFIG_SYNC_SYS_MUTEX=y + sample.rust.philosopher.condvar: + tags: introduction + min_ram: 32 + extra_configs: + - CONFIG_SYNC_CONDVAR=y + sample.rust.philosopher.channel: + tags: introduction + min_ram: 32 + extra_configs: + - CONFIG_SYNC_CHANNEL=y diff --git a/samples/philosophers/src/lib.rs b/samples/philosophers/src/lib.rs new file mode 100644 index 0000000..f8e2821 --- /dev/null +++ b/samples/philosophers/src/lib.rs @@ -0,0 +1,155 @@ +// Copyright (c) 2023 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +#![no_std] + +// Cargo tries to detect configs that have typos in them. Unfortunately, the Zephyr Kconfig system +// uses a large number of Kconfigs and there is no easy way to know which ones might conceivably be +// valid. This prevents a warning about each cfg that is used. +#![allow(unexpected_cfgs)] + +extern crate alloc; + +#[allow(unused_imports)] +use alloc::boxed::Box; +use alloc::vec::Vec; +use zephyr::sys::thread::Thread; +use zephyr::time::{Duration, sleep, Tick}; +use zephyr::{ + printkln, + kobj_define, + sys::uptime_get, + sync::Arc, +}; + +// These are optional, based on Kconfig, so allow them to be unused. +#[allow(unused_imports)] +use crate::semsync::semaphore_sync; + +mod semsync; + +/// How many philosophers. There will be the same number of forks. +const NUM_PHIL: usize = 6; + +/// How much stack should each philosopher thread get. Worst case I've seen is riscv64, with 3336 +/// bytes, when printing messages. Make a bit larger to work. +const PHIL_STACK_SIZE: usize = 4096; + +// The dining philosophers problem is a simple example of cooperation between multiple threads. +// This implementation use one of several different underlying mechanism to support this cooperation. + +// This example uses dynamic dispatch to allow multiple implementations. The intent is to be able +// to periodically shut down all of the philosphers and start them up with a differernt sync +// mechanism. This isn't implemented yet. + +/// The philosophers use a fork synchronization mechanism. Essentially, this is 6 locks, and will be +/// implemented in a few different ways to demonstrate/test different mechanmism in Rust. All of +/// them implement The ForkSync trait which provides this mechanism. +trait ForkSync: core::fmt::Debug + Sync + Send { + /// Take the given fork. The are indexed the same as the philosopher index number. This will + /// block until the fork is released. + fn take(&self, index: usize); + + /// Release the given fork. Index is the same as take. + fn release(&self, index: usize); +} + +#[no_mangle] +extern "C" fn rust_main() { + printkln!("Hello world from Rust on {}", + zephyr::kconfig::CONFIG_BOARD); + printkln!("Time tick: {}", zephyr::time::SYS_FREQUENCY); + + let syncers = get_syncer(); + + printkln!("Pre fork"); + // At this time, the arrays of threads are not supported, so manually unroll the loop for now. + // If NUM_PHIL is changed, this loop and the declarations at the end will have to be updated. + let threads: [Thread; NUM_PHIL] = [ + PHIL_THREAD_1.init_once(PHIL_STACK_1.init_once(()).unwrap()).unwrap(), + PHIL_THREAD_2.init_once(PHIL_STACK_2.init_once(()).unwrap()).unwrap(), + PHIL_THREAD_3.init_once(PHIL_STACK_3.init_once(()).unwrap()).unwrap(), + PHIL_THREAD_4.init_once(PHIL_STACK_4.init_once(()).unwrap()).unwrap(), + PHIL_THREAD_5.init_once(PHIL_STACK_5.init_once(()).unwrap()).unwrap(), + PHIL_THREAD_6.init_once(PHIL_STACK_6.init_once(()).unwrap()).unwrap(), + ]; + + for (i, syncer) in (0..NUM_PHIL).zip(syncers.into_iter()) { + threads[i].spawn(move || { + phil_thread(i, syncer); + }); + } + + let delay = Duration::secs_at_least(10); + loop { + // Periodically, printout the stats. + zephyr::time::sleep(delay); + } +} + +#[cfg(CONFIG_SYNC_SYS_SEMAPHORE)] +fn get_syncer() -> Vec> { + semaphore_sync() +} + +fn phil_thread(n: usize, syncer: Arc) { + printkln!("Child {} started: {:?}", n, syncer); + + // Determine our two forks. + let forks = if n == NUM_PHIL - 1 { + // Per Dijkstra, the last phyilosopher needs to reverse forks, or we deadlock. + (0, n) + } else { + (n, n+1) + }; + + loop { + { + printkln!("Child {} hungry", n); + printkln!("Child {} take left fork", n); + syncer.take(forks.0); + printkln!("Child {} take right fork", n); + syncer.take(forks.1); + + let delay = get_random_delay(n, 25); + printkln!("Child {} eating ({} ms)", n, delay); + sleep(delay); + // stats.lock().unwrap().record_eat(n, delay); + + // Release the forks. + printkln!("Child {} giving up forks", n); + syncer.release(forks.1); + syncer.release(forks.0); + + let delay = get_random_delay(n, 25); + printkln!("Child {} thinking ({} ms)", n, delay); + sleep(delay); + // stats.lock().unwrap().record_think(n, delay); + } + } +} + +/// Get a random delay, based on the ID of this user, and the current uptime. +fn get_random_delay(id: usize, period: usize) -> Duration { + let tick = (uptime_get() & (usize::MAX as i64)) as usize; + let delay = (tick / 100 * (id + 1)) & 0x1f; + + // Use one greater to be sure to never get a delay of zero. + Duration::millis_at_least(((delay + 1) * period) as Tick) +} + +kobj_define! { + static PHIL_THREAD_1: StaticThread; + static PHIL_THREAD_2: StaticThread; + static PHIL_THREAD_3: StaticThread; + static PHIL_THREAD_4: StaticThread; + static PHIL_THREAD_5: StaticThread; + static PHIL_THREAD_6: StaticThread; + + static PHIL_STACK_1: ThreadStack; + static PHIL_STACK_2: ThreadStack; + static PHIL_STACK_3: ThreadStack; + static PHIL_STACK_4: ThreadStack; + static PHIL_STACK_5: ThreadStack; + static PHIL_STACK_6: ThreadStack; +} diff --git a/samples/philosophers/src/semsync.rs b/samples/philosophers/src/semsync.rs new file mode 100644 index 0000000..03bf8ce --- /dev/null +++ b/samples/philosophers/src/semsync.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +//! Semaphore based sync. +//! +//! This is the simplest type of sync, which uses a single semaphore per fork. + +extern crate alloc; + +use alloc::vec::Vec; +use alloc::boxed::Box; + +use zephyr::{ + kobj_define, + sync::Arc, + time::Forever, +}; + +use crate::{ForkSync, NUM_PHIL}; + +#[derive(Debug)] +pub struct SemSync { + /// The forks for this philosopher. This is a big excessive, as we really don't need all of + /// them, but the ForSync code uses the index here. + forks: [zephyr::sys::sync::Semaphore; NUM_PHIL], +} + +impl ForkSync for SemSync { + fn take(&self, index: usize) { + self.forks[index].take(Forever).unwrap(); + } + + fn release(&self, index: usize) { + self.forks[index].give(); + } +} + +#[allow(dead_code)] +pub fn semaphore_sync() -> Vec> { + let forks = SEMS.each_ref().map(|m| { + // Each fork starts as taken. + m.init_once((1, 1)).unwrap() + }); + + let syncers = (0..NUM_PHIL).map(|_| { + let syncer = SemSync { + forks: forks.clone(), + }; + let item = Box::new(syncer) as Box; + Arc::from(item) + }).collect(); + + syncers +} + +kobj_define! { + static SEMS: [StaticSemaphore; NUM_PHIL]; +} From 096d34be6a8f95bf8002be2208d99bd6071788e4 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 8 Oct 2024 11:06:11 -0600 Subject: [PATCH 16/39] Upgrade crate versions to match Zephyr Upgrade the versions of all crates in this module to match the current Zephyr version of 3.7.0. It isn't completely clear if this is the right thing to do, as Zephyr doesn't maintain semantic versioning. But having the numbers match will help make it clear which version of Zephyr goes with which version of this crate. Signed-off-by: David Brown --- README.rst | 4 ++-- samples/hello_world/Cargo.toml | 2 +- tests/time/Cargo.toml | 2 +- zephyr-build/Cargo.toml | 2 +- zephyr-sys/Cargo.toml | 4 ++-- zephyr/Cargo.toml | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 21fa7e6..2900a59 100644 --- a/README.rst +++ b/README.rst @@ -70,7 +70,7 @@ your application directory, with options set so that it can find the Zephyr supp that the output will be contained within the Zephyr build directory. The :file:`Cargo.toml` will need to have a ``[lib]`` section that sets ``crate-type = -["staticlib"]``, and will need to include ``zephyr = "0.1.0"`` as a dependency. You can use +["staticlib"]``, and will need to include ``zephyr = "3.7.0"`` as a dependency. You can use crates.io and the Crate ecosystem to include any other dependencies you need. Just make sure that you use crates that support building with no-std. @@ -115,7 +115,7 @@ To your ``Cargo.toml`` file, add the following: .. code-block:: toml [build-dependencies] - zephyr-build = "0.1.0" + zephyr-build = "3.7.0" Then, you will need a ``build.rs`` file to call the support function. The following will work: diff --git a/samples/hello_world/Cargo.toml b/samples/hello_world/Cargo.toml index 4058e2d..a63ed5b 100644 --- a/samples/hello_world/Cargo.toml +++ b/samples/hello_world/Cargo.toml @@ -4,7 +4,7 @@ [package] # This must be rustapp for now. name = "rustapp" -version = "0.1.0" +version = "3.7.0" edition = "2021" description = "A sample hello world application in Rust" license = "Apache-2.0 or MIT" diff --git a/tests/time/Cargo.toml b/tests/time/Cargo.toml index 360788e..1dbfc4e 100644 --- a/tests/time/Cargo.toml +++ b/tests/time/Cargo.toml @@ -4,7 +4,7 @@ [package] # This must be rustapp for now. name = "rustapp" -version = "0.1.0" +version = "3.7.0" edition = "2021" description = "Tests of time" license = "Apache-2.0 or MIT" diff --git a/zephyr-build/Cargo.toml b/zephyr-build/Cargo.toml index c73a95e..a31eaa8 100644 --- a/zephyr-build/Cargo.toml +++ b/zephyr-build/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "zephyr-build" -version = "0.1.0" +version = "3.7.0" edition = "2021" description = """ Build-time support for Rust-based applications that run on Zephyr. diff --git a/zephyr-sys/Cargo.toml b/zephyr-sys/Cargo.toml index 62e2256..385dcd5 100644 --- a/zephyr-sys/Cargo.toml +++ b/zephyr-sys/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "zephyr-sys" -version = "0.1.0" +version = "3.7.0" edition = "2021" description = """ Zephyr low-level API bindings. @@ -15,4 +15,4 @@ Zephyr low-level API bindings. [build-dependencies] anyhow = "1.0" bindgen = { version = "0.69.4", features = ["experimental"] } -# zephyr-build = { version = "0.1.0", path = "../zephyr-build" } +# zephyr-build = { version = "3.7.0", path = "../zephyr-build" } diff --git a/zephyr/Cargo.toml b/zephyr/Cargo.toml index e0da63f..3775e27 100644 --- a/zephyr/Cargo.toml +++ b/zephyr/Cargo.toml @@ -3,14 +3,14 @@ [package] name = "zephyr" -version = "0.1.0" +version = "3.7.0" edition = "2021" description = """ Functionality for Rust-based applications that run on Zephyr. """ [dependencies] -zephyr-sys = { version = "0.1.0", path = "../zephyr-sys" } +zephyr-sys = { version = "3.7.0", path = "../zephyr-sys" } # Although paste is brought in, it is a compile-time macro, and is not linked into the application. paste = "1.0" @@ -42,4 +42,4 @@ features = ["alloc"] # Whether these need to be vendored is an open question. They are not # used by the core Zephyr tree, but are needed by zephyr applications. [build-dependencies] -zephyr-build = { version = "0.1.0", path = "../zephyr-build" } +zephyr-build = { version = "3.7.0", path = "../zephyr-build" } From 32b8fbfa5dbec38a8534c4a9cc5c92fe13595375 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 23 Sep 2024 15:34:55 -0600 Subject: [PATCH 17/39] zephyr: Add `k_uptime_get` This function is safe, so add a simple wrapper around the unsafe version. Signed-off-by: David Brown --- zephyr/src/sys.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zephyr/src/sys.rs b/zephyr/src/sys.rs index d772f5c..dfb16e4 100644 --- a/zephyr/src/sys.rs +++ b/zephyr/src/sys.rs @@ -30,6 +30,16 @@ pub const K_FOREVER: k_timeout_t = k_timeout_t { ticks: -1 }; /// performed immediately. pub const K_NO_WAIT: k_timeout_t = k_timeout_t { ticks: 0 }; +/// Return the current uptime of the system in ms. +/// +/// Direct Zephyr call. Precision is limited by the system tick timer. +#[inline] +pub fn uptime_get() -> i64 { + unsafe { + crate::raw::k_uptime_get() + } +} + pub mod critical { //! Zephyr implementation of critical sections. //! From 907c0446432fb06e6c312f66cfb304af24568204 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:10:54 -0600 Subject: [PATCH 18/39] zephyr: Implement Debug for Semaphore It just prints the type, but allows other structs containing Semaphores to be printed. Signed-off-by: David Brown --- zephyr/src/lib.rs | 3 +++ zephyr/src/object.rs | 16 +++++++++------- zephyr/src/sys/sync.rs | 7 +++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 822d30e..c865d4c 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -19,6 +19,9 @@ pub mod time; pub use error::{Error, Result}; +/// Re-exported for local macro use. +pub use paste::paste; + // Bring in the generated kconfig module pub mod kconfig { //! Zephyr Kconfig values. diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 77e9f9d..3316e4b 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -44,14 +44,16 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; /// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type /// T, and the wrapper type W. /// +/// TODO: Can we avoid the public fields with a const new method? +/// /// TODO: Handling const-defined alignment for these. pub struct StaticKernelObject { #[allow(dead_code)] /// The underlying zephyr kernel object. - pub(crate) value: UnsafeCell, + pub value: UnsafeCell, /// Initialization status of this object. Most objects will start uninitialized and be /// initialized manually. - pub(crate) init: AtomicUsize, + pub init: AtomicUsize, } /// Each can be wrapped appropriately. The wrapped type is the instance that holds the raw pointer. @@ -205,20 +207,20 @@ macro_rules! _kobj_rule { #[macro_export] macro_rules! _kobj_stack { ($v:vis, $name: ident, $size:expr) => { - ::paste::paste! { + $crate::paste! { // The actual stack itself goes into the no-init linker section. We'll use the user_name, // with _REAL appended, to indicate the real stack. #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())] - $v static [< $name _REAL >] $crate::sys::thread::RealThreadStack<{$crate::sys::thread::stack_len($size)}> = + $v static [< $name _REAL >]: $crate::sys::thread::RealStaticThreadStack<{$crate::sys::thread::stack_len($size)}> = unsafe { ::core::mem::zeroed() }; // The proxy object used to ensure initialization is placed in initialized memory. - $v static $name: $crate::object::StaticKernelObject<$crate::object::StaticThreadStack> = StaticKernelObject { - value: ::core::cell::UnsafeCell::new($crate::object::StaticThreadStack { + $v static $name: $crate::object::StaticKernelObject<$crate::sys::thread::StaticThreadStack> = $crate::object::StaticKernelObject { + value: ::core::cell::UnsafeCell::new($crate::sys::thread::StaticThreadStack { base: [< $name _REAL >].data.get() as *mut $crate::raw::z_thread_stack_element, size: $size, }), - init: $crate::atomic::AtomicUsize::new(0), + init: $crate::sync::atomic::AtomicUsize::new(0), }; } }; diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs index c67f878..03636a5 100644 --- a/zephyr/src/sys/sync.rs +++ b/zephyr/src/sys/sync.rs @@ -31,6 +31,7 @@ //! from a pool, although the objects will still be statically allocated. use core::ffi::c_uint; +use core::fmt; use crate::{ error::{Result, to_result_void}, @@ -129,3 +130,9 @@ impl Wrapped for StaticKernelObject { } } } + +impl fmt::Debug for Semaphore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "sys::Semaphore") + } +} From 3d2477dc7894753c8017a20d42e704efa2d58ab8 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:11:33 -0600 Subject: [PATCH 19/39] zephyr: StaticSemaphire needs to implement Sync This is required to allow it to be a static. Signed-off-by: David Brown --- zephyr/src/sys/sync.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs index 03636a5..2b284f7 100644 --- a/zephyr/src/sys/sync.rs +++ b/zephyr/src/sys/sync.rs @@ -112,6 +112,8 @@ impl Semaphore { /// [`Semaphore`] that is represents. pub type StaticSemaphore = StaticKernelObject; +unsafe impl Sync for StaticSemaphore {} + impl Wrapped for StaticKernelObject { type T = Semaphore; From 3fd043af9fafcd662096348a9adfec82c94d05fc Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:12:07 -0600 Subject: [PATCH 20/39] zephyr: Semaphore methods should not be `mut` The semaphore implementation is safe to use in multiple places, so mut is not necessary and prevents their use in most cases. Signed-off-by: David Brown --- zephyr/src/sys/sync.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs index 2b284f7..e965a07 100644 --- a/zephyr/src/sys/sync.rs +++ b/zephyr/src/sys/sync.rs @@ -65,7 +65,7 @@ impl Semaphore { /// Take a semaphore. /// /// Can be called from ISR if called with [`NoWait`]. - pub fn take(&mut self, timeout: T) -> Result<()> + pub fn take(&self, timeout: T) -> Result<()> where T: Into, { let timeout: Timeout = timeout.into(); @@ -79,7 +79,7 @@ impl Semaphore { /// /// This routine gives to the semaphore, unless the semaphore is already at its maximum /// permitted count. - pub fn give(&mut self) { + pub fn give(&self) { unsafe { k_sem_give(self.item) } From c88307f74b67f7dd94214a701f192696e6b22719 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:14:26 -0600 Subject: [PATCH 21/39] zephyr: hide docs of RealStaticThreadStack This is not intended to be visible, but needed for macros. Signed-off-by: David Brown --- zephyr/src/sys/thread.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index 3bd074a..9db05c6 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -56,6 +56,7 @@ pub const fn stack_len(size: usize) -> usize { size.next_multiple_of(ZR_STACK_ALIGN) } +#[doc(hidden)] /// A Zephyr stack declaration. /// /// It isn't meant to be used directly, as it needs additional decoration about linker sections and From 1b92ea5d251e0c4a69135212d1dc1a5d60af98c8 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:15:21 -0600 Subject: [PATCH 22/39] zephyr: Expose some fields publicly These need to be initialized by macros, so expose them publicly. It would probably be better to export a constructor. Signed-off-by: David Brown --- zephyr/src/sys/thread.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index 9db05c6..050612c 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -66,7 +66,7 @@ pub struct RealStaticThreadStack { #[allow(dead_code)] align: AlignAs, #[allow(dead_code)] - data: UnsafeCell<[z_thread_stack_element; SIZE]>, + pub data: UnsafeCell<[z_thread_stack_element; SIZE]>, #[allow(dead_code)] extra: [z_thread_stack_element; ZR_STACK_RESERVED], } @@ -74,15 +74,19 @@ pub struct RealStaticThreadStack { unsafe impl Sync for RealStaticThreadStack {} /// The dynamic stack value, which wraps the underlying stack. +/// +/// TODO: constructor instead of private. pub struct ThreadStack { - base: *mut z_thread_stack_element, - size: usize, + /// Private + pub base: *mut z_thread_stack_element, + /// Private + pub size: usize, } #[doc(hidden)] pub struct StaticThreadStack { - base: *mut z_thread_stack_element, - size: usize, + pub base: *mut z_thread_stack_element, + pub size: usize, } unsafe impl Sync for StaticKernelObject {} From 1971feb8062b4bf773cda4a6c7035ca6118ed1d9 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:16:15 -0600 Subject: [PATCH 23/39] zephyr: StaticThread must be Sync This is needed to initialize this value into a static. Signed-off-by: David Brown --- zephyr/src/sys/thread.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index 050612c..f8bfdda 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -135,6 +135,11 @@ pub struct Thread { options: u32, } +/// A statically defined thread. +pub type StaticThread = StaticKernelObject; + +unsafe impl Sync for StaticThread {} + impl Wrapped for StaticKernelObject { type T = Thread; type I = ThreadStack; From 97b217f48bdae17621cce18e39c02ce57b01def0 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:36:19 -0600 Subject: [PATCH 24/39] samples: hello_world: Fix dependency version Depend upon the 3.7.0 version of Zephyr. Signed-off-by: David Brown --- samples/hello_world/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/hello_world/Cargo.toml b/samples/hello_world/Cargo.toml index a63ed5b..dbf54f5 100644 --- a/samples/hello_world/Cargo.toml +++ b/samples/hello_world/Cargo.toml @@ -13,4 +13,4 @@ license = "Apache-2.0 or MIT" crate-type = ["staticlib"] [dependencies] -zephyr = "0.1.0" +zephyr = "3.7.0" From 2ccb6287da6fcc98fd47a24648091eb08944d0fb Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:36:56 -0600 Subject: [PATCH 25/39] zephyr: Fix some warnings by being conditional Use of Box depends on Alloc being enabled, so also conditionalize the extern and use declarations. Signed-off-by: David Brown --- zephyr/src/sys/thread.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index f8bfdda..ca70893 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -33,8 +33,10 @@ //! }); //! ``` +#[cfg(CONFIG_RUST_ALLOC)] extern crate alloc; +#[cfg(CONFIG_RUST_ALLOC)] use alloc::boxed::Box; use core::{cell::UnsafeCell, ffi::{c_int, c_void}}; From e8eebb24470a48bdce88e973c257407d4b544406 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:38:37 -0600 Subject: [PATCH 26/39] samples: philosophers: Fix yaml Update the yaml to remove not-yet-implemented variants, and make sure the regexp matches things the test actually prints. Signed-off-by: David Brown --- samples/philosophers/sample.yaml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/samples/philosophers/sample.yaml b/samples/philosophers/sample.yaml index b393344..cfd1eb4 100644 --- a/samples/philosophers/sample.yaml +++ b/samples/philosophers/sample.yaml @@ -8,22 +8,15 @@ common: regex: # Match the statistics, and make sure that each philosopher has at least 10 (two digits) # meals. - - "^\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]" + # - "^\\[\\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}, \\d{2,}\\]" + # + # Until the stastics have been implemented, just match on one of the children thinking + - "^Child 5 thinking \\(\\d+ ticks.*" tags: rust filter: CONFIG_RUST_SUPPORTED tests: - sample.rust.philosopher.sysmutex: + sample.rust.philosopher.semaphore: tags: introduction min_ram: 32 extra_configs: - - CONFIG_SYNC_SYS_MUTEX=y - sample.rust.philosopher.condvar: - tags: introduction - min_ram: 32 - extra_configs: - - CONFIG_SYNC_CONDVAR=y - sample.rust.philosopher.channel: - tags: introduction - min_ram: 32 - extra_configs: - - CONFIG_SYNC_CHANNEL=y + - CONFIG_SYNC_SYS_SEMAPHORE=y From a34507fd01f4374c9da234b582b9cbcf3c851936 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 11 Oct 2024 16:57:20 -0600 Subject: [PATCH 27/39] tests: time: Fix dependency Fix the version dependency to match the Zephyr release version. Signed-off-by: David Brown --- tests/time/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/time/Cargo.toml b/tests/time/Cargo.toml index 1dbfc4e..ffcba92 100644 --- a/tests/time/Cargo.toml +++ b/tests/time/Cargo.toml @@ -13,4 +13,4 @@ license = "Apache-2.0 or MIT" crate-type = ["staticlib"] [dependencies] -zephyr = "0.1.0" +zephyr = "3.7.0" From 91d6f33ebff954ea76b3934df50f72187a876a79 Mon Sep 17 00:00:00 2001 From: David Brown Date: Sat, 12 Oct 2024 07:29:13 -0600 Subject: [PATCH 28/39] zephyr-sys: Allow broken doc links in bindgen The bindgen code copies the doc strings directly from the C headers. These have a different syntax than the Rust doc headers. Until we can find or create a tool to convert these, disable these warnings. Signed-off-by: David Brown --- zephyr-sys/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zephyr-sys/src/lib.rs b/zephyr-sys/src/lib.rs index 3338ada..8934367 100644 --- a/zephyr-sys/src/lib.rs +++ b/zephyr-sys/src/lib.rs @@ -17,4 +17,6 @@ // Note, however, that this suppresses any warnings in the bindings about improper C types. #![allow(improper_ctypes)] +#![allow(rustdoc::broken_intra_doc_links)] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); From 177a9323d1ec1836c3be5b3c9cfc7580e413f2c4 Mon Sep 17 00:00:00 2001 From: David Brown Date: Sat, 12 Oct 2024 07:24:42 -0600 Subject: [PATCH 29/39] zephyr: object: Improve docs about safety Add comments to the object module describing the safety goals, and how we achieve them. Signed-off-by: David Brown --- zephyr/src/object.rs | 119 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 19 deletions(-) diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 3316e4b..1280b25 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -13,37 +13,94 @@ //! There are also kernel objects that are synthesized as part of the build. Most notably, there //! are ones generated by the device tree. //! -//! There are some funny rules about references and mutable references to memory that is -//! inaccessible. Basically, it is never valid to have a reference to something that isn't -//! accessible. However, we can have `*mut ktype` or `*const ktype`. In Rust, having multiple -//! `*mut` pointers live is undefined behavior. Zephyr makes extensive use of shared mutable -//! pointers (or sometimes immutable). We will not dereference these in Rust code, but merely pass -//! them to APIs in Zephyr that require them. +//! ## Safety //! -//! Most kernel objects use mutable pointers, and it makes sense to require the wrapper structure -//! to be mutable to these accesses. There a few cases, mostly generated ones that live in -//! read-only memory, notably device instances, that need const pointers. These will be -//! represented by a separate wrapper. +//! Zephyr has traditionally not focused on safety. Early versions of project goals, in fact, +//! emphasized performance and small code size as priorities over runtime checking of safety. Over +//! the years, this focus has changed a bit, and Zephyr does contain some additional checking, some +//! of which is optional. //! -//! # Initialization tracking +//! Zephyr is still constrained at compile time to checks that can be performed with the limits +//! of the C language. With Rust, we have a much greater ability to enforce many aspects of safety +//! at compile time. However, there is some complexity to doing this at the interface between the C +//! world and Rust. //! -//! The Kconfig `CONFIG_RUST_CHECK_KOBJ_INIT` enabled extra checking in Rust-based kernel objects. -//! This will result in a panic if the objects are used before the underlying object has been -//! initialized. The initialization must happen through the `StaticKernelObject::init_help` -//! method. +//! There are two types of kernel objects we deal with. There are kernel objects that are allocated +//! by C code (often auto-generated) that should be accessible to Rust. These are mostly `struct +//! device` values, and will be handled in a devices module. The other type are objects that +//! application code wishes to declare statically, and use from Rust code. That is the +//! responsibility of this module. (There will also be support for more dynamic management of +//! kernel objects, but this will be handled later). //! -//! TODO: Document how the wrappers work once we figure out how to implement them. +//! Static kernel objects in Zephyr are declared as C top-level variables (where the keyword static +//! means something different). It is the responsibility of the calling code to initialize these +//! items, make sure they are only initialized once, and to ensure that sharing of the object is +//! handled properly. All of these are concerns we can handle in Rust. +//! +//! To handle initialization, we pair each kernel object with a single atomic value, whose zero +//! value indicates [`KOBJ_UNINITIALIZED`]. There are a few instances of values that can be placed +//! into uninitialized memory in a C declaration that will need to be zero initialized as a Rust +//! static. The case of thread stacks is handled as a special case, where the initialization +//! tracking is kept separate so that the stack can still be placed in initialized memory. +//! +//! This state goes through two more values as the item is initialized, one indicating the +//! initialization is happening, and another indicating it has finished. +//! +//! For each kernel object, there will be two types. One, having a name of the form StaticThing, +//! and the other having the form Thing. The StaticThing will be used in a static declaration. +//! There is a [`kobj_define!`] macro that matches declarations of these values and adds the +//! necessary linker declarations to place these in the correct linker sections. This is the +//! equivalent of the set of macros in C, such as `K_SEM_DEFINE`. +//! +//! This StaticThing will have a single method [`init_once`] which accepts a single argument of a +//! type defined by the object. For most objects, it will just be an empty tuple `()`, but it can +//! be whatever initializer is needed for that type by Zephyr. Semaphores, for example, take the +//! initial value and the limit. Threads take as an initializer the stack to be used. +//! +//! This `init_once` will initialize the Zephyr object and return the `Thing` item that will have +//! the methods on it to use the object. Attributes such as `Sync`, and `Clone` will be defined +//! appropriately so as to match the semantics of the underlying Zephyr kernel object. Generally +//! this `Thing` type will simply be a container for a direct pointer, and thus using and storing +//! these will have the same characteristics as it would from C. +//! +//! Rust has numerous strict rules about mutable references, namely that it is not safe to have more +//! than one mutable reference. The language does allow multiple `*mut ktype` references, and their +//! safety depends on the semantics of what is pointed to. In the case of Zephyr, some of these are +//! intentionally thread safe (for example, things like `k_sem` which have the purpose of +//! synchronizing between threads). Others are not, and that is mirrored in Rust by whether or not +//! `Clone` and/or `Sync` are implemented. Please see the documentation of individual entities for +//! details for that object. +//! +//! In general, methods on `Thing` will require `&mut self` if there is any state to manage. Those +//! that are built around synchronization primitives, however, will generally use `&self`. In +//! general, objects that implement `Clone` will use `&self` because there would be no benefit to +//! mutable self when the object could be cloned. +//! +//! [`kobj_define!`]: crate::kobj_define +//! [`init_once`]: StaticKernelObject::init_once use core::{cell::UnsafeCell, mem}; use crate::sync::atomic::{AtomicUsize, Ordering}; +// The kernel object itself must be wrapped in `UnsafeCell` in Rust. This does several thing, but +// the primary feature that we want to declare to the Rust compiler is that this item has "interior +// mutability". One impact will be that the default linker section will be writable, even though +// the object will not be declared as mutable. It also affects the compiler as it will avoid things +// like aliasing and such on the data, as it will know that it is potentially mutable. In our case, +// the mutations happen from C code, so this is less important than the data being placed in the +// proper section. Many will have the link section overridden by the `kobj_define` macro. + /// A kernel object represented statically in Rust code. /// /// These should not be declared directly by the user, as they generally need linker decorations to /// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type /// T, and the wrapper type W. /// +/// Kernel objects will have their `StaticThing` implemented as `StaticKernelObject` where +/// `kobj` is the type of the underlying Zephyr object. `Thing` will usually be a struct with a +/// single field, which is a `*mut kobj`. +/// /// TODO: Can we avoid the public fields with a const new method? /// /// TODO: Handling const-defined alignment for these. @@ -56,9 +113,23 @@ pub struct StaticKernelObject { pub init: AtomicUsize, } -/// Each can be wrapped appropriately. The wrapped type is the instance that holds the raw pointer. +/// Define the Wrapping of a kernel object. +/// +/// This trait defines the association between a static kernel object and the two associated Rust +/// types: `StaticThing` and `Thing`. In the general case: there should be: +/// ``` +/// impl Wrapped for StaticKernelObject { +/// type T = Thing, +/// type I = (), +/// fn get_wrapped(&self, args: Self::I) -> Self::T { +/// let ptr = self.value.get(); +/// // Initizlie the kobj using ptr and possible the args. +/// Thing { ptr } +/// } +/// } +/// ``` pub trait Wrapped { - /// The wrapped type. This is what `take()` on the StaticKernelObject will return after + /// The wrapped type. This is what `init_once()` on the StaticKernelObject will return after /// initialization. type T; @@ -88,7 +159,8 @@ where StaticKernelObject: Wrapped, { /// Construct an empty of these objects, with the zephyr data zero-filled. This is safe in the - /// sense that Zephyr we track the initialization, and they start in the uninitialized state. + /// sense that Zephyr we track the initialization, they start in the uninitialized state, and + /// the zero value of the initialize atomic indicates that it is uninitialized. pub const fn new() -> StaticKernelObject { StaticKernelObject { value: unsafe { mem::zeroed() }, @@ -100,6 +172,8 @@ where /// /// Will return a single wrapped instance of this object. This will invoke the initialization, /// and return `Some` for the wrapped containment type. + /// + /// If it is called an additional time, it will return None. pub fn init_once(&self, args: ::I) -> Option<::T> { if let Err(_) = self.init.compare_exchange( KOBJ_UNINITIALIZED, @@ -225,6 +299,13 @@ macro_rules! _kobj_stack { } }; + // This initializer needs to have the elements of the array initialized to fixed elements of the + // `RealStaticThreadStack`. Unfortunately, methods such as [`each_ref`] on the array are not + // const and can't be used in a static initializer. We could use a recursive macro definition + // to perform the initialization, but this would require the array size to only be an integer + // literal (constants aren't calculated until after macro expansion). It may also be possible + // to write a constructor for the array as a const fn, which would greatly simplify the + // initialization here. ($v:vis, $name: ident, $size:expr, $asize:expr) => { compile_error!("TODO: Stack initializer array"); } From 1cd9299f0cc91d559fb7af9fda7cf6b1927d3850 Mon Sep 17 00:00:00 2001 From: David Brown Date: Sat, 12 Oct 2024 07:39:58 -0600 Subject: [PATCH 30/39] zephyr: printk: Fix doc error The docs reference `alloc::format`. Include the extern for this crate so that this will work. Signed-off-by: David Brown --- zephyr/src/printk.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zephyr/src/printk.rs b/zephyr/src/printk.rs index f68fc5b..6351f2a 100644 --- a/zephyr/src/printk.rs +++ b/zephyr/src/printk.rs @@ -5,6 +5,9 @@ //! //! This uses the `k_str_out` syscall, which is part of printk to output to the console. +// Needed by the docs, even though we don't allocate. +extern crate alloc; + use core::fmt::{ Arguments, Result, From 266a7a7f58475efcfdae27ef1e3c0c8d880bf571 Mon Sep 17 00:00:00 2001 From: David Brown Date: Sat, 12 Oct 2024 07:40:35 -0600 Subject: [PATCH 31/39] zephyr: sys: sync: Fix some doc links Fix a few broken doc links, by filling in full references. Signed-off-by: David Brown --- zephyr/src/sys/sync.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zephyr/src/sys/sync.rs b/zephyr/src/sys/sync.rs index e965a07..b19b593 100644 --- a/zephyr/src/sys/sync.rs +++ b/zephyr/src/sys/sync.rs @@ -65,6 +65,8 @@ impl Semaphore { /// Take a semaphore. /// /// Can be called from ISR if called with [`NoWait`]. + /// + /// [`NoWait`]: crate::time::NoWait pub fn take(&self, timeout: T) -> Result<()> where T: Into, { @@ -89,6 +91,8 @@ impl Semaphore { /// /// This resets the count to zero. Any outstanding [`take`] calls will be aborted with /// `Error(EAGAIN)`. + /// + /// [`take`]: Self::take pub fn reset(&mut self) { unsafe { k_sem_reset(self.item) @@ -108,8 +112,10 @@ impl Semaphore { /// A static Zephyr `k_sem`. /// /// This is intended to be used from within the `kobj_define!` macro. It declares a static ksem -/// that will be properly registered with the Zephyr kernel object system. Call [`take`] to get the -/// [`Semaphore`] that is represents. +/// that will be properly registered with the Zephyr kernel object system. Call [`init_once`] to +/// get the [`Semaphore`] that is represents. +/// +/// [`init_once`]: StaticKernelObject::init_once pub type StaticSemaphore = StaticKernelObject; unsafe impl Sync for StaticSemaphore {} From fdd97aeef9e6d158157d1c542d799c16a5e44225 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 14 Oct 2024 09:47:58 -0600 Subject: [PATCH 32/39] zephyr: printk: Remove dependency on alloc This had an `extern crate alloc` for a link in the docs, but there is no actual dependency on this. Replace this with a resolved link to the standard documentation for formatting. Signed-off-by: David Brown --- zephyr/src/printk.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/zephyr/src/printk.rs b/zephyr/src/printk.rs index 6351f2a..4c7ebbd 100644 --- a/zephyr/src/printk.rs +++ b/zephyr/src/printk.rs @@ -5,9 +5,6 @@ //! //! This uses the `k_str_out` syscall, which is part of printk to output to the console. -// Needed by the docs, even though we don't allocate. -extern crate alloc; - use core::fmt::{ Arguments, Result, @@ -17,13 +14,13 @@ use core::fmt::{ /// Print to Zephyr's console, without a newline. /// -/// This macro uses the same syntax as std's [`format!`], but writes to the Zephyr console instead. +/// This macro uses the same syntax as std's +/// [`format!`](https://doc.rust-lang.org/stable/std/fmt/index.html), but writes to the Zephyr +/// console instead. /// /// if `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation, /// and due to private accessors in the Zephyr printk implementation, the lock is only over groups /// of a small buffer size. This buffer must be kept fairly small, as it resides on the stack. -/// -/// [`format!`]: alloc::format #[macro_export] macro_rules! printk { ($($arg:tt)*) => {{ From d01153ae24b4efc588231c9cab575d959765bb7e Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 08:37:21 -0600 Subject: [PATCH 33/39] zephyr: Create export type alias Although the user is not intended to actually create these static thread stack objects, make an alias for the type in `_export` so that the macro is a little easier to read. This will become more important as this type picks up some constructors. Signed-off-by: David Brown --- zephyr/src/lib.rs | 5 +++++ zephyr/src/object.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index c865d4c..85b6892 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -75,6 +75,11 @@ pub mod raw { #[doc(hidden)] pub mod _export { pub use core::format_args; + + use crate::{object::StaticKernelObject, sys::thread::StaticThreadStack}; + + /// Type alias for the thread stack kernel object. + pub type KStaticThreadStack = StaticKernelObject; } // Mark this as `pub` so the docs can be read. diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 1280b25..7c8673d 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -289,7 +289,7 @@ macro_rules! _kobj_stack { unsafe { ::core::mem::zeroed() }; // The proxy object used to ensure initialization is placed in initialized memory. - $v static $name: $crate::object::StaticKernelObject<$crate::sys::thread::StaticThreadStack> = $crate::object::StaticKernelObject { + $v static $name: $crate::_export::KStaticThreadStack = $crate::object::StaticKernelObject { value: ::core::cell::UnsafeCell::new($crate::sys::thread::StaticThreadStack { base: [< $name _REAL >].data.get() as *mut $crate::raw::z_thread_stack_element, size: $size, From 7375e4434650163a66ec54c95ea199eb5834caee Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 08:49:07 -0600 Subject: [PATCH 34/39] zephyr: Use constructor for static thread stack Instead of fully expanding the thread stack initializer in the kobj_define macro, create a hidden constructor as a const fn. Signed-off-by: David Brown --- zephyr/src/object.rs | 9 ++------- zephyr/src/sys/thread.rs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 7c8673d..c24a48a 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -289,13 +289,8 @@ macro_rules! _kobj_stack { unsafe { ::core::mem::zeroed() }; // The proxy object used to ensure initialization is placed in initialized memory. - $v static $name: $crate::_export::KStaticThreadStack = $crate::object::StaticKernelObject { - value: ::core::cell::UnsafeCell::new($crate::sys::thread::StaticThreadStack { - base: [< $name _REAL >].data.get() as *mut $crate::raw::z_thread_stack_element, - size: $size, - }), - init: $crate::sync::atomic::AtomicUsize::new(0), - }; + $v static $name: $crate::_export::KStaticThreadStack = + $crate::_export::KStaticThreadStack::new_from(&[< $name _REAL >]); } }; diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index ca70893..46755d6 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -50,7 +50,7 @@ use zephyr_sys::{ }; use super::K_NO_WAIT; -use crate::{align::AlignAs, object::{StaticKernelObject, Wrapped}}; +use crate::{align::AlignAs, object::{StaticKernelObject, Wrapped}, sync::atomic::AtomicUsize}; /// Adjust the stack size for alignment. Note that, unlike the C code, we don't include the /// reservation in this, as it has its own fields in the struct. @@ -123,6 +123,22 @@ impl Wrapped for StaticKernelObject { } } +impl StaticKernelObject { + /// Construct a StaticThreadStack object. + /// + /// This is not intended to be directly called, but is used by the [`kobj_define`] macro. + #[doc(hidden)] + pub const fn new_from(real: &RealStaticThreadStack) -> Self { + Self { + value: UnsafeCell::new(StaticThreadStack { + base: real.data.get() as *mut z_thread_stack_element, + size: SZ, + }), + init: AtomicUsize::new(0), + } + } +} + /// A single Zephyr thread. /// /// This wraps a `k_thread` type within Rust. This value is returned from From 8bbade354f6e9983e7e2914661ee3f5e9a9583e3 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 09:18:27 -0600 Subject: [PATCH 35/39] zephyr: object: Add kboj_define support for arrays of stacks Create a const constructor function for building the stack arrays. This requires unsafe, and can't use MaybeUninit, because the array version of this is still unsable. Just use zero initialization, and make sure the loop initializes everything. Use this function in the kobj_define macro in order to allow apps to define arrays of thread stacks, in addition to arrays of threads. Signed-off-by: David Brown --- zephyr/src/object.rs | 15 +++++++++++++-- zephyr/src/sys/thread.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index c24a48a..417c1bd 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -302,6 +302,17 @@ macro_rules! _kobj_stack { // to write a constructor for the array as a const fn, which would greatly simplify the // initialization here. ($v:vis, $name: ident, $size:expr, $asize:expr) => { - compile_error!("TODO: Stack initializer array"); - } + $crate::paste! { + // The actual stack itself goes into the no-init linker section. We'll use the user_name, + // with _REAL appended, to indicate the real stack. + #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())] + $v static [< $name _REAL >]: + [$crate::sys::thread::RealStaticThreadStack<{$crate::sys::thread::stack_len($size)}>; $asize] = + unsafe { ::core::mem::zeroed() }; + + $v static $name: + [$crate::_export::KStaticThreadStack; $asize] = + $crate::_export::KStaticThreadStack::new_from_array(&[< $name _REAL >]); + } + }; } diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index 46755d6..abdd719 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -38,7 +38,7 @@ extern crate alloc; #[cfg(CONFIG_RUST_ALLOC)] use alloc::boxed::Box; -use core::{cell::UnsafeCell, ffi::{c_int, c_void}}; +use core::{cell::UnsafeCell, ffi::{c_int, c_void}, mem}; use zephyr_sys::{ k_thread, @@ -137,6 +137,32 @@ impl StaticKernelObject { init: AtomicUsize::new(0), } } + + /// Construct an array of StaticThreadStack kernel objects, based on the same sized array of the + /// RealStaticThreadStack objects. + /// + /// This is not intended to be directly called, but is used by the [`kobj_define`] macro. + #[doc(hidden)] + pub const fn new_from_array( + real: &[RealStaticThreadStack; N], + ) -> [Self; N] { + // Rustc currently doesn't support iterators in constant functions, but it does allow simple + // looping. Since the value is not Copy, we need to use the MaybeUninit with a bit of + // unsafe. This initialization is safe, as we loop through all of the entries, giving them + // a value. + // + // In addition, MaybeUninit::uninit_array is not stable, so do this the old unsafe way. + // let mut res: [MaybeUninit; N] = MaybeUninit::uninit_array(); + // Note that `mem::uninitialized` in addition to being deprecated, isn't const. But, since + // this is a const computation, zero-filling shouldn't hurt anything. + let mut res: [Self; N] = unsafe { mem::zeroed() }; + let mut i = 0; + while i < N { + res[i] = Self::new_from(&real[i]); + i += 1; + } + res + } } /// A single Zephyr thread. From 98a15fe38b381017fb7ef70ce395b53873c3ad59 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 09:22:50 -0600 Subject: [PATCH 36/39] samples: philosophers: Convert to array of stacks Now that kboj_define supports arrays of thread stacks, change the expanded definitions into array definitions. The demo now properly supports changing `NUM_PHIL` to use a different number of philosophers. Signed-off-by: David Brown --- samples/philosophers/src/lib.rs | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/samples/philosophers/src/lib.rs b/samples/philosophers/src/lib.rs index f8e2821..7e51430 100644 --- a/samples/philosophers/src/lib.rs +++ b/samples/philosophers/src/lib.rs @@ -13,7 +13,6 @@ extern crate alloc; #[allow(unused_imports)] use alloc::boxed::Box; use alloc::vec::Vec; -use zephyr::sys::thread::Thread; use zephyr::time::{Duration, sleep, Tick}; use zephyr::{ printkln, @@ -63,19 +62,10 @@ extern "C" fn rust_main() { let syncers = get_syncer(); printkln!("Pre fork"); - // At this time, the arrays of threads are not supported, so manually unroll the loop for now. - // If NUM_PHIL is changed, this loop and the declarations at the end will have to be updated. - let threads: [Thread; NUM_PHIL] = [ - PHIL_THREAD_1.init_once(PHIL_STACK_1.init_once(()).unwrap()).unwrap(), - PHIL_THREAD_2.init_once(PHIL_STACK_2.init_once(()).unwrap()).unwrap(), - PHIL_THREAD_3.init_once(PHIL_STACK_3.init_once(()).unwrap()).unwrap(), - PHIL_THREAD_4.init_once(PHIL_STACK_4.init_once(()).unwrap()).unwrap(), - PHIL_THREAD_5.init_once(PHIL_STACK_5.init_once(()).unwrap()).unwrap(), - PHIL_THREAD_6.init_once(PHIL_STACK_6.init_once(()).unwrap()).unwrap(), - ]; for (i, syncer) in (0..NUM_PHIL).zip(syncers.into_iter()) { - threads[i].spawn(move || { + let thread = PHIL_THREADS[i].init_once(PHIL_STACKS[i].init_once(()).unwrap()).unwrap(); + thread.spawn(move || { phil_thread(i, syncer); }); } @@ -139,17 +129,6 @@ fn get_random_delay(id: usize, period: usize) -> Duration { } kobj_define! { - static PHIL_THREAD_1: StaticThread; - static PHIL_THREAD_2: StaticThread; - static PHIL_THREAD_3: StaticThread; - static PHIL_THREAD_4: StaticThread; - static PHIL_THREAD_5: StaticThread; - static PHIL_THREAD_6: StaticThread; - - static PHIL_STACK_1: ThreadStack; - static PHIL_STACK_2: ThreadStack; - static PHIL_STACK_3: ThreadStack; - static PHIL_STACK_4: ThreadStack; - static PHIL_STACK_5: ThreadStack; - static PHIL_STACK_6: ThreadStack; + static PHIL_THREADS: [StaticThread; NUM_PHIL]; + static PHIL_STACKS: [ThreadStack; NUM_PHIL]; } From 861cb834890120fda8f9ad0f12bd6d91e7cd25de Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 09:30:15 -0600 Subject: [PATCH 37/39] zephyr: docgen Add a small docgen crate. To help facilitate checking the docs, this project can be built, and then `cargo doc` run there. This includes the symlink for the config file, because that is needed in order for this to be able to build. Signed-off-by: David Brown --- docgen/.cargo/config.toml | 1 + docgen/.gitignore | 7 +++++++ docgen/CMakeLists.txt | 8 ++++++++ docgen/Cargo.toml | 16 ++++++++++++++++ docgen/prj.conf | 5 +++++ docgen/src/lib.rs | 16 ++++++++++++++++ 6 files changed, 53 insertions(+) create mode 120000 docgen/.cargo/config.toml create mode 100644 docgen/.gitignore create mode 100644 docgen/CMakeLists.txt create mode 100644 docgen/Cargo.toml create mode 100644 docgen/prj.conf create mode 100644 docgen/src/lib.rs diff --git a/docgen/.cargo/config.toml b/docgen/.cargo/config.toml new file mode 120000 index 0000000..f26aaf0 --- /dev/null +++ b/docgen/.cargo/config.toml @@ -0,0 +1 @@ +../../../../../zephyr/build/rust/sample-cargo-config.toml \ No newline at end of file diff --git a/docgen/.gitignore b/docgen/.gitignore new file mode 100644 index 0000000..80e243a --- /dev/null +++ b/docgen/.gitignore @@ -0,0 +1,7 @@ +# In this case, we do want the symlink checked in. We'll assume we have the module in the standard +# module place. +# +# On Windows, this symlink will just get checked out as a regular file and will have to be replaced +# with a copy (or real symlinks enabled). But, this shouldn't affect CI runs of the documentation, +# which are done on Linux. +!.cargo/ diff --git a/docgen/CMakeLists.txt b/docgen/CMakeLists.txt new file mode 100644 index 0000000..3030520 --- /dev/null +++ b/docgen/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(hello_rust_world) +rust_cargo_application() diff --git a/docgen/Cargo.toml b/docgen/Cargo.toml new file mode 100644 index 0000000..5588550 --- /dev/null +++ b/docgen/Cargo.toml @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +[package] +# This must be rustapp for now. +name = "rustapp" +version = "3.7.0" +edition = "2021" +description = "A small application to generate documentation" +license = "Apache-2.0 or MIT" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +zephyr = "3.7.0" diff --git a/docgen/prj.conf b/docgen/prj.conf new file mode 100644 index 0000000..930445c --- /dev/null +++ b/docgen/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RUST=y +CONFIG_RUST_ALLOC=y diff --git a/docgen/src/lib.rs b/docgen/src/lib.rs new file mode 100644 index 0000000..b29be52 --- /dev/null +++ b/docgen/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +#![no_std] + +use zephyr::printkln; + +// Reference the Zephyr crate so that the panic handler gets used. This is only needed if no +// symbols from the crate are directly used. +extern crate zephyr; + +#[no_mangle] +extern "C" fn rust_main() { + printkln!("Hello world from Rust on {}", + zephyr::kconfig::CONFIG_BOARD); +} From 057e1695da1bd1efcfb98d2314b80ee9730eca8c Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 09:43:04 -0600 Subject: [PATCH 38/39] zephyr: Fix some broken doc comments Ensure that the links actually resolve correctly, eliminating all of the warnings from a doc build. Signed-off-by: David Brown --- zephyr/src/printk.rs | 4 ++-- zephyr/src/sys/thread.rs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/zephyr/src/printk.rs b/zephyr/src/printk.rs index 4c7ebbd..8a281df 100644 --- a/zephyr/src/printk.rs +++ b/zephyr/src/printk.rs @@ -31,8 +31,8 @@ macro_rules! printk { /// Print to Zephyr's console, with a newline. /// /// This macro uses the same syntax as std's -/// [`format!`], but writes to the Zephyr console -/// instead. See `std::fmt` for more information. +/// [`format!`](https://doc.rust-lang.org/stable/std/fmt/index.html), but writes to the Zephyr +/// console instead. /// /// If `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation, /// and due to private accessors in the Zephyr printk implementation, the lock is only over groups diff --git a/zephyr/src/sys/thread.rs b/zephyr/src/sys/thread.rs index abdd719..4e9725f 100644 --- a/zephyr/src/sys/thread.rs +++ b/zephyr/src/sys/thread.rs @@ -18,7 +18,7 @@ //! } //! ``` //! -//! Each of these has a `.take(...)` method that returns the single usable instance. The +//! Each of these has a [`init_once`] method that returns the single usable instance. The //! StaticThread takes the stack retrieved by take as its argument. This will return a //! ThreadStarter, where various options can be set on the thread, and then it started with one of //! `spawn`, or `simple_spawn` (spawn requires `CONFIG_RUST_ALLOC`). @@ -26,12 +26,14 @@ //! Provided that `CONFIG_RUST_ALLOC` has been enabled (recommended): the read can be initialized as //! follows: //! ``` -//! let mut thread = MY_THREAD.take(MY_THREAD_STACK.take().unwrap()).unwrap(); +//! let mut thread = MY_THREAD.init_once(MY_THREAD_STACK.init_once(()).unwrap()).unwrap(); //! thread.set_priority(5); //! let child = thread.spawn(|| move { //! // thread code... //! }); //! ``` +//! +//! [`init_once`]: StaticKernelObject::init_once #[cfg(CONFIG_RUST_ALLOC)] extern crate alloc; From d11c5d2492a0acb1127ec0a3b7db06f481a21647 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 15 Oct 2024 10:10:22 -0600 Subject: [PATCH 39/39] module: Make sure all tests are run Don't just run hello world, but all samples that are under samples. Signed-off-by: David Brown --- zephyr/module.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zephyr/module.yml b/zephyr/module.yml index ef97cdf..2bf1c9f 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -4,6 +4,6 @@ build: cmake: . kconfig: Kconfig samples: - - samples/hello_world + - samples tests: - tests