• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #![no_std]
15 #![feature(const_trait_impl)]
16 #![feature(naked_functions)]
17 
18 use foreign_box::ForeignBox;
19 use pw_log::info;
20 
21 mod arch;
22 #[cfg(not(feature = "std_panic_handler"))]
23 mod panic;
24 mod scheduler;
25 pub mod sync;
26 mod target;
27 
28 use scheduler::yield_timeslice;
29 use scheduler::Stack;
30 use scheduler::Thread;
31 use scheduler::SCHEDULER_STATE;
32 
33 use arch::{Arch, ArchInterface};
34 use sync::mutex::Mutex;
35 
36 // A structure intended to be statically allocated to hold a Thread structure that will
37 // be constructed at run time.
38 #[repr(C, align(4))]
39 struct ThreadBuffer {
40     buffer: [u8; size_of::<Thread>()],
41 }
42 
43 impl ThreadBuffer {
new() -> Self44     const fn new() -> Self {
45         ThreadBuffer {
46             buffer: [0; size_of::<Thread>()],
47         }
48     }
49 
50     // Create and new a thread out of the internal u8 buffer.
51     // TODO: figure out how to properly statically construct a thread or
52     // make sure this function can only be called once.
53     #[inline(never)]
alloc_thread(&mut self) -> ForeignBox<Thread>54     unsafe fn alloc_thread(&mut self) -> ForeignBox<Thread> {
55         assert!(self.buffer.as_ptr().align_offset(align_of::<Thread>()) == 0);
56         let thread_ptr = self.buffer.as_mut_ptr() as *mut Thread;
57         thread_ptr.write(Thread::new());
58         ForeignBox::new_from_ptr(&mut *thread_ptr)
59     }
60 }
61 
62 pub struct Kernel {}
63 
64 impl Kernel {
main() -> !65     pub fn main() -> ! {
66         target::console_init();
67         info!("Welcome to Maize on {}!", target::name());
68 
69         Arch::early_init();
70 
71         let mut bootstrap_thread;
72         #[allow(static_mut_refs)]
73         unsafe {
74             info!("allocating bootstrap thread");
75             static mut THREAD_BUFFER_BOOTSTRAP: ThreadBuffer = ThreadBuffer::new();
76             bootstrap_thread = THREAD_BUFFER_BOOTSTRAP.alloc_thread();
77 
78             info!("initializing bootstrap thread");
79             static mut STACK_BOOTSTRAP: [u8; 2048] = [0; 2048];
80             bootstrap_thread.initialize(
81                 Stack::from_slice(&STACK_BOOTSTRAP),
82                 bootstrap_thread_entry,
83                 0,
84             );
85         }
86 
87         info!("created thread, bootstrapping");
88 
89         // special case where we bootstrap the system by half context switching to this thread
90         scheduler::bootstrap_scheduler(bootstrap_thread);
91 
92         // never get to here
93     }
94 }
95 
96 // completion of main in thread context
bootstrap_thread_entry(_arg: usize)97 fn bootstrap_thread_entry(_arg: usize) {
98     info!("Welcome to the first thread, continuing bootstrap");
99     assert!(Arch::interrupts_enabled());
100 
101     Arch::init();
102 
103     SCHEDULER_STATE.lock().dump_all_threads();
104 
105     let mut idle_thread;
106     #[allow(static_mut_refs)]
107     unsafe {
108         info!("allocating idle_thread");
109         static mut IDLE_THREAD_BUFFER: ThreadBuffer = ThreadBuffer::new();
110         idle_thread = IDLE_THREAD_BUFFER.alloc_thread();
111 
112         info!("initializing idle_thread");
113         static mut IDLE_STACK: [u8; 2048] = [0; 2048];
114         idle_thread.initialize(Stack::from_slice(&IDLE_STACK), idle_thread_entry, 0usize);
115     }
116     SCHEDULER_STATE.lock().dump_all_threads();
117 
118     // TODO: Create a few test threads
119     let mut thread_a;
120     #[allow(static_mut_refs)]
121     unsafe {
122         info!("allocating thread A");
123         static mut THREAD_BUFFER_A: ThreadBuffer = ThreadBuffer::new();
124         thread_a = THREAD_BUFFER_A.alloc_thread();
125 
126         info!("initializing thread A");
127         static mut STACK_A: [u8; 2048] = [0; 2048];
128         thread_a.initialize(
129             Stack::from_slice(&STACK_A),
130             test_thread_entry_a,
131             'a' as usize,
132         );
133     }
134     SCHEDULER_STATE.lock().dump_all_threads();
135 
136     let mut thread_b;
137     #[allow(static_mut_refs)]
138     unsafe {
139         info!("allocating thread B");
140         static mut THREAD_BUFFER_B: ThreadBuffer = ThreadBuffer::new();
141         thread_b = THREAD_BUFFER_B.alloc_thread();
142 
143         info!("initializing thread B");
144         static mut STACK_B: [u8; 2048] = [0; 2048];
145         thread_b.initialize(
146             Stack::from_slice(&STACK_B),
147             test_thread_entry_b,
148             'b' as usize,
149         );
150     }
151 
152     Thread::start(idle_thread);
153     Thread::start(thread_a);
154     Thread::start(thread_b);
155 
156     SCHEDULER_STATE.lock().dump_all_threads();
157 
158     info!("End of kernel test");
159     assert!(Arch::interrupts_enabled());
160 
161     info!("Exiting bootstrap thread");
162 }
163 
164 #[allow(dead_code)]
idle_thread_entry(_arg: usize)165 fn idle_thread_entry(_arg: usize) {
166     // Fake idle thread to keep the runqueue from being empty if all threads are blocked.
167     assert!(Arch::interrupts_enabled());
168     loop {
169         Arch::idle();
170     }
171 }
172 
173 static TEST_COUNTER: Mutex<u64> = Mutex::new(0);
174 
175 #[allow(dead_code)]
test_thread_entry_a(_arg: usize)176 fn test_thread_entry_a(_arg: usize) {
177     info!("I'm thread A");
178     assert!(Arch::interrupts_enabled());
179     loop {
180         let mut counter = TEST_COUNTER.lock();
181         info!("Thread A incrementing counter");
182         *counter += 1;
183         scheduler::TICK_WAIT_QUEUE.lock().wait();
184     }
185 }
186 
187 #[allow(dead_code)]
test_thread_entry_b(_arg: usize)188 fn test_thread_entry_b(_arg: usize) {
189     info!("I'm thread A");
190     assert!(Arch::interrupts_enabled());
191     loop {
192         let counter = TEST_COUNTER.lock();
193         info!("Thread B: counter value {}", *counter);
194         drop(counter);
195         yield_timeslice();
196     }
197 }
198