| // 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() |
| } |
| } |