blob: 01ce62fc8d037895682f86f3969dbb610044b60f [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.
use core::arch::naked_asm;
use pw_status::Result;
use syscall_defs::{
ExitStatus, Signals, SysCallId, SysCallInterface, SysCallReturnValue, WaitReturn,
};
pub struct SysCall {}
macro_rules! syscall_veneer {
($id:ident, $name:ident($($arg_name:ident: $arg_type:ty),* $(,)?)) => {
#[unsafe(naked)]
unsafe extern "C" fn $name($($arg_name: $arg_type),*) -> i64 {
naked_asm!("
li t0, {id}
ecall
ret
",
id = const SysCallId::$id as u32
);
}
};
}
syscall_veneer!(ObjectWait, object_wait(handle: u32, signals: u32, deadline: u64));
syscall_veneer!(WaitGroupAdd, wait_group_add(
wait_group: u32,
object: u32,
signal_mask: Signals,
user_data: usize,
));
syscall_veneer!(WaitGroupRemove, wait_group_remove(
wait_group: u32,
object: u32,
));
syscall_veneer!(ChannelTransact, channel_transact(
object_handle: u32, // a0
send_data: *const u8, // a1
send_len: usize, // a2
recv_data: *mut u8, // a3
recv_len: usize, // a4
deadline: u64, // a6-a7
));
syscall_veneer!(ChannelAsyncTransact, channel_async_transact(
object_handle: u32,
send_data: *const u8,
send_len: usize,
recv_data: *mut u8,
recv_len: usize,
));
syscall_veneer!(ChannelAsyncTransactComplete, channel_async_transact_complete(object_handle: u32));
syscall_veneer!(ChannelAsyncCancel, channel_async_cancel(object_handle: u32));
syscall_veneer!(ChannelRead, channel_read(
object_handle: u32,
offset: usize,
buffer: *mut u8,
buffer_len: usize,
));
syscall_veneer!(ChannelRespond, channel_respond(object_handle: u32, buffer: *const u8, buffer_len: usize));
syscall_veneer!(InterruptAck, interrupt_ack(object_handle: u32, signal_mask: Signals));
syscall_veneer!(RaisePeerUserSignal, object_set_peer_user_signal(object_handle: u32, set: u32));
syscall_veneer!(DebugPutc, putc(a: u32));
syscall_veneer!(DebugShutdown, shutdown(a: u32));
syscall_veneer!(DebugLog, log(buffer: *const u8, buffer_len: usize));
syscall_veneer!(DebugNop, nop());
syscall_veneer!(DebugTriggerInterrupt, debug_trigger_interrupt(irq: u32));
syscall_veneer!(DebugClockNow, debug_clock_now());
syscall_veneer!(ThreadStart, thread_start(handle: u32, initial_pc: usize, initial_sp: usize));
syscall_veneer!(TaskTerminate, task_terminate(handle: u32));
syscall_veneer!(TaskJoin, task_join(handle: u32));
syscall_veneer!(ThreadExit, thread_exit(exit_code: u32));
syscall_veneer!(ProcessStart, process_start(handle: u32));
syscall_veneer!(ProcessExit, process_exit(exit_code: u32));
impl SysCallInterface for SysCall {
#[inline(always)]
fn object_wait(handle: u32, signals: u32, deadline: u64) -> Result<WaitReturn> {
SysCallReturnValue::from(unsafe { object_wait(handle, signals, deadline) }).into()
}
#[inline(always)]
fn wait_group_add(
wait_group: u32,
object: u32,
signal_mask: Signals,
user_data: usize,
) -> Result<()> {
SysCallReturnValue::from(unsafe {
wait_group_add(wait_group, object, signal_mask, user_data)
})
.into()
}
#[inline(always)]
fn wait_group_remove(wait_group: u32, object: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { wait_group_remove(wait_group, object) }).into()
}
#[inline(always)]
unsafe fn channel_transact(
handle: u32,
send_data: *const u8,
send_len: usize,
recv_data: *mut u8,
recv_len: usize,
deadline: u64,
) -> Result<u32> {
SysCallReturnValue::from(unsafe {
channel_transact(handle, send_data, send_len, recv_data, recv_len, deadline)
})
.into()
}
#[inline(always)]
unsafe fn channel_async_transact(
handle: u32,
send_data: *const u8,
send_len: usize,
recv_data: *mut u8,
recv_len: usize,
) -> Result<()> {
SysCallReturnValue::from(unsafe {
channel_async_transact(handle, send_data, send_len, recv_data, recv_len)
})
.into()
}
#[inline(always)]
fn channel_async_transact_complete(handle: u32) -> Result<u32> {
SysCallReturnValue::from(unsafe { channel_async_transact_complete(handle) }).into()
}
#[inline(always)]
fn channel_async_cancel(handle: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { channel_async_cancel(handle) }).into()
}
#[inline(always)]
unsafe fn channel_read(
handle: u32,
offset: usize,
buffer: *mut u8,
buffer_len: usize,
) -> Result<u32> {
SysCallReturnValue::from(unsafe { channel_read(handle, offset, buffer, buffer_len) }).into()
}
#[inline(always)]
unsafe fn channel_respond(handle: u32, buffer: *const u8, buffer_len: usize) -> Result<()> {
SysCallReturnValue::from(unsafe { channel_respond(handle, buffer, buffer_len) }).into()
}
#[inline(always)]
fn interrupt_ack(handle: u32, signal_mask: Signals) -> Result<()> {
SysCallReturnValue::from(unsafe { interrupt_ack(handle, signal_mask) }).into()
}
#[inline(always)]
fn thread_start(handle: u32, initial_pc: usize, initial_sp: usize) -> Result<()> {
SysCallReturnValue::from(unsafe { thread_start(handle, initial_pc, initial_sp) }).into()
}
#[inline(always)]
fn task_terminate(handle: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { task_terminate(handle) }).into()
}
#[inline(always)]
fn task_join(handle: u32) -> Result<ExitStatus> {
let ret = SysCallReturnValue::from(unsafe { task_join(handle) });
// SAFETY: The kernel guarantees that if the syscall succeeds, the return value
// corresponds to either a valid ExitStatus or a valid Error.
unsafe { ret.to_exit_status() }
}
#[inline(always)]
fn thread_exit(exit_code: u32) -> ! {
let _ = unsafe { thread_exit(exit_code) };
// SAFETY: `thread_exit` will never return per the contract of the syscall.
unsafe { core::hint::unreachable_unchecked() }
}
#[inline(always)]
fn process_start(handle: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { process_start(handle) }).into()
}
#[inline(always)]
fn process_exit(exit_code: u32) -> ! {
let _ = unsafe { process_exit(exit_code) };
// SAFETY: `process_exit` will never return per the contract of the syscall.
unsafe { core::hint::unreachable_unchecked() }
}
#[inline(always)]
fn object_set_peer_user_signal(handle: u32, set: bool) -> Result<()> {
SysCallReturnValue::from(unsafe { object_set_peer_user_signal(handle, u32::from(set)) })
.into()
}
#[inline(always)]
fn debug_putc(a: u32) -> Result<u32> {
SysCallReturnValue::from(unsafe { putc(a) }).into()
}
#[inline(always)]
fn debug_shutdown(a: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { shutdown(a) }).into()
}
#[inline(always)]
unsafe fn debug_log(buffer: *const u8, buffer_len: usize) -> Result<()> {
SysCallReturnValue::from(unsafe { log(buffer, buffer_len) }).into()
}
#[inline(always)]
fn debug_nop() -> Result<()> {
SysCallReturnValue::from(unsafe { nop() }).into()
}
#[inline(always)]
fn debug_trigger_interrupt(irq: u32) -> Result<()> {
SysCallReturnValue::from(unsafe { debug_trigger_interrupt(irq) }).into()
}
#[inline(always)]
fn debug_clock_now() -> u64 {
SysCallReturnValue::from(unsafe { debug_clock_now() }).into()
}
}