blob: cce5fa96a45820b6dbd523ecb1d033f32f8cd6a0 [file]
// Copyright 2025 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_main]
#![no_std]
use handler_codegen::handle;
use pw_status::{Error, Result};
use userspace::syscall::Signals;
use userspace::time::Instant;
use userspace::{entry, syscall};
fn wait_group() -> Result<()> {
test_logger::info!("WaitGroup service starting");
syscall::wait_group_add(handle::WAIT_GROUP_1, handle::IPC_A, Signals::READABLE, 11)?;
syscall::wait_group_add(handle::WAIT_GROUP_1, handle::IPC_B, Signals::READABLE, 22)?;
for i in 0..3 {
test_logger::info!("waiting for objects iteration {}", i as usize);
let wait_return =
syscall::object_wait(handle::WAIT_GROUP_1, Signals::READABLE, Instant::MAX)?;
let (ipc_handle, expected_char) = match i {
0 => {
if wait_return.user_data != 11
|| !wait_return.pending_signals.contains(Signals::READABLE)
{
return Err(Error::Internal);
}
(handle::IPC_A, 'a')
}
1 => {
if wait_return.user_data != 22
|| !wait_return.pending_signals.contains(Signals::READABLE)
{
return Err(Error::Internal);
}
(handle::IPC_B, 'b')
}
2 => {
if wait_return.user_data != 11
|| !wait_return.pending_signals.contains(Signals::READABLE)
{
return Err(Error::Internal);
}
(handle::IPC_A, 'c')
}
_ => {
test_logger::error!("Unknown iteration: {}", i as usize);
return Err(Error::Internal);
}
};
test_logger::info!("done waiting on objects iteration {}", i as usize);
// Read the payload.
let mut buffer = [0u8; size_of::<char>()];
let len = syscall::channel_read(ipc_handle, 0, &mut buffer)?;
if len != buffer.len() {
return Err(Error::OutOfRange);
};
// Convert the payload to a character.
let Some(c) = char::from_u32(u32::from_ne_bytes(buffer)) else {
return Err(Error::InvalidArgument);
};
if c != expected_char {
test_logger::error!(
"Received {} character, {} expected",
c as char,
expected_char as char
);
return Err(Error::InvalidArgument);
}
// Respond to the IPC with the iteration number.
let mut response_buffer = [0u8; size_of::<usize>()];
response_buffer.copy_from_slice(&(i as usize).to_ne_bytes());
syscall::channel_respond(ipc_handle, &response_buffer)?;
}
test_logger::info!("Objects can only be in one wait group");
let add_res =
syscall::wait_group_add(handle::WAIT_GROUP_2, handle::IPC_A, Signals::READABLE, 3);
if let Err(e) = add_res {
if e != Error::ResourceExhausted {
return Err(Error::Internal);
}
} else {
return Err(Error::Internal);
}
test_logger::info!("Object removed from incorrect wait group");
let remove_res = syscall::wait_group_remove(handle::WAIT_GROUP_2, handle::IPC_A);
if let Err(e) = remove_res {
if e != Error::NotFound {
return Err(Error::Internal);
}
} else {
return Err(Error::Internal);
}
syscall::wait_group_remove(handle::WAIT_GROUP_1, handle::IPC_B)?;
syscall::wait_group_remove(handle::WAIT_GROUP_1, handle::IPC_A)?;
test_logger::info!("Object removed when not in any wait group");
let remove_res = syscall::wait_group_remove(handle::WAIT_GROUP_1, handle::IPC_A);
if let Err(e) = remove_res {
if e != Error::NotFound {
return Err(Error::Internal);
}
} else {
return Err(Error::Internal);
}
test_logger::info!("Waiting on empty wait group");
let wait_return = syscall::object_wait(handle::WAIT_GROUP_1, Signals::READABLE, Instant::MAX);
if let Err(e) = wait_return {
if e != Error::InvalidArgument {
return Err(Error::Internal);
}
} else {
return Err(Error::Internal);
}
test_logger::info!("Nested wait groups not supported");
let add_res = syscall::wait_group_add(
handle::WAIT_GROUP_1,
handle::WAIT_GROUP_2,
Signals::READABLE,
3,
);
// No objects in the wait group should return an error.
if let Err(e) = add_res {
if e != Error::InvalidArgument {
return Err(Error::Internal);
}
} else {
return Err(Error::Internal);
}
Ok(())
}
#[entry]
fn entry() -> Result<()> {
wait_group().inspect_err(|e| {
// On error, log that it occurred and, since this is written as a test,
// shut down the system with the error code.
test_logger::error!("WaitGroup service error: {}", *e as u32);
let _ = syscall::debug_shutdown(Err(*e));
})
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}