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