blob: 315bebae4f2d4b27a6edb857952d5632e0d9ea93 [file] [edit]
// Copyright 2026 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#![no_std]
#![no_main]
use core::sync::atomic::{AtomicU32, Ordering};
use main_codegen::handle;
use pw_kernel_debug_mailbox::{Mailbox, annotate_kernel_debug_mailbox};
use pw_kernel_debug_mailbox_protocol::HostCommand;
use pw_status::Result;
use userspace::{entry, syscall};
// NOTE: Atomic operations will not work on platforms without atomic support.
static THREAD_DONE: AtomicU32 = AtomicU32::new(0);
#[unsafe(no_mangle)]
static TEST_MAILBOX: Mailbox<HostCommand, AtomicU32> = Mailbox::new(HostCommand::None);
annotate_kernel_debug_mailbox!("test_mailbox", TEST_MAILBOX);
#[unsafe(no_mangle)]
pub extern "C" fn test_thread_entry(_arg: usize) -> ! {
test_logger::info!("Test thread started");
THREAD_DONE.store(1, Ordering::SeqCst);
test_logger::info!("Test thread exiting");
syscall::thread_exit(42);
}
#[unsafe(no_mangle)]
pub extern "C" fn spin_thread_entry(_arg: usize) -> ! {
test_logger::info!("Spin thread started");
loop {}
}
fn do_test() -> Result<()> {
test_logger::start("Tracing");
let thread_handle = handle::TEST_THREAD;
static mut THREAD_STACK: [u8; 1024] = [0; 1024];
let initial_pc = test_thread_entry as *const () as usize;
let initial_sp =
unsafe { core::ptr::addr_of_mut!(THREAD_STACK).cast::<u8>().add(1024) as usize };
test_logger::info!("Starting test thread");
syscall::thread_start(thread_handle, initial_pc, initial_sp)?;
test_logger::info!("Waiting for test thread to terminate");
syscall::object_wait(
thread_handle,
syscall::Signals::JOINABLE,
userspace::time::Instant::MAX,
)?;
let status = syscall::task_join(thread_handle)?;
if status != syscall::ExitStatus::Success(42) {
test_logger::error!("Thread joined with unexpected status");
return Err(pw_status::Error::Internal);
}
test_logger::info!("Thread joined");
let done = THREAD_DONE.load(Ordering::SeqCst);
if done != 1 {
return Err(pw_status::Error::Internal);
}
test_logger::info!("Starting test thread again for external termination");
let initial_pc = spin_thread_entry as *const () as usize;
syscall::thread_start(thread_handle, initial_pc, initial_sp)?;
test_logger::info!("Terminating test thread from main thread");
syscall::task_terminate(thread_handle)?;
test_logger::info!("Waiting for test thread to terminate");
syscall::object_wait(
thread_handle,
syscall::Signals::JOINABLE,
userspace::time::Instant::from_ticks(u64::MAX),
)?;
let status = syscall::task_join(thread_handle)?;
if status != syscall::ExitStatus::TerminatedBySyscall {
test_logger::error!("Thread joined with unexpected status (expected TerminatedBySyscall)");
return Err(pw_status::Error::Internal);
}
test_logger::info!("Thread joined (terminated from outside)");
test_logger::info!("Waiting for HostCommand in mailbox to exit");
loop {
if let Some(message) = TEST_MAILBOX.try_take()
&& message == HostCommand::Exit
{
test_logger::info!("Got signal to exit, doing so now.");
break;
}
}
Ok(())
}
#[entry]
fn main_entry() -> Result<()> {
let ret = do_test()
.inspect(|_| test_logger::passed("Tracing"))
.inspect_err(|e| {
test_logger::failed("Tracing");
test_logger::error!("status code: {}", *e as u32);
});
let _ = syscall::debug_shutdown(ret);
ret
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
test_logger::error!("PANIC");
let _ = syscall::debug_shutdown(Err(pw_status::Error::Internal));
loop {}
}