From 22be0c9c0fd22de045c6d34fc84fdcefa698e7a1 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Fri, 27 Jun 2025 14:44:09 +0930 Subject: [PATCH 01/19] Mature the sockets implementation, fix some lints, and add more docs to items I've touched. --- nx-derive/Cargo.toml | 2 +- nx-derive/src/ipc_traits.rs | 10 +- nx-derive/src/lib.rs | 8 +- src/applet.rs | 19 +- src/console.rs | 37 ++- src/exception.rs | 2 + src/ipc.rs | 2 + src/ipc/sf.rs | 63 +++- src/ipc/sf/bsd.rs | 161 +++++++-- src/ipc/sf/bsd/rc.rs | 3 +- src/la.rs | 19 +- src/lib.rs | 4 +- src/mii.rs | 16 +- src/socket.rs | 634 +++++++++++++++++++++++++++++++----- 14 files changed, 816 insertions(+), 164 deletions(-) diff --git a/nx-derive/Cargo.toml b/nx-derive/Cargo.toml index b035fd0cd..63be3e214 100644 --- a/nx-derive/Cargo.toml +++ b/nx-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nx-derive" version = "0.1.0" -edition = "2021" +edition = "2024" publish = false [lib] diff --git a/nx-derive/src/ipc_traits.rs b/nx-derive/src/ipc_traits.rs index abc6c82f6..7cb20c513 100644 --- a/nx-derive/src/ipc_traits.rs +++ b/nx-derive/src/ipc_traits.rs @@ -22,7 +22,7 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result syn::Result syn::Result pat, @@ -233,8 +232,7 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result>()?; + })?; let mut out_param_names = vec![]; let mut out_param_types = vec![]; diff --git a/nx-derive/src/lib.rs b/nx-derive/src/lib.rs index 73a581d2c..1e8098c4a 100644 --- a/nx-derive/src/lib.rs +++ b/nx-derive/src/lib.rs @@ -13,7 +13,7 @@ pub fn derive_request(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; - return TokenStream::from(quote!( + TokenStream::from(quote!( impl ::nx::ipc::client::RequestCommandParameter for #name { fn before_request_write(_raw: &Self, walker: &mut ::nx::ipc::DataWalker, ctx: &mut ::nx::ipc::CommandContext) -> ::nx::result::Result<()> { walker.advance::(); @@ -31,7 +31,7 @@ pub fn derive_request(input: TokenStream) -> TokenStream { Ok(ctx.raw_data_walker.advance_get()) } } - )); + )) } /// This creates the required trait implementations for the type to be used as an IPC response parameter. @@ -42,7 +42,7 @@ pub fn derive_response(input: TokenStream) -> TokenStream { let name = input.ident; let item_generics = &input.generics; - return TokenStream::from(quote!( + TokenStream::from(quote!( impl #item_generics ::nx::ipc::client::ResponseCommandParameter<#name> for #name { fn after_response_read(walker: &mut ::nx::ipc::DataWalker, ctx: &mut ::nx::ipc::CommandContext) -> ::nx::result::Result { Ok(walker.advance_get()) @@ -60,7 +60,7 @@ pub fn derive_response(input: TokenStream) -> TokenStream { Ok(()) } } - )); + )) } #[proc_macro_attribute] diff --git a/src/applet.rs b/src/applet.rs index a76885600..2e71b6660 100644 --- a/src/applet.rs +++ b/src/applet.rs @@ -1,3 +1,5 @@ +//! AppletAE/AppletOE Support + use crate::hbl::{AppletType, get_applet_type}; use crate::ipc::sf; use crate::result::*; @@ -14,13 +16,22 @@ static ALL_SYSTEM_APPLET_PROXY_SERVICE: RwLock> = RwLock::new(None); static WINDOW_CONTROLLER: RwLock> = RwLock::new(None); + +/// Global AppletResourceUserID. +/// Stored as part of `applet::initialize()` pub static GLOBAL_ARUID: AtomicU64 = AtomicU64::new(0); +/// Proxy type to avoid passing boxed trait objects for Applet Proxy actions. pub enum AppletProxy { + /// AppletType::Application | AppletType::Default Application(ApplicationProxy), + /// AppletType::SystemApplet SystemApplet(SystemAppletProxy), + /// AppletType::LibraryApplet LibraryApplet(LibraryAppletProxy), + /// AppletType::OverlayApplet OverlayApplet(OverlayAppletProxy), + /// AppletType::SystemApplication SystemApplication(SystemApplicationProxy), } @@ -65,7 +76,11 @@ impl ProxyCommon for AppletProxy { } } +/// global AppletAttribute used for openning the applet proxy for the program +/// +/// TODO - make a better way to override this value #[linkage = "weak"] +#[unsafe(export_name = "__nx_applet_attribute")] pub static APPLET_ATTRIBUTE: AppletAttribute = AppletAttribute::zero(); /// Attempts to initialize the module, or returns if the module has already been initialized. @@ -164,14 +179,16 @@ pub(crate) fn finalize() { *app_proxy_service_guard = None; } +/// Gets the registered global Window Controller pub fn get_window_controller<'a>() -> ReadGuard<'a, Option> { WINDOW_CONTROLLER.read() } +/// Gets the registered global AppletProxy pub fn get_applet_proxy<'a>() -> ReadGuard<'a, Option> { LIBRARY_APPLET_PROXY.read() } - +/// Gets the registered global System Proxy Service pub fn get_system_proxy_service<'a>() -> ReadGuard<'a, Option> { ALL_SYSTEM_APPLET_PROXY_SERVICE.read() } diff --git a/src/console.rs b/src/console.rs index a7bbd1a5a..9a343b48c 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,8 +1,10 @@ +//! Console Services + +/// Virtuall TTY functionality #[cfg(feature = "vty")] pub mod vty { use alloc::boxed::Box; - use alloc::vec::Vec; use embedded_graphics_core::prelude::OriginDimensions; use crate::gpu::canvas::{AlphaBlend, CanvasManager, RGBA8, sealed::CanvasColorFormat}; @@ -14,24 +16,23 @@ pub mod vty { pub use embedded_graphics_core::pixelcolor::{Rgb888, RgbColor}; pub use embedded_graphics_core::primitives::rectangle::Rectangle; + /// Canvas/Framebuffer type that keeps a single buffer that is + /// flushed to the display on change. pub struct PersistantBufferedCanvas { buffer: Box<[Rgb888]>, canvas: CanvasManager, } impl PersistantBufferedCanvas { + /// Wraps and existing `CanvasManager` instance + #[inline(always)] pub fn new(canvas: CanvasManager) -> Self { - // new buffer with reserved size - let mut buffer = - Vec::with_capacity((canvas.surface.width() * canvas.surface.height()) as usize); - // fill the buffer with black pixels to start - buffer.resize( - (canvas.surface.width() * canvas.surface.height()) as usize, - Rgb888::new(0, 0, 0), - ); - Self { - buffer: buffer.into_boxed_slice(), + buffer: vec![ + Rgb888::new(0, 0, 0); + (canvas.surface.width() * canvas.surface.height()) as usize + ] + .into_boxed_slice(), canvas, } } @@ -43,6 +44,7 @@ pub mod vty { const COLOR_FORMAT: crate::gpu::ColorFormat = ::COLOR_FORMAT; const PIXEL_FORMAT: crate::gpu::PixelFormat = ::PIXEL_FORMAT; + #[inline(always)] fn blend_with(self, other: Self, blend: AlphaBlend) -> Self { if matches!(blend, AlphaBlend::Destination) { other @@ -51,29 +53,35 @@ pub mod vty { } } + #[inline(always)] fn from_raw(raw: Self::RawType) -> Self { let intermediate = RGBA8::from_bits(raw); Rgb888::new(intermediate.r(), intermediate.g(), intermediate.b()) } + #[inline(always)] fn new() -> Self { Rgb888::new(0, 0, 0) } + #[inline(always)] fn new_scaled(r: u8, g: u8, b: u8, _a: u8) -> Self { Rgb888::new(r, g, b) } + #[inline(always)] fn scale_alpha(self, _alpha: f32) -> Self { self } + #[inline(always)] fn to_raw(self) -> Self::RawType { RGBA8::new_scaled(self.r(), self.g(), self.b(), 255).to_raw() } } impl OriginDimensions for PersistantBufferedCanvas { + #[inline(always)] fn size(&self) -> Size { Size { width: self.canvas.surface.width(), @@ -117,7 +125,12 @@ pub mod vty { for y in y..(y + height as i32) { for x in x..(x + width as i32) { if let Some(color) = color_iter.next() { - self.buffer[(x + y * self.canvas.surface.width() as i32) as usize] = color; + if (0..self.canvas.surface.height().cast_signed()).contains(&y) + && (0..self.canvas.surface.width().cast_signed()).contains(&x) + { + self.buffer[(x + y * self.canvas.surface.width() as i32) as usize] = + color; + } } } } diff --git a/src/exception.rs b/src/exception.rs index 42e7fd830..337bf2532 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,3 +1,5 @@ +//! Exception handling support (stubbed for now). + use crate::diag::abort::{AbortLevel, abort}; use crate::result::ResultCode; use crate::svc; diff --git a/src/ipc.rs b/src/ipc.rs index a02277f69..d277f4463 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,3 +1,5 @@ +//! Contains the machinery for Horizon OS's IPC interface + use crate::result::*; use crate::svc; use crate::thread; diff --git a/src/ipc/sf.rs b/src/ipc/sf.rs index 6191aa9af..767ec986d 100644 --- a/src/ipc/sf.rs +++ b/src/ipc/sf.rs @@ -8,6 +8,7 @@ use alloc::{ }; pub use nx_derive::{Request, Response, ipc_trait}; +use zeroize::Zeroize; pub struct Buffer< 'borrow, @@ -184,10 +185,6 @@ impl< self.count } - pub const fn get_var(&self) -> &T { - unsafe { self.buf.as_ref().unwrap() } - } - pub fn get_mut_var(&mut self) -> &mut T { unsafe { self.buf.as_mut().unwrap() } } @@ -208,14 +205,6 @@ impl< out } - - pub fn as_slice(&self) -> Result<&[T]> { - result_return_unless!( - self.buf.is_aligned() && !self.buf.is_null(), - rc::ResultInvalidBufferPointer - ); - Ok(unsafe { core::slice::from_raw_parts(self.buf, self.count) }) - } } impl< @@ -252,6 +241,47 @@ impl< } } +impl< + 'borrow, + const OUT: bool, + const MAP_ALIAS: bool, + const POINTER: bool, + const FIXED_SIZE: bool, + const AUTO_SELECT: bool, + const ALLOW_NON_SECURE: bool, + const ALLOW_NON_DEVICE: bool, + T +> + Buffer< + 'borrow, + true, + OUT, + MAP_ALIAS, + POINTER, + FIXED_SIZE, + AUTO_SELECT, + ALLOW_NON_SECURE, + ALLOW_NON_DEVICE, + T, + > +{ + pub fn get_var(&self) -> Result<&T> { + unsafe { + self.buf + .as_ref() + .ok_or(rc::ResultInvalidBufferPointer::make()) + } + } + + pub fn as_slice(&self) -> Result<&[T]> { + result_return_unless!( + self.buf.is_aligned() && !self.buf.is_null(), + rc::ResultInvalidBufferPointer + ); + Ok(unsafe { core::slice::from_raw_parts(self.buf, self.count) }) + } +} + impl< 'borrow, const IN: bool, @@ -371,6 +401,8 @@ impl< T, > { + /// If the input buffer is not marked as "IN" then there isn't an API contract that it will be readable/initialized. + /// As such, we should consider all "OUT + !IN" buffers as uninitialized until written by the server function. pub fn as_maybeuninit_mut(&mut self) -> Result<&mut [MaybeUninit]> { result_return_unless!( self.buf.is_aligned() && !self.buf.is_null(), @@ -403,9 +435,8 @@ impl< > { pub fn get_string(&self) -> String { - let cstr = unsafe { core::ffi::CStr::from_ptr(self.buf as _) }; - - String::from_utf8_lossy(cstr.to_bytes()).to_string() + String::from_utf8_lossy(unsafe { core::slice::from_raw_parts_mut(self.buf, self.count) }) + .to_string() } } @@ -434,7 +465,7 @@ impl< pub fn set_string(&mut self, string: String) { unsafe { // First memset to zero so that it will be a valid nul-terminated string - core::ptr::write_bytes(self.buf, 0, self.count); + core::slice::from_raw_parts_mut(self.buf, self.count).zeroize(); core::ptr::copy( string.as_ptr(), self.buf, diff --git a/src/ipc/sf/bsd.rs b/src/ipc/sf/bsd.rs index cffcf4787..d40b65e81 100644 --- a/src/ipc/sf/bsd.rs +++ b/src/ipc/sf/bsd.rs @@ -1,7 +1,6 @@ use nx_derive::{Request, Response}; -use crate::ipc::sf::{ - CopyHandle, InAutoSelectBuffer, InOutAutoSelectBuffer, OutAutoSelectBuffer, OutMapAliasBuffer, +use crate::ipc::sf::{CopyHandle, InAutoSelectBuffer, InOutAutoSelectBuffer, OutAutoSelectBuffer, OutMapAliasBuffer, ProcessId, }; use crate::result::Result; @@ -414,14 +413,79 @@ impl From> for BsdTimeout { } } +/// Shutdown mode for TCP streams #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] pub enum ShutdownMode { + /// Disable reads Receive = 0, + /// Disable writes Send = 1, + /// Disable both directions Bidirectional = 2, } + +/// `get/set_sock_opt` options for level `IpProto::IP` +#[derive(Copy, Clone, Debug, Request, Response)] +#[repr(C)] +pub enum IpOptions { + /// buf/ip_opts; set/get IP options + Options = 1, + /// int; header is included with data + HeaderIncluded = 2, + /// int; IP type of service and preced. + TypeOfService = 3, + /// int; IP time to live + TimeToLive = 4, + /// bool; receive all IP opts w/dgram + RecvOptions = 5, + /// bool; receive IP opts for response + RecvReturnOptions = 6, + /// bool; receive IP dst addr w/dgram + ReceiveDestinationOptions = 7, + /// ip_opts; set/get IP options + ReturnOptions = 8, + /// struct in_addr *or* struct ip_mreqn; + MulticastInterface = 9, + /// u_char; set/get IP multicast ttl + MulticastTimeToLive = 10, + /// u_char; set/get IP multicast loopback + MulticastLoopback = 11, + /// ip_mreq; add an IP group membership + MulticastAddMembership = 12, + /// ip_mreq; drop an IP group membership + MulticastDropMembership = 13, + /// set/get IP mcast virt. iface + MulticastVirtualInterface = 14, + /// enable RSVP in kernel + EnableRSVP = 15, + /// disable RSVP in kernel + DisableRSVP = 16, + /// set RSVP per-vif socket + EnablePerInterfaceRSVP = 17, + /// unset RSVP per-vif socket + DisablePerInterfaceRSVP = 18, + /// int; range to choose for unspec port + PortRange = 19, + /// bool; receive reception if w/dgram + RecvInterface = 20, + /// int; set/get security policy + IpsecPolicy = 21, + /// bool: send all-ones broadcast + AllOnesBroadcast = 23, + /// bool: allow bind to any address + AllowBindAny = 24, + /// bool: allow multiple listeners on a tuple + AllowBindMulti = 25, + /// int; set RSS listen bucket + ResetListenBucket = 26, + /// bool: receive IP dst addr/port w/dgram + OriginalDestinationAddress = 27, +} + + +/// `get/set_sock_opt` options for level `SOL_SOCKET` #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] pub enum SocketOptions { @@ -460,43 +524,80 @@ pub enum SocketOptions { // Other options not generally kept in so_options /// send buffer size - SNDBUF = 0x1001, + SendBufferSize = 0x1001, /// receive buffer size - RCVBUF = 0x1002, + ReceiveBufferSize = 0x1002, /// send low-water mark - SNDLOWAT = 0x1003, + SendLowWaterMark = 0x1003, /// receive low-water mark - RCVLOWAT = 0x1004, + ReceiveLowWaterMark = 0x1004, /// send timeout - SNDTIMEO = 0x1005, + SendTimeout = 0x1005, /// receive timeout - RCVTIMEO = 0x1006, + ReceiveTimeout = 0x1006, /// get error status and clear - ERROR = 0x1007, + Error = 0x1007, /// get socket type - TYPE = 0x1008, + Type = 0x1008, /// socket's MAC label - LABEL = 0x1009, + Label = 0x1009, /// socket's peer's MAC label - PEERLABEL = 0x1010, + PeerLabel = 0x1010, /// socket's backlog limit - LISTENQLIMIT = 0x1011, + ListenQueueLimit = 0x1011, /// socket's complete queue length - LISTENQLEN = 0x1012, + ListenQueueLength = 0x1012, /// socket's incomplete queue length - LISTENINCQLEN = 0x1013, + ListenIncompleteQueueLength = 0x1013, /// use this FIB to route - SETFIB = 0x1014, + SetFIB = 0x1014, /// user cookie (dummynet etc.) - USER_COOKIE = 0x1015, + UserCookie = 0x1015, /// get socket protocol (Linux name) - PROTOCOL = 0x1016, + Protocol = 0x1016, /// clock type used for SO_TIMESTAMP - TS_CLOCK = 0x1017, + TimestampClock = 0x1017, /// socket's max TX pacing rate (Linux name) - MAX_PACING_RATE = 0x1018, + MaxPacingRate = 0x1018, } + +#[derive(Clone, Copy, Debug, Request, Response)] +#[repr(C)] +pub enum TcpOptions { + /// don't delay send to coalesce packets + NoDelay = 1, + /// set maximum segment size + MaxSegmentSize = 2, + /// don't push last block of write + NoPush = 4, + /// don't use TCP options + NoOptions = 8, + /// use MD5 digests (RFC2385) + MD5Signature = 16, + /// retrieve tcp_info structure + Info = 32, + /// get/set congestion control algorithm + Congestion = 64, + /// get/set cc algorithm specific options + CCAlgorithmOptions = 65, + /// N, time to establish connection + KeepInit = 128, + /// L,N,X start keeplives after this period + KeepIdle = 256, + /// L,N interval between keepalives + KeepInterval = 512, + /// L,N number of keepalives before close + KeepCount = 1024, + /// enable TFO / was created via TFO + FastOpen = 1025, + /// number of output packets to keep + PCapOut = 2048, + /// number of input packets to keep + PCapIn = 4096, + /// Set the tcp function pointers to the specified stack + FunctionBlock = 8192, +} #[derive(Copy, Clone, Debug, Default, Request, Response)] #[repr(C)] pub struct Linger { @@ -504,6 +605,13 @@ pub struct Linger { pub linger_time: u32, } +#[derive(Copy, Clone, Debug, Request, Response)] +#[repr(C)] +pub struct IpMulticastRequest{ + pub multicast_addr: Ipv4Addr, + pub interface_addr: Ipv4Addr +} + impl Linger { pub fn as_duration(self) -> Option { if self.on_off == 0 { @@ -608,6 +716,7 @@ define_bit_set! { } } +/// Valid socket domains to request from the bsd service #[derive(Copy, Clone, Debug, Default, Request, Response)] #[repr(u8)] pub enum SocketDomain { @@ -620,6 +729,8 @@ pub enum SocketDomain { //INet6 = 28, - not supported? } + +/// Valid socket types to create from the bsd service. #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] pub enum SocketType { @@ -629,13 +740,15 @@ pub enum SocketType { SequencePacket = 5, } +/// Valid Levels for `get/set_sock_opt` #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] pub enum IpProto { - Ip = 0, + IP = 0, ICMP = 1, TCP = 6, UDP = 17, + Socket = 0xffffffff_u32.cast_signed() as _ } define_bit_set! { @@ -713,9 +826,7 @@ pub trait Bsd { #[ipc_rid(4)] fn open(&self, path_cstr: InAutoSelectBuffer, flags: i32) -> BsdResult<()>; - // incompatible with rust's memory model. - // The reads and writes to the buffers are implemented as a double borrow as mut and shared. - /*#[ipc_rid(5)] + #[ipc_rid(5)] fn select( &self, fd_count: u32, @@ -723,7 +834,7 @@ pub trait Bsd { write_fds: InOutAutoSelectBuffer, except_fds: InOutAutoSelectBuffer, timeout: BsdTimeout, - ) -> BsdResult<()>;*/ + ) -> BsdResult<()>; #[ipc_rid(6)] fn poll(&self, fds: InOutAutoSelectBuffer, timeout: i32) -> BsdResult<()>; diff --git a/src/ipc/sf/bsd/rc.rs b/src/ipc/sf/bsd/rc.rs index 1090b9837..5c8815c5a 100644 --- a/src/ipc/sf/bsd/rc.rs +++ b/src/ipc/sf/bsd/rc.rs @@ -3,5 +3,6 @@ pub const RESULT_MODULE: u32 = 17; result_define_group!(RESULT_MODULE => { NotInitialized: 1, InvalidSocketString: 2, - InvalidSockAddr:3 + InvalidSockAddr:3, + InvalidTimeout: 4 }); diff --git a/src/la.rs b/src/la.rs index 03e445b05..a0aec2037 100755 --- a/src/la.rs +++ b/src/la.rs @@ -144,16 +144,9 @@ pub(crate) fn finalize() { } /// Gets access to the global [`ILibraryAppletCreatorClient`] shared object instance -/// -/// This will fail with [`ResultNotInitialized`][`super::rc::ResultNotInitialized`] if library applet support isn't initialized #[inline] -pub fn get_creator<'a>() -> Result>> { - let guard = G_CREATOR.lock(); - if guard.is_some() { - Ok(guard) - } else { - Err(rc::ResultNotInitialized::make()) - } +pub fn get_creator<'a>() -> MutexGuard<'a, Option> { + G_CREATOR.lock() } /// Wrapper for reading data from a [`IStorageClient`] shared object @@ -201,9 +194,9 @@ pub fn write_storage(storage: &mut Storage, t: T) -> Result<()> { pub fn create_write_storage(t: T) -> Result { result_return_unless!(is_initialized(), super::rc::ResultNotInitialized); - let mut storage = get_creator()? + let mut storage = get_creator() .as_ref() - .unwrap() + .ok_or(rc::ResultNotInitialized::make())? .create_storage(cmem::size_of::())?; write_storage(&mut storage, t)?; @@ -226,9 +219,9 @@ pub fn create_library_applet( ) -> Result { result_return_unless!(is_initialized(), super::rc::ResultNotInitialized); - let accessor = get_creator()? + let accessor = get_creator() .as_ref() - .unwrap() + .ok_or(rc::ResultNotInitialized::make())? .create_library_applet(id, mode)?; let mut holder = LibraryAppletHolder::new(Box::new(accessor))?; diff --git a/src/lib.rs b/src/lib.rs index 3f405701d..a17819f61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,9 +55,11 @@ // for manually pre-checked pointer to reference conversion #![feature(ptr_as_ref_unchecked)] #![feature(pointer_is_aligned_to)] + +//#![warn(missing_docs)] + #![macro_use] use core::arch::global_asm; - // Required assembly bits (those which essentially cannot/shouldn't be inlined) global_asm!(include_str!("asm.s")); diff --git a/src/mii.rs b/src/mii.rs index 9a1a98264..e3ea9e5a7 100644 --- a/src/mii.rs +++ b/src/mii.rs @@ -1,11 +1,14 @@ +//! Mii Support + use crate::result::*; -use crate::sync::Mutex; +use crate::sync::{Mutex, MutexGuard}; use crate::service::mii::*; static G_STATIC_SRV: Mutex> = Mutex::new(None); static G_DB_SRV: Mutex> = Mutex::new(None); +/// Initializes the Mii service objects pub fn initialize() -> Result<()> { let static_service = crate::service::new_service_object::()?; let db_service = static_service.get_database_service(SpecialKeyCode::Normal)?; @@ -14,11 +17,22 @@ pub fn initialize() -> Result<()> { Ok(()) } +/// Gets access to the global [`IStaticClient`] shared object instance +pub fn get_static_service<'a>() -> MutexGuard<'a, Option> { + G_STATIC_SRV.lock() +} + +/// Gets access to the global [`IMiiDatabaseClient`] shared object instance +pub fn get_mii_database<'a>() -> MutexGuard<'a, Option> { + G_DB_SRV.lock() +} + pub(crate) fn finalize() { *G_DB_SRV.lock() = None; *G_STATIC_SRV.lock() = None; } +/// Gets the Mii author ID for the current user. #[inline] pub fn get_device_id() -> Result { use crate::service::set::{ISystemSettingsClient, SystemSettingsService}; diff --git a/src/socket.rs b/src/socket.rs index 9c42f5fed..ce77e4ac7 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1,3 +1,5 @@ +//! Implementation of the Rust libstd TCP/UDP APIs, and re-exports of the raw bsd sockets API. + use core::alloc::Layout; use alloc::alloc::Allocator; @@ -18,6 +20,7 @@ use crate::svc::Handle; use crate::svc::MemoryPermission; use crate::sync::{ReadGuard, RwLock, WriteGuard}; +/// Holder type for the intialized bsd service pub struct BsdSocketService { _tmem_buffer: Buffer, tmem_handle: Handle, @@ -105,6 +108,7 @@ impl Drop for BsdSocketService { static BSD_SERVICE: RwLock> = RwLock::new(None); +/// Initializes the bsd/socket service. pub fn initialize( kind: BsdSrvkind, config: BsdServiceConfig, @@ -133,6 +137,7 @@ pub fn write_socket_service<'a>() -> WriteGuard<'a, Option> { BSD_SERVICE.write() } +/// Implementation of the Rust stdlib TCP/UDP API pub mod net { use core::time::Duration; use core::{mem::offset_of, net::Ipv4Addr}; @@ -150,9 +155,10 @@ pub mod net { service::bsd::{BsdResult, ReadFlags, SendFlags, SocketAddrRepr}, }; - mod sealed { + pub mod traits { use super::*; + /// Trait for making a bsd-fd wrapper type usable in `super::poll` pub trait Pollable { fn get_poll_fd(&self) -> i32; } @@ -163,10 +169,37 @@ pub mod net { } } - pub trait SocketCommon { + /// Contains common functions for bsd-compatible socket-like types + /// + /// # Safety + /// + /// Implementors are responsible for synchonising any interior mutablility for types, if any exists. + pub unsafe trait SocketCommon { + /// gets the raw file descriptor for the type fn as_raw_fd(&self) -> i32; - fn read(&mut self, data: &mut [u8]) -> Result { + /// Opens a connection to a remote host. + fn connect>(destination: A, port: u16) -> Result + where + Self: Sized; + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned object is a references the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// This function is also why objects implementing this trait _should not_ contain any methods requiring mutable references. + /// Consumers should expect that calls to these functions are synchronized by the implementation. + fn try_clone(&self) -> Result + where + Self: Sized; + + /// Reads data from the remote side into the provided buffer. + /// + /// Immediately returns an error if the socket is not connected. + fn recv(&self, data: &mut [u8]) -> Result { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -183,27 +216,19 @@ pub mod net { } } - fn read_non_blocking(&mut self, data: &mut [u8]) -> Result { - let socket_server_handle = BSD_SERVICE.read(); - let socket_server = socket_server_handle.as_ref().unwrap(); - - match socket_server.service.recv(self.as_raw_fd(), ReadFlags::DontWait(), Buffer::from_mut_array(data))? { - BsdResult::Ok(ret, ()) => { - Ok(ret as usize) - }, - BsdResult::Err(11) /* EAGAIN */ => { - Ok(0) - } - BsdResult::Err(errno) => { - ResultCode::new_err(nx::result::pack_value( - rc::RESULT_MODULE, - 1000 + errno.cast_unsigned(), - )) - } - } - } - - fn peek(&mut self, data: &mut [u8]) -> Result { + /// Receives single datagram on the socket from the remote address to which it is connected, without removing the message from input queue. On success, returns the number of bytes peeked. + /// + /// The function must be called with valid byte array buf of sufficient size to hold the message bytes. If a message is too long to fit in the supplied buffer, excess bytes may be discarded. + /// + /// Successive calls return the same data. This is accomplished by passing `MSG_PEEK`` as a flag to the underlying `recv` system call. + /// + /// Do not use this function to implement busy waiting, instead use [`poll`][`nx::socket::net::poll`] to synchronize IO events on one or more sockets. + /// `UdpSocket::connect` will connect this socket to a remote address. This method will fail if the socket is not connected. + /// + /// # Errors + /// + /// This method will fail if the socket is not connected. The connect method will connect this socket to a remote address. + fn peek(&self, data: &mut [u8]) -> Result { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -226,7 +251,7 @@ pub mod net { /// Returns the length of the data written from the buffer /// /// `Self::connect`` will connect this socket to a remote address. This method will fail if the socket is not connected. - fn send(&mut self, data: &[u8]) -> Result { + fn send(&self, data: &[u8]) -> Result { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -243,7 +268,13 @@ pub mod net { } } - fn send_non_blocking(&mut self, data: &[u8]) -> Result<()> { + /// Sends data on the socket to the remote address to which it is connected. + /// For TCP, all data is sent or an error is returned. + /// + /// Returns the length of the data written from the buffer + /// + /// `Self::connect`` will connect this socket to a remote address. This method will fail if the socket is not connected. + fn send_non_blocking(&self, data: &[u8]) -> Result<()> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -266,26 +297,27 @@ pub mod net { ///The function must be called with valid byte array buf of sufficient size to hold the message bytes. If a message is too long to fit in the supplied buffer, excess bytes may be discarded. /// /// `UdpSocket::connect`` will connect this socket to a remote address. This method will fail if the socket is not connected. - fn recv_non_blocking(&mut self, buffer: &mut [u8]) -> Result> { + fn recv_non_blocking(&self, buffer: &mut [u8]) -> Result> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); match socket_server.service.recv(self.as_raw_fd(), ReadFlags::DontWait(), Buffer::from_mut_array(buffer))? { - BsdResult::Ok(ret, ()) => { - Ok(Some(ret as usize)) - }, - BsdResult::Err(11) /* EAGAIN */ => { - Ok(None) - } - BsdResult::Err(errno) => { - ResultCode::new_err(nx::result::pack_value( - rc::RESULT_MODULE, - 1000 + errno.cast_unsigned(), - )) + BsdResult::Ok(ret, ()) => { + Ok(Some(ret as usize)) + }, + BsdResult::Err(11) /* EAGAIN */ => { + Ok(None) + } + BsdResult::Err(errno) => { + ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )) + } } } - } + /// Returns the local address of this socket fn local_addr(&self) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -310,6 +342,7 @@ pub mod net { } } + /// Returns the remote address of this socket (errors for unconnected UDP sockets). fn peer_addr(&self) -> Result { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -333,14 +366,18 @@ pub mod net { } } + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. fn set_ttl(&self, ttl: u32) -> Result<()> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( self.as_raw_fd(), - 0, /* IPPROTO_IP */ - 4, /* IP_TTL */ + IpProto::IP as _, + IpOptions::TimeToLive as _, Buffer::from_other_var(&ttl), )? { return ResultCode::new_err(nx::result::pack_value( @@ -352,6 +389,7 @@ pub mod net { Ok(()) } + /// Gets the value of the `IP_TTL` option for this socket fn ttl(&self) -> Result { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -359,8 +397,8 @@ pub mod net { let mut ttl: u32 = 0; if let BsdResult::Err(errno) = socket_server.service.get_sock_opt( self.as_raw_fd(), - 0, /* IPPROTO_IP */ - 4, /* IP_TTL */ + IpProto::IP as _, + IpOptions::TimeToLive as _, Buffer::from_other_mut_var(&mut ttl), )? { return ResultCode::new_err(nx::result::pack_value( @@ -372,6 +410,14 @@ pub mod net { Ok(ttl) } + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// This will result in `read`, `write`, `recv` and `send` system operations + /// becoming nonblocking, i.e., immediately returning from their calls. + /// If the IO operation is successful, `Ok` is returned and no further + /// action is required. If the IO operation could not be completed and needs + /// to be retried, an error with the value set to `EAGAIN` is + /// returned. fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { const O_NONBLOCK: i32 = 0x4000; @@ -412,7 +458,9 @@ pub mod net { Ok(()) } - fn read_timeout(&self) -> Result> { + /// Returns the read timeout of this socket. + /// If the timeout is [`None`], then [`SocketCommon::recv`] calls will block indefinitely. + fn recv_timeout(&self) -> Result> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -420,7 +468,7 @@ pub mod net { match socket_server.service.get_sock_opt( self.as_raw_fd(), SOL_SOCKET, - SocketOptions::RCVTIMEO as _, + SocketOptions::ReceiveTimeout as _, Buffer::from_other_mut_var(&mut timeout), )? { BsdResult::Ok(_, written_len) => { @@ -441,7 +489,14 @@ pub mod net { } } + /// Sets the read timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`SocketCommon::recv`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. fn set_read_timeout(&self, timeout: Option) -> Result<()> { + result_return_if!(timeout == Some(Duration::ZERO), rc::ResultInvalidTimeout); + let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -449,7 +504,7 @@ pub mod net { if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( self.as_raw_fd(), SOL_SOCKET, - SocketOptions::RCVTIMEO as _, + SocketOptions::ReceiveTimeout as _, Buffer::from_other_var(&timeout), )? { return ResultCode::new_err(nx::result::pack_value( @@ -461,7 +516,10 @@ pub mod net { Ok(()) } - fn write_timeout(&self) -> Result> { + /// Returns the write timeout of this socket. + /// + /// If the timeout is [`None`], then [`SocketCommon::send`] calls will block indefinitely. + fn send_timeout(&self) -> Result> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -469,7 +527,7 @@ pub mod net { match socket_server.service.get_sock_opt( self.as_raw_fd(), SOL_SOCKET, - SocketOptions::SNDTIMEO as _, + SocketOptions::SendTimeout as _, Buffer::from_other_mut_var(&mut timeout), )? { BsdResult::Ok(_, written_len) => { @@ -490,7 +548,14 @@ pub mod net { } } + /// Sets the write timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. fn set_write_timeout(&self, timeout: Option) -> Result<()> { + result_return_if!(timeout == Some(Duration::ZERO), rc::ResultInvalidTimeout); + let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -498,7 +563,7 @@ pub mod net { if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( self.as_raw_fd(), SOL_SOCKET, - SocketOptions::SNDTIMEO as _, + SocketOptions::SendTimeout as _, Buffer::from_other_var(&timeout), )? { return ResultCode::new_err(nx::result::pack_value( @@ -509,14 +574,43 @@ pub mod net { Ok(()) } + + /// Gets the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + fn take_error(&self) -> Result> { + let socket_server_handle = BSD_SERVICE.read(); + + let socket_server = socket_server_handle.as_ref().unwrap(); + + let mut ret_errno: i32 = 0; + if let BsdResult::Err(errno) = socket_server.service.get_sock_opt( + self.as_raw_fd(), + SOL_SOCKET, + SocketOptions::Error as i32, + OutAutoSelectBuffer::from_other_mut_var(&mut ret_errno), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(if ret_errno != 0 { + Some(ret_errno) + } else { + None + }) + } } } - use sealed::{Pollable, SocketCommon}; - + use traits::{Pollable, SocketCommon}; /// Takes a slice of pollable values and requested events returns an iterator over the matched index in the input list and the returned events. #[inline(always)] - pub fn poll( + pub fn poll( pollers: &[(P, PollFlags)], timeout: Option, ) -> Result> { @@ -577,7 +671,7 @@ pub mod net { let listenfd = match socket_server.service.socket( super::SocketDomain::INet, super::SocketType::Stream, - super::IpProto::Ip, + super::IpProto::IP, )? { BsdResult::Ok(ret, ()) => ret, BsdResult::Err(errno) => { @@ -671,7 +765,7 @@ pub mod net { } } - impl sealed::Pollable for TcpListener { + impl traits::Pollable for TcpListener { fn get_poll_fd(&self) -> i32 { self.0 } @@ -680,12 +774,6 @@ pub mod net { pub struct TcpStream(i32); impl TcpStream { - #[inline(always)] - pub fn connect>(destination: A) -> Result { - let destination = destination.into(); - Self::connect_impl(destination) - } - fn connect_impl(destination: SocketAddrRepr) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -696,7 +784,7 @@ pub mod net { let socket = match socket_server.service.socket( super::SocketDomain::INet, super::SocketType::Stream, - super::IpProto::Ip, + super::IpProto::IP, )? { BsdResult::Ok(ret, ()) => ret, BsdResult::Err(errno) => { @@ -740,7 +828,9 @@ pub mod net { Ok(linger.into()) } - + /// Gets the value of the TCP_NODELAY option on this socket. + /// + /// For more information about this option, see `TcpStream::set_nodelay`. pub fn nodelay(&self) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -749,8 +839,8 @@ pub mod net { let mut delay: i32 = 0; match socket_server.service.get_sock_opt( self.0, - 0, /* IPPROTO_IP */ - 1, /* TCP_NODELAY */ + IpProto::IP as _, + TcpOptions::NoDelay as _, Buffer::from_other_mut_var(&mut delay), )? { BsdResult::Ok(_, written_data_len) => { @@ -764,6 +854,47 @@ pub mod net { } } + /// Sets the value of the TCP_NODELAY option on this socket. + /// + /// If set, this option disables the Nagle algorithm. + /// This means that segments are always sent as soon as possible, even + /// if there is only a small amount of data. When not set, data is + /// buffered until there is a sufficient amount to send out, thereby + /// avoiding the frequent sending of small packets. + pub fn set_nodelay(&self, value: bool) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + SOL_SOCKET, + SocketOptions::Broadcast as _, + Buffer::from_other_var(&(value as i32)), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O on the specified + /// portions to return immediately with an appropriate value (see the + /// documentation of [`ShutdownMode`]). + /// + /// # Examples + /// + /// ```no_run + /// use nx::socket::net::{Shutdown, TcpStream}; + /// + /// let stream = TcpStream::connect(Ipv4Addr::LOCALHOST, Some(8080)) + /// .expect("Couldn't connect to the server..."); + /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); + /// ``` pub fn shutdown(&self, mode: ShutdownMode) -> Result<()> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -779,14 +910,102 @@ pub mod net { } } - impl sealed::SocketCommon for TcpStream { + unsafe impl traits::SocketCommon for TcpStream { + #[inline(always)] fn as_raw_fd(&self) -> i32 { self.0 } + + #[inline(always)] + fn connect>(destination: A, port: u16) -> Result { + let destination = (destination.into(), port).into(); + Self::connect_impl(destination) + } + + #[inline(always)] + fn try_clone(&self) -> Result { + Ok(Self(self.0)) + } } + + /// A UDP socket. + /// + /// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be + /// [sent to] and [received from] any other socket address. + /// + /// Although UDP is a connectionless protocol, this implementation provides an interface + /// to set an address where data should be sent and received from. After setting a remote + /// address with [`connect`], data can be sent to and received from that address with + /// [`send`] and [`recv`]. + /// + /// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is + /// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP + /// primitives. + /// + /// [`bind`]: UdpSocket::bind + /// [`connect`]: SocketCommon::connect + /// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 + /// [`recv`]: SocketCommon::recv + /// [received from]: UdpSocket::recv_from + /// [`send`]: SocketCommon::send + /// [sent to]: UdpSocket::send_to + /// [`TcpListener`]: TcpListener + /// [`TcpStream`]: TcpStream + /// + /// # Examples + /// + /// ```no_run + /// use nx::socket::net::UdpSocket; + /// + /// fn main() { + /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST, Some(34254))?; + /// + /// // Receives a single datagram message on the socket. If `buf` is too small to hold + /// // the message, it will be cut off. + /// let mut buf = [0; 10]; + /// let (amt, src_ip, src_port) = socket.recv_from(&mut buf)?; + /// + /// // Redeclare `buf` as slice of the received data and send reverse data back to origin. + /// let buf = &mut buf[..amt]; + /// buf.reverse(); + /// socket.send_to(buf, (src_ip, src_port))?; + /// } + /// ``` pub struct UdpSocket(i32); impl UdpSocket { + /// Creates a UDP socket from the given address. + /// + /// The address type can be any implementor of [`Into`]. + /// + /// # Examples + /// + /// Creates a UDP socket bound to `127.0.0.1:3400`: + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST,Some(3400)).expect("couldn't bind to address"); + /// ``` + /// + /// Creates a UDP socket bound to a port assigned by the operating system + /// at `127.0.0.1`. + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST, None).unwrap(); + /// ``` + /// + /// Note that `bind` declares the scope of your network connection. + /// You can only receive datagrams from and send datagrams to + /// participants in that view of the network. + /// For instance, binding to a loopback address as in the example + /// above will prevent you from sending datagrams to another device + /// in your local network. + /// + /// In order to limit your view of the network the least, `bind` to + /// [`Ipv4Addr::UNSPECIFIED`]. pub fn bind>(addr: A, port: Option) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -821,12 +1040,6 @@ pub mod net { Err(rc::ResultNotInitialized::make()) } - #[inline(always)] - pub fn connect>(destination: A) -> Result { - let destination = destination.into(); - Self::connect_impl(destination) - } - fn connect_impl(destination: SocketAddrRepr) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -865,7 +1078,7 @@ pub mod net { /// On success, returns the number of bytes read and the origin. /// ///The function must be called with valid byte array buf of sufficient size to hold the message bytes. If a message is too long to fit in the supplied buffer, excess bytes may be discarded. - pub fn read_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Ipv4Addr, u16)> { + pub fn recv_from(&self, buffer: &mut [u8]) -> Result<(usize, Ipv4Addr, u16)> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); @@ -888,13 +1101,45 @@ pub mod net { )), } } + /// Receives a single datagram message on the socket, without removing it from the queue. + /// On success, returns the number of bytes read and the origin. + /// + /// The function must be called with valid byte array buf of sufficient size to hold the message bytes. + /// If a message is too long to fit in the supplied buffer, excess bytes may be discarded. + /// + /// Successive calls return the same data. This is accomplished by passing `MSG_PEEK` as a flag to the underlying `recvfrom` system call. + /// + /// Do not use this function to implement busy waiting, instead use [`poll`][`nx::socket::net::poll`] to synchronize IO events on one or more sockets. + pub fn peek_from(&self, buffer: &mut [u8]) -> Result<(usize, Ipv4Addr, u16)> { + let socket_server_handle = BSD_SERVICE.read(); + + let socket_server = socket_server_handle.as_ref().unwrap(); + + let mut out_addr: SocketAddrRepr = Default::default(); + match socket_server.service.recv_from( + self.0, + ReadFlags::Peek(), + Buffer::from_mut_array(buffer), + Buffer::from_mut_var(&mut out_addr), + )? { + BsdResult::Ok(ret, ()) => Ok(( + ret as usize, + Ipv4Addr::from_bits(u32::from_be_bytes(out_addr.addr)), + u16::from_be(out_addr.port), + )), + BsdResult::Err(errno) => ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )), + } + } /// Receives data on the socket from the remote address to which it is connected. /// On success, returns the number of bytes read and the origin or a None value if there is no data. /// ///The function must be called with valid byte array buf of sufficient size to hold the message bytes. If a message is too long to fit in the supplied buffer, excess bytes may be discarded. pub fn recv_from_non_blocking( - &mut self, + &self, buffer: &mut [u8], ) -> Result> { let socket_server_handle = BSD_SERVICE.read(); @@ -921,16 +1166,19 @@ pub mod net { /// Sends data on the socket to the remote address provided (no call to `UdpSocket::connect` is necessary). /// Unlike `std::net::UdpSocket`, this method does not return length of the written data. /// All data is sent or an error is returned. + #[inline(always)] pub fn send_to>( - &mut self, + &self, data: &[u8], - destinaation: A, + destination: A, ) -> Result<()> { + self.send_to_impl(data, destination.into()) + } + + fn send_to_impl(&self, data: &[u8], destination: SocketAddrRepr) -> Result<()> { let socket_server_handle = BSD_SERVICE.read(); let socket_server = socket_server_handle.as_ref().unwrap(); - - let destination = destinaation.into(); match socket_server.service.send_to( self.0, SendFlags::None(), @@ -944,9 +1192,227 @@ pub mod net { )), } } + /// Gets the value of the SO_BROADCAST option for this socket. + /// + /// For more information about this option, see `UdpSocket::set_broadcast``. + pub fn broadcast(&self) -> Result { + let socket_server_handle = BSD_SERVICE.read(); + + let socket_server = socket_server_handle.as_ref().unwrap(); + + let mut broadcast: i32 = 0; + match socket_server.service.get_sock_opt( + self.0, + SOL_SOCKET, + SocketOptions::Broadcast as _, + Buffer::from_other_mut_var(&mut broadcast), + )? { + BsdResult::Ok(_, written_data_len) => { + debug_assert_ne!(written_data_len, 0); // we're reading an i32, but we only care if it's zero or not so any sized write is valid. + Ok(broadcast != 0) + } + BsdResult::Err(errno) => ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )), + } + } + /// Sets the value of the SO_BROADCAST option for this socket. + /// + /// When enabled, this socket is allowed to send packets to a broadcast address. + pub fn set_broadcast(&self, value: bool) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + SOL_SOCKET, + SocketOptions::Broadcast as _, + Buffer::from_other_var(&(value as u8)), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } + /// Gets the value of the IP_MULTICAST_LOOP option for this socket. + /// + ///For more information about this option, see `UdpSocket::set_multicast_loop``. + pub fn multicast_loop(&self) -> Result { + let socket_server_handle = BSD_SERVICE.read(); + + let socket_server = socket_server_handle.as_ref().unwrap(); + + let mut mm_loop: u8 = 0; + match socket_server.service.get_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastLoopback as _, + Buffer::from_other_mut_var(&mut mm_loop), + )? { + BsdResult::Ok(_, written_data_len) => { + debug_assert_ne!(written_data_len, 0); + Ok(mm_loop != 0) + } + BsdResult::Err(errno) => ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )), + } + } + /// Sets the value of the IP_MULTICAST_LOOP option for this socket. + /// + /// If enabled, multicast packets will be looped back to the local socket. + pub fn set_multicast_loop(&self, value: bool) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastLoopback as _, + Buffer::from_other_var(&(value as u8)), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } + /// Gets the value of the IP_MULTICAST_TTL option for this socket. + /// + /// For more information about this option, see `UdpSocket::set_multicast_ttl``. + pub fn multicast_ttl(&self) -> Result { + let socket_server_handle = BSD_SERVICE.read(); + + let socket_server = socket_server_handle.as_ref().unwrap(); + + let mut ttl: u8 = 0; + match socket_server.service.get_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastTimeToLive as _, + Buffer::from_other_mut_var(&mut ttl), + )? { + BsdResult::Ok(_, written_data_len) => { + debug_assert_ne!(written_data_len, 0); + Ok(ttl) + } + BsdResult::Err(errno) => ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )), + } + } + /// Sets the value of the IP_MULTICAST_TTL option for this socket. + /// + ///Indicates the time-to-live value of outgoing multicast packets for this socket. The default value is 1 which means that multicast packets don’t leave the local network unless explicitly requested. + pub fn set_multicast_ttl(&self, ttl: u8) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastTimeToLive as _, + Buffer::from_other_var(&(ttl as u8)), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and interface is the address of the local interface + /// with which the system should join the multicast group. + /// If it’s equal to `Ipv4Addr::UNSPECIFIED` then an appropriate interface is chosen by the system. + pub fn join_multicast_group( + &self, + multicast_addr: Ipv4Addr, + interface_addr: Ipv4Addr, + ) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + let ip_request = IpMulticastRequest { + multicast_addr, + interface_addr, + }; + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastAddMembership as _, + Buffer::from_other_var(&ip_request), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } + /// Executes an operation of the IP_DROP_MEMBERSHIP type. + /// + /// For more information about this option, see `UdpSocket::join_multicast`. + pub fn leave_multicast_group( + &self, + multicast_addr: Ipv4Addr, + interface_addr: Ipv4Addr, + ) -> Result<()> { + let socket_server_handle = BSD_SERVICE.read(); + let socket_server = socket_server_handle.as_ref().unwrap(); + + let ip_request = IpMulticastRequest { + multicast_addr, + interface_addr, + }; + if let BsdResult::Err(errno) = socket_server.service.set_sock_opt( + self.0, + IpProto::IP as _, + IpOptions::MulticastDropMembership as _, + Buffer::from_other_var(&ip_request), + )? { + return ResultCode::new_err(nx::result::pack_value( + rc::RESULT_MODULE, + 1000 + errno.cast_unsigned(), + )); + } + + Ok(()) + } } - impl core::fmt::Write for UdpSocket { + unsafe impl traits::SocketCommon for UdpSocket { + #[inline(always)] + fn as_raw_fd(&self) -> i32 { + self.0 + } + + #[inline(always)] + fn connect>(destination: A, port: u16) -> Result { + let destination = (destination.into(), port).into(); + Self::connect_impl(destination) + } + + #[inline(always)] + fn try_clone(&self) -> Result { + Ok(Self(self.0)) + } + } + + /// Despite the impl requirements, the object is not mutated + impl core::fmt::Write for TcpStream { fn write_str(&mut self, s: &str) -> core::fmt::Result { match self.send(s.as_bytes()) { Ok(_) => Ok(()), @@ -955,11 +1421,13 @@ pub mod net { } } - impl sealed::SocketCommon for UdpSocket { - fn as_raw_fd(&self) -> i32 { - self.0 + /// Despite the impl requirements, the object is not mutated + impl core::fmt::Write for UdpSocket { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + match self.send(s.as_bytes()) { + Ok(_) => Ok(()), + Err(_) => Err(core::fmt::Error), + } } } - - mod sys {} } From a6352cdc228c393da2918ee193370f1921e1ff3f Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sun, 29 Jun 2025 15:06:25 +0930 Subject: [PATCH 02/19] Fixup doc issues --- .github/workflows/static.yml | 2 +- src/arm.rs | 11 +++++------ src/macros/ipc/sf.rs | 3 --- src/macros/util.rs | 12 ++++++------ src/mem/alloc.rs | 6 +----- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index ad465e7d0..44de292c4 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -34,7 +34,7 @@ jobs: - run: rustup update - run: rustup toolchain install nightly - run: rustup component add rust-src - - run: cargo doc --target=aarch64-nintendo-switch-freestanding --all-features + - run: cargo doc --target=aarch64-nintendo-switch-freestanding --all-features --no-deps - run: mkdir docs - run: cp .github/docs/index.html ./docs - run: mv ./target/aarch64-nintendo-switch-freestanding/doc ./docs/doc diff --git a/src/arm.rs b/src/arm.rs index 33035f395..729c1b318 100755 --- a/src/arm.rs +++ b/src/arm.rs @@ -154,18 +154,17 @@ pub struct ThreadContext { pub tpidr: u64, } -/// Flushes memory cache at a certain memory location. +/// Flushes (clean + invalidate) memory cache at a certain memory location. +/// +/// The start and end address are rounded to cache line boundaries read from the `CTR_EL0` register. /// /// # Arguments: /// /// * `address`: Memory address. /// * `size`: Memory size. -/// -/// # Safety -/// -/// `address` must be valid pointer. #[inline(always)] -pub unsafe fn cache_flush(address: *mut u8, size: usize) { +#[deny(clippy::not_unsafe_ptr_arg_deref)] +pub fn cache_flush(address: *mut u8, size: usize) { unsafe extern "C" { fn __nx_arm_cache_flush(address: *mut u8, size: usize); } diff --git a/src/macros/ipc/sf.rs b/src/macros/ipc/sf.rs index 9302e8c24..8fab2df16 100644 --- a/src/macros/ipc/sf.rs +++ b/src/macros/ipc/sf.rs @@ -188,13 +188,10 @@ macro_rules! ipc_sf_define_control_interface_trait { }; } -// TODO: better system than using ipc_sf_object_impl_default_command_metadata!(), enforce command version when invoking it (only on client implementations, etc.), more - #[macro_export] macro_rules! server_mark_request_command_parameters_types_as_copy { ($($t:ty),*) => { $( - //const_assert!($t::is_pod()); impl $crate::ipc::server::RequestCommandParameter<'_,$t> for $t { fn after_request_read(ctx: &mut $crate::ipc::server::ServerContext) -> $crate::result::Result { Ok(ctx.raw_data_walker.advance_get()) diff --git a/src/macros/util.rs b/src/macros/util.rs index d442718b2..0949582fd 100755 --- a/src/macros/util.rs +++ b/src/macros/util.rs @@ -5,7 +5,7 @@ /// # Arguments /// /// * `val`: Expression that should resolve to an unsigned primitive integer type -/// * `alignement`: Alignement value, that should resolve to the same type as `$val` +/// * `alignement`: Alignement value, that should resolve to the same type as `val` /// /// # Examples /// @@ -29,7 +29,7 @@ macro_rules! align_up { /// # Arguments /// /// * `val`: Expression that should resolve to an unsigned primitive integer type -/// * `alignement`: Alignement value, that should resolve to the same type as `$val` +/// * `alignment`: Alignement value, that should resolve to the same type as `val` /// /// # Examples /// @@ -98,17 +98,17 @@ macro_rules! define_bit_set { #[allow(non_snake_case)] impl $name { - /// Creates a `$name` from the underlying base type `$base` + #[doc = concat!("Creates a `", stringify!($name), "` from the underlying base type `", stringify!($base), "`")] pub const fn from(val: $base) -> Self { Self(val) } - /// Checks if the provided `$name` has all of the set bits in `other` are set in `self` + #[doc = concat!("Checks if the provided `", stringify!($name), "` has all of the set bits in `other` are set in `self`")] pub const fn contains(self, other: Self) -> bool { (self.0 & other.0) == other.0 } - /// Checks if the provided `$name` has any common bits with `other` + #[doc = concat!("Checks if the provided ", stringify!($name), " has any common bits with `other`")] pub const fn intersects(self, other: Self) -> bool { (self.0 & other.0) != 0 } @@ -119,7 +119,7 @@ macro_rules! define_bit_set { } $( - /// Returns a `$name` where only the bit for `$entry_name` is set + #[doc = concat!("Returns a `", stringify!($name), "` where only the bit for `", stringify!($entry_name), "` is set")] $(#[$b_meta])* pub const fn $entry_name() -> Self { Self($entry_value) diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index 2d8d508a2..adf019c5b 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -10,17 +10,13 @@ use core::ops::Index; use core::ops::IndexMut; use core::ptr; use core::ptr::NonNull; -extern crate alloc; -use alloc::alloc::Allocator; -use alloc::alloc::Global; -pub use alloc::alloc::Layout; +use ::alloc::alloc::{Global, Allocator, AllocError, Layout}; pub const PAGE_ALIGNMENT: usize = 0x1000; pub mod rc; -use alloc::alloc::AllocError; impl From for ResultCode { fn from(_value: AllocError) -> Self { From fa052a15eb811aae1debf2421ace830d88302cec Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sun, 29 Jun 2025 15:08:12 +0930 Subject: [PATCH 03/19] Get systemtick as nanoseconds since boot. --- src/arm.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arm.rs b/src/arm.rs index 729c1b318..5d18b263d 100755 --- a/src/arm.rs +++ b/src/arm.rs @@ -187,6 +187,12 @@ pub fn get_system_tick() -> u64 { system_tick } +/// Gets the system tick time as nanoseconds. +#[inline(always)] +pub fn get_system_tick_as_nanos() -> u64 { + get_system_tick() / (get_system_tick_frequency() / 1_000_000_000u64) +} + /// Gets the system tick frequency. #[inline(always)] pub fn get_system_tick_frequency() -> u64 { From e51b780be542fe99f87dee6203228c4ff84ff1df Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sun, 29 Jun 2025 15:10:38 +0930 Subject: [PATCH 04/19] remove mem::flush_data_cache in favour of arm::cache_flush --- src/gpu/surface.rs | 4 ++-- src/lib.rs | 4 ++-- src/mem.rs | 44 ++++++++++---------------------------------- src/mem.s | 20 -------------------- 4 files changed, 14 insertions(+), 58 deletions(-) delete mode 100755 src/mem.s diff --git a/src/gpu/surface.rs b/src/gpu/surface.rs index ab3053572..4eef6f101 100755 --- a/src/gpu/surface.rs +++ b/src/gpu/surface.rs @@ -304,7 +304,7 @@ impl Surface { surface.do_ioctl(&mut ioctl_alloc)?; unsafe { - mem::flush_data_cache(surface.buffer_data.ptr, total_framebuffer_size); + nx::arm::cache_flush(surface.buffer_data.ptr, total_framebuffer_size); svc::set_memory_attribute( surface.buffer_data.ptr, total_framebuffer_size, @@ -461,7 +461,7 @@ impl Surface { ..Default::default() }; - mem::flush_data_cache( + nx::arm::cache_flush( unsafe { self.buffer_data .ptr diff --git a/src/lib.rs b/src/lib.rs index a17819f61..838eeaf80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,8 +66,8 @@ global_asm!(include_str!("asm.s")); global_asm!(include_str!("rrt0.s")); global_asm!(include_str!("mod0.s")); global_asm!(include_str!("arm.s")); -global_asm!(include_str!("mem.s")); -global_asm!(include_str!("svc.s")); +//global_asm!(include_str!("mem.s")); +//global_asm!(include_str!("svc.s")); //global_asm!(include_str!("exception.s")); extern crate self as nx; diff --git a/src/mem.rs b/src/mem.rs index 8fcf506c1..833002914 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,32 +1,11 @@ //! Memory (heap) support and utils -extern crate alloc as core_alloc; +use core::usize; + use crate::result::ResultBase; use crate::svc; - pub mod alloc; -/// Flushes data cache at a certain memory region -/// -/// # Arguments -/// -/// * `address`: Memory region address -/// * `size`: Memory region size -/// # Safety -/// -/// Null pointers are OK as we are just doing cache invalidation, not accessing the pointer. -#[inline(always)] -#[allow(clippy::not_unsafe_ptr_arg_deref)] -pub fn flush_data_cache(address: *mut u8, size: usize) { - unsafe extern "C" { - fn __nx_mem_flush_data_cache(address: *mut u8, size: usize); - } - - unsafe { - __nx_mem_flush_data_cache(address, size); - } -} - /// Blocks thread until the memory region specified has the permission passed /// /// # Arguments @@ -41,17 +20,14 @@ pub fn wait_for_permission( permission: svc::MemoryPermission, timeout: Option, ) -> crate::result::Result<()> { - let mut iteration: usize = 0; - loop { - let (memory, _) = svc::query_memory(address)?; - if memory.permission.contains(permission) { - return Ok(()); - } - if timeout.is_some() && timeout <= Some(100_000 * iteration) { - // The timeout has been set and has already expired - return Err(svc::rc::ResultTimedOut::make()); - } - iteration += 1; + let timeout = timeout.unwrap_or(usize::MAX); + let mut time_taken: usize = 0; + + while !svc::query_memory(address)?.0.permission.intersects(permission) { + result_return_if!( time_taken >= timeout, svc::rc::ResultTimedOut); + time_taken = time_taken.saturating_add(100_000); let _ = crate::thread::sleep(100_000); } + + Ok(()) } diff --git a/src/mem.s b/src/mem.s deleted file mode 100755 index f60f774c5..000000000 --- a/src/mem.s +++ /dev/null @@ -1,20 +0,0 @@ -FN_START __nx_mem_flush_data_cache - add x1, x1, x0 - mrs x8, CTR_EL0 - lsr x8, x8, #16 - and x8, x8, #0xf - mov x9, #4 - lsl x9, x9, x8 - sub x10, x9, #1 - bic x8, x0, x10 - mov x10, x1 - -data_cache_flush_l0: - dc civac, x8 - add x8, x8, x9 - cmp x8, x10 - bcc data_cache_flush_l0 - - dsb sy - ret -FN_END From f4242ee3b6015af237f61c0bbbf686f9ae1b5f33 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sun, 29 Jun 2025 15:12:26 +0930 Subject: [PATCH 05/19] export the default heap size so it can be overriden. --- src/mem/alloc.rs | 3 ++- src/rrt0.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index adf019c5b..b19c30d32 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -25,7 +25,8 @@ impl From for ResultCode { } #[linkage = "weak"] -pub static HEAP_SIZE: usize = 0; +#[unsafe(export_name = "__nx_mem_alloc_default_heap_size")] +pub static DEFAULT_HEAP_SIZE: usize = 0; /// Default implementation #[linkage = "weak"] diff --git a/src/rrt0.rs b/src/rrt0.rs index 761b9a898..219f5e0da 100644 --- a/src/rrt0.rs +++ b/src/rrt0.rs @@ -190,7 +190,7 @@ unsafe fn set_main_thread_tlr(handle: svc::Handle) { #[allow(unsafe_op_in_unsafe_fn)] unsafe fn normal_entry(loader_mode: LoaderMode, exit_config: Option) -> ! { let mut main_thread_handle: svc::Handle = 0; - let mut heap = util::PointerAndSize::new(ptr::null_mut(), crate::mem::alloc::HEAP_SIZE); + let mut heap = util::PointerAndSize::new(ptr::null_mut(), crate::mem::alloc::DEFAULT_HEAP_SIZE); let mut hos_version_opt: Option = None; match loader_mode { LoaderMode::Nso(thread_handle) => { From df297801693cb04c15840122f6852d44f527e998 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sun, 29 Jun 2025 16:20:43 +0930 Subject: [PATCH 06/19] Fix lint error for potentially confusing lifetimes. --- src/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input.rs b/src/input.rs index edde2b4de..11357bbe6 100755 --- a/src/input.rs +++ b/src/input.rs @@ -438,7 +438,7 @@ impl Context { /// /// `npad_id`: The [`NpadIdType`][`hid::NpadIdType`] to use #[inline] - pub fn get_player(&self, npad_id: hid::NpadIdType) -> Player { + pub fn get_player(&'_ self, npad_id: hid::NpadIdType) -> Player<'_> { Player::new(npad_id, self.supported_style_tags, &self.shmem) .expect("The pointers provided by the hid service should never be invalid") } From 6c197026f00838d48a38eb6f6659ce5bc37c4d9f Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 30 Jun 2025 12:15:00 +0930 Subject: [PATCH 07/19] Add method to configure default heap size for init function. --- src/macros.rs | 10 ++++++---- src/macros/alloc.rs | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/macros/alloc.rs diff --git a/src/macros.rs b/src/macros.rs index f00247762..a3f80e1ac 100755 --- a/src/macros.rs +++ b/src/macros.rs @@ -2,12 +2,14 @@ //! //! Note that all library macros are defined in this module (and exported) to easily use them all over the library -pub mod result; - -pub mod util; +pub mod diag; pub mod ipc; -pub mod diag; +pub mod alloc; + +pub mod result; pub mod rrt0; + +pub mod util; \ No newline at end of file diff --git a/src/macros/alloc.rs b/src/macros/alloc.rs new file mode 100644 index 000000000..efead0051 --- /dev/null +++ b/src/macros/alloc.rs @@ -0,0 +1,24 @@ +#![macro_use] + +/// Defines the default heap size of the current project +/// +/// Must only be called once in a project +/// +/// # Arguments +/// +/// * `size`: The size passed to the user-configurable heap-initialization function. +/// +/// # Examples +/// +/// ``` +/// alloc_set_default_heap_size!(0x13F00); +/// ``` +#[macro_export] +macro_rules! alloc_set_default_heap_size { + ($val:expr) => { + #[unsafe(no_mangle)] + #[used] + #[unsafe(export_name = "__nx_mem_alloc_default_heap_size")] + pub static DEFAULT_HEAP_SIZE: usize = $val; + }; +} From e5bd299e08f62451712e9ff421c69d0a9714dabd Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 30 Jun 2025 12:19:25 +0930 Subject: [PATCH 08/19] remove the need for arm.s --- src/arm.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/arm.s | 29 --------------------------- 2 files changed, 56 insertions(+), 31 deletions(-) delete mode 100755 src/arm.s diff --git a/src/arm.rs b/src/arm.rs index 5d18b263d..1b703deb8 100755 --- a/src/arm.rs +++ b/src/arm.rs @@ -165,8 +165,34 @@ pub struct ThreadContext { #[inline(always)] #[deny(clippy::not_unsafe_ptr_arg_deref)] pub fn cache_flush(address: *mut u8, size: usize) { - unsafe extern "C" { - fn __nx_arm_cache_flush(address: *mut u8, size: usize); + // Equivalent to `cache_flush2` commented out below, but ends up being better hand-written + // than compiler optimised. + #[unsafe(naked)] + unsafe extern "C" fn __nx_arm_cache_flush(address: *mut u8, size: usize) { + core::arch::naked_asm!( + crate::macros::util::maybe_cfi!(".cfi_startproc"), + "add x1, x1, x0", + "mrs x8, CTR_EL0", + "lsr x8, x8, #16", + "and x8, x8, #0xf", + "mov x9, #4", + "lsl x9, x9, x8", + "sub x10, x9, #1", + "bic x8, x0, x10", + "mov x10, x1", + "mov w1, #1", + "mrs x0, tpidrro_el0", + "strb w1, [x0, #0x104] ",// Set flag at TLR[0x104] for kernel + "2:", + "dc civac, x8", + "add x8, x8, x9", + "cmp x8, x10", + "bcc 2b", + "dsb sy", + "strb wzr, [x0, #0x104]", // Unset flag at TLR[0x104] for kernel + "ret", + crate::macros::util::maybe_cfi!(".cfi_endproc") + ); } unsafe { @@ -174,6 +200,34 @@ pub fn cache_flush(address: *mut u8, size: usize) { } } +/* +pub fn cache_flush2(address: *mut u8, size: usize) { + let address = address.expose_provenance(); + let mut ctr_el0: u64; + unsafe { + asm!("mrs {}, CTR_EL0", out(reg) ctr_el0); + } + + let cache_line_size = 4usize << (ctr_el0 as usize >> 16 & 0xF); + let cache_line_mask = !(cache_line_size - 1); + let last_address = address.saturating_add(size) & cache_line_mask; + let mut address = address & cache_line_mask; + + unsafe { + let tlr = nx::thread::get_thread_local_region(); + (*tlr).cache_maintenance_flag = true; + while address <= last_address { + asm!("dc civac, {}", in(reg) address); + address = address.saturating_add(cache_line_size); + } + + asm!("dsb sy"); + + (*tlr).cache_maintenance_flag = false; + } +} +*/ + /// Gets the system tick. #[inline(always)] pub fn get_system_tick() -> u64 { diff --git a/src/arm.s b/src/arm.s deleted file mode 100755 index 62877d8ec..000000000 --- a/src/arm.s +++ /dev/null @@ -1,29 +0,0 @@ -FN_START __nx_arm_cache_flush - add x1, x1, x0 - mrs x8, CTR_EL0 - lsr x8, x8, #16 - and x8, x8, #0xf - mov x9, #4 - lsl x9, x9, x8 - sub x10, x9, #1 - bic x8, x0, x10 - mov x10, x1 - - // Set flag at TLR[0x104] for kernel - mov w1, #1 - mrs x0, tpidrro_el0 - strb w1, [x0, #0x104] - -armDCacheFlush_L0: - dc civac, x8 - add x8, x8, x9 - cmp x8, x10 - bcc armDCacheFlush_L0 - - dsb sy - - // Unset flag at TLR[0x104] for kernel - strb wzr, [x0, #0x104] - - ret -FN_END From 2f49b1d08cb3f84af13a145fb26bf60df1c222ca Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 30 Jun 2025 13:14:59 +0930 Subject: [PATCH 09/19] More big changes that need testing. --- src/asm.s | 12 - src/diag/log.rs | 34 +- src/diag/log/svc.rs | 2 +- src/gpu.rs | 6 +- src/gpu/canvas.rs | 8 +- src/gpu/surface.rs | 7 +- src/ipc/server.rs | 4 +- src/lib.rs | 6 +- src/macros/util.rs | 37 +- src/rrt0.rs | 2 +- src/service.rs | 6 +- src/service/sm.rs | 6 +- src/svc.rs | 782 ++++++++++++----------- src/svc.s | 787 ----------------------- src/svc/asm.rs | 1463 +++++++++++++++++++++++++++++++++++++++++++ src/thread.rs | 19 +- src/wait.rs | 2 +- 17 files changed, 1921 insertions(+), 1262 deletions(-) delete mode 100755 src/asm.s delete mode 100755 src/svc.s create mode 100644 src/svc/asm.rs diff --git a/src/asm.s b/src/asm.s deleted file mode 100755 index a42286e95..000000000 --- a/src/asm.s +++ /dev/null @@ -1,12 +0,0 @@ -.macro FN_START name - .section .text.\name, "ax", %progbits - .global \name - .type \name, %function - .align 2 - .cfi_startproc -\name: -.endm - -.macro FN_END - .cfi_endproc -.endm diff --git a/src/diag/log.rs b/src/diag/log.rs index 211422157..a728ba319 100755 --- a/src/diag/log.rs +++ b/src/diag/log.rs @@ -1,7 +1,7 @@ //! Logging support and utils use crate::thread; -use alloc::string::String; +use alloc::{ffi::CString, string::String}; /// Represents the logging severity pub type LogSeverity = logpacket::detail::LogSeverity; @@ -77,7 +77,7 @@ pub fn log_with(metadata: &LogMetadata) { logger.log(metadata); } -fn format_plain_string_log_impl(metadata: &LogMetadata, log_type: &str) -> String { +fn format_plain_string_log_impl(metadata: &LogMetadata, log_type: &str) -> CString { let severity_str = match metadata.severity { LogSeverity::Trace => "Trace", LogSeverity::Info => "Info", @@ -93,17 +93,25 @@ fn format_plain_string_log_impl(metadata: &LogMetadata, log_type: &str) -> Strin .get_str() .unwrap_or("") }; - format!( - "[ {} (severity: {}, verbosity: {}) from {} in thread {}, at {}:{} ] {}", - log_type, - severity_str, - metadata.verbosity, - metadata.fn_name, - thread_name, - metadata.file_name, - metadata.line_number, - metadata.msg - ) + + // SAFETY - This is find as we are only writing ascii string generated in this function, an the thread_name with is read through + // a conversion from a CStr, and a log message that has been escaped of null bytes. + unsafe { + CString::from_vec_unchecked( + format!( + "[ {} (severity: {}, verbosity: {}) from {} in thread {}, at {}:{} ] {}\0", + log_type, + severity_str, + metadata.verbosity, + metadata.fn_name, + thread_name, + metadata.file_name, + metadata.line_number, + metadata.msg.replace('\0', "\\0") + ) + .into_bytes(), + ) + } } pub mod svc; diff --git a/src/diag/log/svc.rs b/src/diag/log/svc.rs index a20788a49..8daae08f3 100755 --- a/src/diag/log/svc.rs +++ b/src/diag/log/svc.rs @@ -14,6 +14,6 @@ impl Logger for SvcOutputLogger { #[allow(unused_must_use)] fn log(&mut self, metadata: &LogMetadata) { let msg = format_plain_string_log_impl(metadata, "SvcOutputLog"); - unsafe { svc::output_debug_string(msg.as_ptr(), msg.len()) }; + unsafe { svc::output_debug_string(msg.as_c_str()) }; } } diff --git a/src/gpu.rs b/src/gpu.rs index f8b2605fe..54db5a7bd 100755 --- a/src/gpu.rs +++ b/src/gpu.rs @@ -850,9 +850,9 @@ impl BlockLinearHeights { } } -const NVHOST_AS_GPU_PATH: &str = nul!("/dev/nvhost-as-gpu"); -const NVMAP_PATH: &str = nul!("/dev/nvmap"); -const NVHOST_CTRL_PATH: &str = nul!("/dev/nvhost-ctrl"); +const NVHOST_AS_GPU_PATH: &str = "/dev/nvhost-as-gpu\0"; +const NVMAP_PATH: &str = "/dev/nvmap\0"; +const NVHOST_CTRL_PATH: &str = "/dev/nvhost-ctrl\0"; /// Represents the screen width pub const SCREEN_WIDTH: u32 = 1280; diff --git a/src/gpu/canvas.rs b/src/gpu/canvas.rs index a92b77c92..7de8fbc7a 100644 --- a/src/gpu/canvas.rs +++ b/src/gpu/canvas.rs @@ -719,9 +719,7 @@ impl Canvas for BufferedCanvas<'_, ColorFormat> impl Drop for BufferedCanvas<'_, ColorFormat> { fn drop(&mut self) { self.convert_buffers(); - unsafe { - arm::cache_flush(self.base_pointer as *mut u8, self.buffer_size); - } + arm::cache_flush(self.base_pointer as *mut u8, self.buffer_size); let _ = self.manager.surface.queue_buffer(self.slot, self.fences); let _ = self.manager.surface.wait_buffer_event(-1); } @@ -833,9 +831,7 @@ impl Canvas for UnbufferedCanvas<'_, Col impl Drop for UnbufferedCanvas<'_, ColorFormat> { fn drop(&mut self) { - unsafe { - arm::cache_flush(self.base_pointer as *mut u8, self.buffer_size); - } + arm::cache_flush(self.base_pointer as *mut u8, self.buffer_size); let _ = self.manager.surface.queue_buffer(self.slot, self.fences); let _ = self.manager.surface.wait_buffer_event(-1); } diff --git a/src/gpu/surface.rs b/src/gpu/surface.rs index 4eef6f101..3eac2f789 100755 --- a/src/gpu/surface.rs +++ b/src/gpu/surface.rs @@ -8,7 +8,6 @@ use super::*; use crate::gpu::binder; use crate::gpu::ioctl; use crate::ipc::sf; -use crate::mem; use crate::mem::alloc; use crate::service::dispdrv; use crate::svc; @@ -308,8 +307,7 @@ impl Surface { svc::set_memory_attribute( surface.buffer_data.ptr, total_framebuffer_size, - 8, - svc::MemoryAttribute::Uncached(), + true )?; } @@ -593,8 +591,7 @@ impl Drop for Surface { svc::set_memory_attribute( self.buffer_data.ptr, self.buffer_data.layout.size(), - 8, - svc::MemoryAttribute::None(), + false ) }; diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 5efe5f87c..a504f73a9 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -662,7 +662,7 @@ impl ISessionObject for MitmQueryService { } pub trait INamedPort: IServerObject { - fn get_port_name() -> &'static str; + fn get_port_name() -> &'static core::ffi::CStr; fn get_max_sesssions() -> i32; } @@ -1125,7 +1125,7 @@ impl ServerManager

{ pub fn register_named_port_server(&mut self) -> Result<()> { let port_handle = - unsafe { svc::manage_named_port(S::get_port_name().as_ptr(), S::get_max_sesssions())? }; + unsafe { svc::manage_named_port(S::get_port_name(), S::get_max_sesssions())? }; self.register_server::(port_handle, sm::ServiceName::empty()); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 838eeaf80..8dff5c713 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,14 +60,10 @@ #![macro_use] use core::arch::global_asm; -// Required assembly bits (those which essentially cannot/shouldn't be inlined) -global_asm!(include_str!("asm.s")); +// Required assembly bits (those which essentially cannot/shouldn't be inlined) global_asm!(include_str!("rrt0.s")); global_asm!(include_str!("mod0.s")); -global_asm!(include_str!("arm.s")); -//global_asm!(include_str!("mem.s")); -//global_asm!(include_str!("svc.s")); //global_asm!(include_str!("exception.s")); extern crate self as nx; diff --git a/src/macros/util.rs b/src/macros/util.rs index 0949582fd..e0a85cc90 100755 --- a/src/macros/util.rs +++ b/src/macros/util.rs @@ -238,24 +238,6 @@ macro_rules! read_bits { }; } -/// Creates a NUL-terminated string literal -/// -/// # Arguments -/// -/// * `lit`: The string literal -/// -/// # Examples -/// -/// ``` -/// assert_eq!("demo\0", nul!("demo")); -/// ``` -#[macro_export] -macro_rules! nul { - ($lit:literal) => { - concat!($lit, "\0") - }; -} - /// Gets the current function name /// /// # Examples @@ -279,3 +261,22 @@ macro_rules! cur_fn_name { &name[..name.len() - DUMMY_FN_EXTRA_SIZE] }}; } + + +// CFI directives cannot be used if neither debuginfo nor panic=unwind is enabled. +// We don't have an easy way to check the former, so just check based on panic strategy. +#[cfg(panic = "abort")] +macro_rules! maybe_cfi { + ($x: literal) => { + "" + }; +} + +#[cfg(panic = "unwind")] +macro_rules! maybe_cfi { + ($x: literal) => { + $x + }; +} + +pub(crate) use maybe_cfi; \ No newline at end of file diff --git a/src/rrt0.rs b/src/rrt0.rs index 219f5e0da..9ad91a821 100644 --- a/src/rrt0.rs +++ b/src/rrt0.rs @@ -338,8 +338,8 @@ enum LoaderMode { Nso(u32), Nro(*const AbiConfigEntry), } + #[unsafe(no_mangle)] -#[linkage = "weak"] #[allow(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn __nx_rrt0_entry(arg0: usize, arg1: usize) -> ! { // Since we're using the `b` instruction instead of `bl` in `rrt0.s`, the `lr` register will still have the passed in value. diff --git a/src/service.rs b/src/service.rs index 310a96f80..07e72a892 100755 --- a/src/service.rs +++ b/src/service.rs @@ -1,5 +1,7 @@ //! Base service/named port support and wrappers +use core::ffi::CStr; + use sm::IUserInterfaceClient; use crate::ipc::client; @@ -14,7 +16,7 @@ pub mod sm; /// Interfaces which wrap named ports (see [`manage_named_port`][`svc::manage_named_port`] or [`connect_to_named_port`][`svc::connect_to_named_port`]) must implement this trait pub trait INamedPort: client::IClientObject { /// Gets the name to be used to connect to the named port (via [`connect_to_named_port`][`svc::connect_to_named_port`]) - fn get_name() -> &'static str; + fn get_name() -> &'static CStr; /// This will get executed after connecting to the named port in [`new_named_port_object`], allowing for extra initialization /// /// Some interfaces may have initialization commands (check [SM's case][`sm::UserInterface::register_client`]) which can be automatically called this way @@ -41,7 +43,7 @@ pub trait IService: client::IClientObject { /// /// For more information about this, check [`INamedPort`] pub fn new_named_port_object() -> Result { - let handle = unsafe { svc::connect_to_named_port(T::get_name().as_ptr()) }?; + let handle = unsafe { svc::connect_to_named_port(T::get_name()) }?; let mut object = T::new(sf::Session::from_handle(handle)); object.post_initialize()?; Ok(object) diff --git a/src/service/sm.rs b/src/service/sm.rs index d47b9b881..177a88e47 100755 --- a/src/service/sm.rs +++ b/src/service/sm.rs @@ -1,3 +1,5 @@ +use core::ffi::CStr; + use crate::ipc; use crate::ipc::sf; use crate::result::*; @@ -7,8 +9,8 @@ use crate::version; pub use crate::ipc::sf::sm::*; impl service::INamedPort for UserInterface { - fn get_name() -> &'static str { - nul!("sm:") + fn get_name() -> &'static CStr { + c"sm:" } fn post_initialize(&mut self) -> Result<()> { diff --git a/src/svc.rs b/src/svc.rs index 4cd6949c5..930977d78 100755 --- a/src/svc.rs +++ b/src/svc.rs @@ -6,11 +6,10 @@ use crate::arm; use crate::ipc::sf::ncm; use crate::result::*; use crate::util; -use crate::version; use core::mem; -use core::mem::ManuallyDrop; use core::ptr; +pub mod asm; pub mod rc; #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -212,6 +211,7 @@ pub enum ExceptionType { pub type PageInfo = u32; pub type Address = *const u8; +pub type MutAddress = *mut u8; pub type Size = usize; pub type ThreadEntrypointFn = unsafe extern "C" fn(*mut u8) -> !; pub type Handle = u32; @@ -226,7 +226,7 @@ impl ScopedHandle { // Take the value out without running the destructor and closing the handle, consuming the guard pub unsafe fn take(guard: Self) -> Handle { - ManuallyDrop::new(guard).0 + mem::ManuallyDrop::new(guard).0 } } @@ -239,6 +239,46 @@ impl Drop for ScopedHandle { } } +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +#[repr(C)] +/// Context of a scheduled thread. +pub struct LastThreadContext { + /// Frame Pointer for the thread. + fp: u64, + /// Stack Pointer for the thread. + sp: u64, + /// Link Register for the thread. + lr: u64, + /// Program Counter for the thread. + pc: u64, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +/// Limitable Resources. +pub enum LimitableResource { + /// How much memory can a process map. + Memory = 0, + /// How many threads can a process spawn. + Threads = 1, + /// How many events can a process have. + Events = 2, + /// How many transfer memories can a process make. + TransferMemories = 3, + /// How many sessions can a process own. + Sessions = 4, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +/// Thread Activity. +pub enum ThreadActivity { + /// Thread can run. + Runnable = 0, + /// Thread is paused. + Paused = 1, +} + pub const INVALID_HANDLE: Handle = 0; pub const CURRENT_THREAD_PSEUDO_HANDLE: Handle = 0xFFFF8000; @@ -246,115 +286,120 @@ pub const CURRENT_PROCESS_PSEUDO_HANDLE: Handle = 0xFFFF8001; pub const DEFAULT_PROCESS_PROCESSOR_ID: i32 = -2; +/// Set the process heap to a given size. It can both extend and shrink the heap. #[inline(always)] -pub fn set_heap_size(size: Size) -> Result<*mut u8> { - unsafe extern "C" { - fn __nx_svc_set_heap_size(out_address: *mut *mut u8, size: Size) -> ResultCode; - } - +pub fn set_heap_size(size: Size) -> Result { unsafe { - let mut address: *mut u8 = ptr::null_mut(); + let mut address: MutAddress = ptr::null_mut(); - let rc = __nx_svc_set_heap_size(&mut address, size); + let rc = asm::set_heap_size(&mut address, size); pack(rc, address) } } +/// Set the memory permissions of a (page-aligned) range of memory. +/// +/// `MemoryPermission::Execute()` and `MemoryPermission::Execute()` are not allowed. +/// This can be used to move back and forth between `MemoryPermission::None()`, `MemoryPermission::Read()` +/// and `MemoryPermission::Read() | MemoryPermission::Write()`. #[inline(always)] -pub unsafe fn set_memory_attribute( +pub unsafe fn set_memory_permission( address: Address, size: Size, - mask: u32, - value: MemoryAttribute, + value: MemoryPermission, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_set_memory_attribute( - address: Address, - size: Size, - mask: u32, - value: MemoryAttribute, - ) -> ResultCode; + unsafe { + let rc = asm::set_memory_permission(address, size, value); + pack(rc, ()) } +} +/// Set the memory attributes of a (page-aligned) range of memory. +/// +/// Only setting or unsetting the `Uncached` flag (bit 3) is supported, +/// so the function signature has been changed from libnx to enforce this constraint. +/// +/// # Safety +/// +/// The provided address must be valid, and should be page aligned (0x1000). +#[inline(always)] +pub unsafe fn set_memory_attribute(address: Address, size: Size, set_uncached: bool) -> Result<()> { unsafe { - let rc = __nx_svc_set_memory_attribute(address, size, mask, value); + let rc = asm::set_memory_attribute( + address, + size, + 8, + if set_uncached { + MemoryAttribute::Uncached() + } else { + MemoryAttribute::None() + }, + ); pack(rc, ()) } } +/// Maps a memory range into a different range. Mainly used for adding guard pages around stack. +/// +/// Source range gets reprotected to [`MemoryAttribute::None()`] (it can no longer be accessed), +/// and [`MemoryAttribute::Borrowed()`] is set in the source page's [`MemoryAttribute`]. #[inline(always)] -pub unsafe fn set_memory_permission( +pub unsafe fn map_memory( address: Address, + source_address: MutAddress, size: Size, - value: MemoryPermission, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_set_memory_permission( - address: Address, - size: Size, - value: MemoryPermission, - ) -> ResultCode; + unsafe { + let rc = asm::map_memory(address, source_address, size); + pack(rc, ()) } +} +/// Unmaps a region that was previously mapped with [`map_memory`] +#[inline(always)] +pub unsafe fn unmap_memory( + address: Address, + source_address: MutAddress, + size: Size, +) -> Result<()> { unsafe { - let rc = __nx_svc_set_memory_permission(address, size, value); + let rc = asm::unmap_memory(address, source_address, size); pack(rc, ()) } } +/// Query information about an address. Will always fetch the lowest page-aligned mapping that contains the provided address. +/// +/// # SAFETY: null pointers are OK here, as we are just querying memory properties #[inline(always)] -// SAFETY: null pointers are OK here, as we are just querying memory properties #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn query_memory(address: Address) -> Result<(MemoryInfo, PageInfo)> { - unsafe extern "C" { - fn __nx_svc_query_memory( - out_info: *mut MemoryInfo, - out_page_info: *mut PageInfo, - address: Address, - ) -> ResultCode; - } - unsafe { let mut memory_info: MemoryInfo = Default::default(); let mut page_info: PageInfo = 0; - let rc = __nx_svc_query_memory(&mut memory_info, &mut page_info, address); + let rc = asm::query_memory(&mut memory_info, &mut page_info, address); pack(rc, (memory_info, page_info)) } } #[inline(always)] pub fn exit_process() -> ! { - unsafe extern "C" { - fn __nx_svc_exit_process() -> !; - } - - unsafe { __nx_svc_exit_process() } + unsafe { asm::exit_process() } } #[inline(always)] pub unsafe fn create_thread( entry: ThreadEntrypointFn, - entry_arg: Address, - stack_top: Address, + entry_arg: MutAddress, + stack_top: MutAddress, priority: i32, processor_id: i32, ) -> Result { - unsafe extern "C" { - fn __nx_svc_create_thread( - handle: *mut Handle, - entry: ThreadEntrypointFn, - entry_arg: Address, - stack_top: Address, - priority: i32, - processor_id: i32, - ) -> ResultCode; - } - unsafe { let mut handle: Handle = 0; - let rc = __nx_svc_create_thread( + let rc = asm::create_thread( &mut handle, entry, entry_arg, @@ -368,80 +413,78 @@ pub unsafe fn create_thread( #[inline(always)] pub fn start_thread(handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_start_thread(handle: Handle) -> ResultCode; - } - unsafe { - let rc = __nx_svc_start_thread(handle); + let rc = asm::start_thread(handle); pack(rc, ()) } } #[inline(always)] pub fn exit_thread() -> ! { - unsafe extern "C" { - fn __nx_svc_exit_thread() -> !; - } - - unsafe { __nx_svc_exit_thread() } + unsafe { asm::exit_thread() } } #[inline(always)] pub fn sleep_thread(timeout: i64) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_sleep_thread(timeout: i64) -> ResultCode; - } - unsafe { - let rc = __nx_svc_sleep_thread(timeout); + let rc = asm::sleep_thread(timeout); pack(rc, ()) } } #[inline(always)] pub fn get_thread_priority(handle: Handle) -> Result { - unsafe extern "C" { - fn __nx_svc_get_thread_priority(out_priority: *mut i32, handle: Handle) -> ResultCode; - } - unsafe { let mut priority: i32 = 0; - let rc = __nx_svc_get_thread_priority(&mut priority, handle); + let rc = asm::get_thread_priority(&mut priority, handle); pack(rc, priority) } } #[inline(always)] pub fn set_thread_priority(handle: Handle, priority: i32) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_set_thread_priority(handle: Handle, priority: i32) -> ResultCode; + unsafe { + let rc = asm::set_thread_priority(handle, priority); + pack(rc, ()) } +} +#[inline(always)] +pub fn get_thread_core_mask(handle: Handle) -> Result<(i32, u64)> { unsafe { - let rc = __nx_svc_set_thread_priority(handle, priority); - pack(rc, ()) + let mut mask = 0; + let mut affinity = 0; + let rc = asm::get_thread_core_mask(&mut mask, &mut affinity, handle); + pack(rc, (mask, affinity)) } } #[inline(always)] -pub fn get_current_processor_number() -> u32 { - unsafe extern "C" { - fn __nx_svc_get_current_processor_number() -> u32; +pub fn set_thread_core_mask(handle: Handle, preferred_core: i32, affinity_mask: u32) -> Result<()> { + unsafe { + let rc = asm::set_thread_core_mask(handle, preferred_core, affinity_mask); + pack(rc, ()) } +} - unsafe { __nx_svc_get_current_processor_number() } +#[inline(always)] +pub fn get_current_processor_number() -> u32 { + unsafe { asm::get_current_processor_number() } } #[inline(always)] pub fn signal_event(handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_signal_event(handle: Handle) -> ResultCode; + unsafe { + let rc = asm::signal_event(handle); + pack(rc, ()) } +} +#[inline(always)] +pub fn clear_event(handle: Handle) -> Result<()> { unsafe { - let rc = __nx_svc_signal_event(handle); + let rc = asm::clear_event(handle); pack(rc, ()) } } @@ -449,33 +492,20 @@ pub fn signal_event(handle: Handle) -> Result<()> { #[inline(always)] pub unsafe fn map_shared_memory( handle: Handle, - address: Address, + address: MutAddress, size: Size, permission: MemoryPermission, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_map_shared_memory( - handle: Handle, - address: Address, - size: Size, - permission: MemoryPermission, - ) -> ResultCode; - } - unsafe { - let rc = __nx_svc_map_shared_memory(handle, address, size, permission); + let rc = asm::map_shared_memory(handle, address, size, permission); pack(rc, ()) } } #[inline(always)] pub unsafe fn unmap_shared_memory(handle: Handle, address: Address, size: Size) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_unmap_shared_memory(handle: Handle, address: Address, size: Size) - -> ResultCode; - } unsafe { - let rc = __nx_svc_unmap_shared_memory(handle, address, size); + let rc = asm::unmap_shared_memory(handle, address, size); pack(rc, ()) } } @@ -483,97 +513,72 @@ pub unsafe fn unmap_shared_memory(handle: Handle, address: Address, size: Size) #[inline(always)] #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn create_transfer_memory( - address: Address, + address: MutAddress, size: Size, permissions: MemoryPermission, ) -> Result { - unsafe extern "C" { - fn __nx_svc_create_transfer_memory( - out_handle: *mut Handle, - address: Address, - size: Size, - permissions: MemoryPermission, - ) -> ResultCode; - } unsafe { let mut handle: Handle = 0; - let rc = __nx_svc_create_transfer_memory(&mut handle, address, size, permissions); + let rc = asm::create_transfer_memory(&mut handle, address, size, permissions); pack(rc, handle) } } #[inline(always)] pub fn close_handle(handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_close_handle(handle: Handle) -> ResultCode; - } - unsafe { - let rc = __nx_svc_close_handle(handle); + let rc = asm::close_handle(handle); pack(rc, ()) } } #[inline(always)] pub fn reset_signal(handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_reset_signal(handle: Handle) -> ResultCode; - } - unsafe { - let rc = __nx_svc_reset_signal(handle); + let rc = asm::reset_signal(handle); pack(rc, ()) } } #[inline(always)] pub unsafe fn wait_synchronization( - handles: *const Handle, - handle_count: u32, + handles: &[Handle], timeout: i64, ) -> Result { - unsafe extern "C" { - fn __nx_svc_wait_synchronization( - out_index: *mut i32, - handles: *const Handle, - handle_count: u32, - timeout: i64, - ) -> ResultCode; - } - unsafe { let mut index: i32 = 0; - let rc = __nx_svc_wait_synchronization(&mut index, handles, handle_count, timeout); + let rc = asm::wait_synchronization(&mut index, handles.as_ptr(), handles.len() as u32, timeout); pack(rc, index) } } #[inline(always)] -pub unsafe fn arbitrate_lock(thread_handle: Handle, tag_location: Address, tag: u32) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_arbitrate_lock( - thread_handle: Handle, - tag_location: Address, - tag: u32, - ) -> ResultCode; - } +pub fn wait_synchronization_one(handle: Handle, timeout: i64) -> Result<()> { + unsafe { wait_synchronization(&[handle], timeout).map(|_| ()) } +} +#[inline(always)] +pub fn cancel_synchronisation(handle: Handle) -> Result<()> { unsafe { - let rc = __nx_svc_arbitrate_lock(thread_handle, tag_location, tag); + let rc = asm::cancel_synchronisation(handle); pack(rc, ()) } } #[inline(always)] -pub unsafe fn arbitrate_unlock(tag_location: Address) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_arbitrate_unlock(tag_location: Address) -> ResultCode; +pub unsafe fn arbitrate_lock(thread_handle: Handle, tag_location: Address, tag: u32) -> Result<()> { + unsafe { + let rc = asm::arbitrate_lock(thread_handle, tag_location, tag); + pack(rc, ()) } +} +#[inline(always)] +pub unsafe fn arbitrate_unlock(tag_location: Address) -> Result<()> { unsafe { - let rc = __nx_svc_arbitrate_unlock(tag_location); + let rc = asm::arbitrate_unlock(tag_location); pack(rc, ()) } } @@ -585,151 +590,229 @@ pub unsafe fn wait_process_wide_key_atomic( desired_tag: u32, timeout: i64, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_wait_process_wide_key_atomic( - wait_location: Address, - tag_location: Address, - desired_tag: u32, - timeout: i64, - ) -> ResultCode; - } - unsafe { - let rc = __nx_svc_wait_process_wide_key_atomic( - wait_location, - tag_location, - desired_tag, - timeout, - ); + let rc = + asm::wait_process_wide_key_atomic(wait_location, tag_location, desired_tag, timeout); pack(rc, ()) } } #[inline(always)] pub unsafe fn signal_process_wide_key(tag_location: Address, desired_tag: i32) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_signal_process_wide_key(tag_location: Address, desired_tag: i32) -> ResultCode; - } - unsafe { - let rc = __nx_svc_signal_process_wide_key(tag_location, desired_tag); + let rc = asm::signal_process_wide_key(tag_location, desired_tag); pack(rc, ()) } } #[inline(always)] pub fn get_system_tick() -> u64 { - unsafe extern "C" { - fn __nx_svc_get_system_tick() -> u64; - } - - unsafe { __nx_svc_get_system_tick() } + unsafe { asm::get_system_tick() } } #[inline(always)] -pub unsafe fn connect_to_named_port(name: Address) -> Result { - unsafe extern "C" { - fn __nx_svc_connect_to_named_port(out_handle: *mut Handle, name: Address) -> ResultCode; - } +pub unsafe fn connect_to_named_port(name: &core::ffi::CStr) -> Result { unsafe { let mut handle: Handle = 0; - let rc = __nx_svc_connect_to_named_port(&mut handle, name); + let rc = asm::connect_to_named_port(&mut handle, name.as_ptr()); pack(rc, handle) } } +#[inline(always)] +pub fn send_sync_request_light(handle: Handle) -> Result<()> { + unsafe { + let rc = asm::send_sync_request_light(handle); + pack(rc, ()) + } +} + #[inline(always)] pub fn send_sync_request(handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_send_sync_request(handle: Handle) -> ResultCode; + unsafe { + let rc = asm::send_sync_request(handle); + pack(rc, ()) } +} +#[inline(always)] +pub unsafe fn send_sync_request_with_user_data( + buffer: &mut [u8], + handle: Handle, +) -> Result<()> { unsafe { - let rc = __nx_svc_send_sync_request(handle); + let rc = asm::send_sync_request_with_user_data(buffer.as_mut_ptr(), buffer.len(), handle); pack(rc, ()) } } #[inline(always)] -pub fn get_process_id(process_handle: Handle) -> Result { - unsafe extern "C" { - fn __nx_svc_get_process_id(out_process_id: *mut u64, process_handle: Handle) -> ResultCode; +pub unsafe fn send_async_request_with_user_data( + buffer: &mut [u8], + session: Handle, +) -> Result { + unsafe { + let mut out_handle = 0; + let rc = asm::send_async_request_with_user_data(&mut out_handle, buffer.as_mut_ptr(), buffer.len(), session); + pack(rc, out_handle) } +} +#[inline(always)] +pub fn get_process_id(process_handle: Handle) -> Result { unsafe { let mut process_id: u64 = 0; - let rc = __nx_svc_get_process_id(&mut process_id, process_handle); + let rc = asm::get_process_id(&mut process_id, process_handle); pack(rc, process_id) } } #[inline(always)] pub fn get_thread_id(handle: Handle) -> Result { - unsafe extern "C" { - fn __nx_svc_get_thread_id(out_thread_id: *mut u64, handle: Handle) -> ResultCode; - } - unsafe { let mut thread_id: u64 = 0; - let rc = __nx_svc_get_thread_id(&mut thread_id, handle); + let rc = asm::get_thread_id(&mut thread_id, handle); pack(rc, thread_id) } } #[inline(always)] pub unsafe fn r#break(reason: BreakReason, arg: Address, size: Size) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_break(reason: BreakReason, arg: Address, size: Size) -> ResultCode; + unsafe { + let rc = asm::r#break(reason, arg, size); + pack(rc, ()) } +} +#[inline(always)] +pub unsafe fn output_debug_string(msg: &core::ffi::CStr) -> Result<()> { unsafe { - let rc = __nx_svc_break(reason, arg, size); + let rc = asm::output_debug_string(msg.as_ptr(), msg.count_bytes()); pack(rc, ()) } } #[inline(always)] -pub unsafe fn output_debug_string(msg: Address, len: Size) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_output_debug_string(msg: Address, len: Size) -> ResultCode; +pub fn return_from_exception(res: ResultCode) -> ! { + unsafe { + asm::return_from_exception(res); + } +} + +#[inline(always)] +pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { + unsafe { + let mut info: u64 = 0; + + let rc = asm::get_info(&mut info, id, handle, sub_id); + pack(rc, info) } +} +#[inline(always)] +pub unsafe fn flush_entire_data_cache() -> Result<()> { unsafe { - let rc = __nx_svc_output_debug_string(msg, len); + let rc = asm::flush_entire_data_cache(); pack(rc, ()) } } #[inline(always)] -pub fn return_from_exception(res: ResultCode) -> ! { - unsafe extern "C" { - fn __nx_svc_return_from_exception(res: ResultCode) -> !; +pub unsafe fn flush_data_cache(address: Address, len: Size) -> Result<()> { + unsafe { + let rc = asm::flush_data_cache(address, len); + pack(rc, ()) } +} +#[inline(always)] +pub unsafe fn map_physical_memory(address: Address, len: Size) -> Result<()> { unsafe { - __nx_svc_return_from_exception(res); + let rc = asm::map_physical_memory(address, len); + pack(rc, ()) } } #[inline(always)] -pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { - unsafe extern "C" { - fn __nx_svc_get_info( - out_info: *mut u64, - id: InfoId, - handle: Handle, - sub_id: u64, - ) -> ResultCode; +pub unsafe fn unmap_physical_memory(address: Address, len: Size) -> Result<()> { + unsafe { + let rc = asm::unmap_physical_memory(address, len); + pack(rc, ()) } +} +#[inline(always)] +pub fn get_debug_future_thread_info( + debug_proc_handle: Handle, + ns: i64, +) -> Result<(LastThreadContext, u64)> { unsafe { - let mut info: u64 = 0; + let mut out_context = mem::zeroed(); + let mut out_thread_id = 0; + let rc = asm::get_debug_future_thread_info( + &mut out_context, + &mut out_thread_id, + debug_proc_handle, + ns, + ); + pack(rc, (out_context, out_thread_id)) + } +} - let rc = __nx_svc_get_info(&mut info, id, handle, sub_id); - pack(rc, info) +#[inline(always)] +pub fn get_last_thread_info() -> Result<(LastThreadContext, u64, u32)> { + unsafe { + let mut out_context = mem::zeroed(); + let mut out_tls_address = 0; + let mut out_flags = 0; + let rc = asm::get_last_thread_info(&mut out_context, &mut out_tls_address, &mut out_flags); + pack(rc, (out_context, out_tls_address, out_flags)) + } +} + +#[inline(always)] +pub fn get_resource_limit_limit_value( + resource_limit_handle: Handle, + limit_kind: LimitableResource, +) -> Result { + unsafe { + let mut out_val = 0; + let rc = + asm::get_resource_limit_limit_value(&mut out_val, resource_limit_handle, limit_kind); + pack(rc, out_val) + } +} + +#[inline(always)] +pub fn get_resource_limit_current_value( + resource_limit_handle: Handle, + limit_kind: LimitableResource, +) -> Result { + unsafe { + let mut out_val = 0; + let rc = + asm::get_resource_limit_current_value(&mut out_val, resource_limit_handle, limit_kind); + pack(rc, out_val) + } +} + +#[inline(always)] +pub fn set_thread_activity(thread_handle: Handle, thread_state: ThreadActivity) -> Result<()> { + unsafe { + let rc = asm::set_thread_activity(thread_handle, thread_state); + pack(rc, ()) + } +} + +#[inline(always)] +pub fn get_thread_context3(thread_handle: Handle) -> Result<()> { + unsafe { + let mut out_context = Default::default(); + let rc = asm::get_thread_context3(&mut out_context, thread_handle); + pack(rc, ()) } } @@ -740,17 +823,8 @@ pub unsafe fn wait_for_address( value: u32, timeout: i64, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_wait_for_address( - address: Address, - arbitration_type: u32, - value: u32, - timeout: i64, - ) -> ResultCode; - } - unsafe { - let rc = __nx_svc_wait_for_address(address, arbitration_type as u32, value, timeout); + let rc = asm::wait_for_address(address, arbitration_type as u32, value, timeout); pack(rc, ()) } } @@ -762,56 +836,49 @@ pub unsafe fn signal_to_address( value: u32, thread_signal_count: i32, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_signal_to_address( - address: Address, - signal: u32, - value: u32, - signal_count: i32, - ) -> ResultCode; + unsafe { + let rc = asm::signal_to_address(address, signal as u32, value, thread_signal_count); + pack(rc, ()) } +} +#[inline(always)] +pub unsafe fn synchronize_preemption_state() -> Result<()> { unsafe { - let rc = __nx_svc_signal_to_address(address, signal as u32, value, thread_signal_count); + let rc = asm::synchronize_preemption_states(); pack(rc, ()) } } #[inline(always)] pub fn create_session(is_light: bool, unk_name: u64) -> Result<(Handle, Handle)> { - unsafe extern "C" { - fn __nx_svc_create_session( - out_server_handle: *mut Handle, - out_client_handle: *mut Handle, - is_light: bool, - unk_name: u64, - ) -> ResultCode; - } - unsafe { let mut server_handle: Handle = 0; let mut client_handle: Handle = 0; - let rc = - __nx_svc_create_session(&mut server_handle, &mut client_handle, is_light, unk_name); + let rc = asm::create_session(&mut server_handle, &mut client_handle, is_light, unk_name); pack(rc, (server_handle, client_handle)) } } #[inline(always)] pub fn accept_session(handle: Handle) -> Result { - unsafe extern "C" { - fn __nx_svc_accept_session(out_session_handle: *mut Handle, handle: Handle) -> ResultCode; - } - unsafe { let mut session_handle: Handle = 0; - let rc = __nx_svc_accept_session(&mut session_handle, handle); + let rc = asm::accept_session(&mut session_handle, handle); pack(rc, session_handle) } } +#[inline(always)] +pub fn reply_and_receive_light(handle: Handle) -> Result<()> { + unsafe { + let rc = asm::reply_and_receive_light(handle); + pack(rc, ()) + } +} + #[inline(always)] pub unsafe fn reply_and_receive( handles: *const Handle, @@ -819,132 +886,103 @@ pub unsafe fn reply_and_receive( reply_target: Handle, timeout: i64, ) -> Result { - unsafe extern "C" { - fn __nx_svc_reply_and_receive( - out_index: *mut i32, - handles: *const Handle, - handle_count: u32, - reply_target: Handle, - timeout: i64, - ) -> ResultCode; + unsafe { + let mut index: i32 = 0; + + let rc = asm::reply_and_receive(&mut index, handles, handle_count, reply_target, timeout); + pack(rc, index) } +} +#[inline(always)] +pub unsafe fn reply_and_receive_with_user_buffer( + user_buffer: &mut [u8], + handles: &[Handle], + reply_target: Handle, + timeout: i64, +) -> Result { unsafe { let mut index: i32 = 0; - let rc = - __nx_svc_reply_and_receive(&mut index, handles, handle_count, reply_target, timeout); + let rc = asm::reply_and_receive_with_user_buffer( + &mut index, + user_buffer.as_mut_ptr(), + user_buffer.len(), + handles.as_ptr(), + handles.len() as u32, + reply_target, + timeout, + ); pack(rc, index) } } #[inline(always)] pub fn create_event() -> Result<(Handle, Handle)> { - unsafe extern "C" { - fn __nx_svc_create_event( - out_server_handle: *mut Handle, - out_client_handle: *mut Handle, - ) -> ResultCode; - } - unsafe { let mut server_handle: Handle = 0; let mut client_handle: Handle = 0; - let rc = __nx_svc_create_event(&mut server_handle, &mut client_handle); + let rc = asm::create_event(&mut server_handle, &mut client_handle); pack(rc, (server_handle, client_handle)) } } #[inline(always)] pub fn debug_active_process(process_id: u64) -> Result { - unsafe extern "C" { - fn __nx_svc_debug_active_process(out_handle: *mut Handle, process_id: u64) -> ResultCode; - } - unsafe { let mut handle: Handle = 0; - let rc = __nx_svc_debug_active_process(&mut handle, process_id); + let rc = asm::debug_active_process(&mut handle, process_id); pack(rc, handle) } } #[inline(always)] pub fn break_debug_process(debug_handle: Handle) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_break_debug_process(debug_handle: Handle) -> ResultCode; + unsafe { + let rc = asm::break_debug_process(debug_handle); + pack(rc, ()) } +} +#[inline(always)] +pub fn terminate_debug_process(debug_handle: Handle) -> Result<()> { unsafe { - let rc = __nx_svc_break_debug_process(debug_handle); + let rc = asm::terminate_debug_process(debug_handle); pack(rc, ()) } } #[inline(always)] pub fn get_debug_event(debug_handle: Handle) -> Result { - unsafe extern "C" { - fn __nx_svc_get_debug_event( - out_debug_event: *mut DebugEvent, - debug_handle: Handle, - ) -> ResultCode; - } - unsafe { let mut debug_event: DebugEvent = mem::zeroed(); - let rc = __nx_svc_get_debug_event(&mut debug_event, debug_handle); + let rc = asm::get_debug_event(&mut debug_event, debug_handle); pack(rc, debug_event) } } #[inline(always)] pub fn continue_debug_event(debug_handle: Handle, flags: u32, thread_ids: &[u64]) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_legacy_continue_debug_event( - debug_handle: Handle, - flags: u32, - thread_id: u64, - ) -> ResultCode; - fn __nx_svc_continue_debug_event( - debug_handle: Handle, - flags: u32, - thread_ids: *const u64, - thread_id_count: u32, - ) -> ResultCode; - } - - unsafe { - if version::get_version() < version::Version::new(3, 0, 0) { - let rc = __nx_svc_legacy_continue_debug_event(debug_handle, flags, thread_ids[0]); - pack(rc, ()) - } else { - let rc = __nx_svc_continue_debug_event( - debug_handle, - flags, - thread_ids.as_ptr(), - thread_ids.len() as u32, - ); - pack(rc, ()) - } + unsafe { + let rc = asm::continue_debug_event( + debug_handle, + flags, + thread_ids.as_ptr(), + thread_ids.len() as u32, + ); + pack(rc, ()) } } #[inline(always)] pub fn get_process_list(process_list: &mut [u64]) -> Result { - unsafe extern "C" { - fn __nx_svc_get_process_list( - out_count: *mut u32, - out_process_ids: *mut u64, - process_id_count: u32, - ) -> ResultCode; - } - unsafe { let mut count: u32 = 0; - let rc = __nx_svc_get_process_list( + let rc = asm::get_process_list( &mut count, process_list.as_mut_ptr(), process_list.len() as u32, @@ -955,19 +993,10 @@ pub fn get_process_list(process_list: &mut [u64]) -> Result { #[inline(always)] pub fn get_thread_list(debug_handle: Handle, thread_id_list: &mut [u64]) -> Result { - unsafe extern "C" { - fn __nx_svc_get_thread_list( - out_count: *mut u32, - out_thread_ids: *mut u64, - thread_id_count: u32, - debug_handle: Handle, - ) -> ResultCode; - } - unsafe { let mut count: u32 = 0; - let rc = __nx_svc_get_thread_list( + let rc = asm::get_thread_list( &mut count, thread_id_list.as_mut_ptr(), thread_id_list.len() as u32, @@ -983,20 +1012,11 @@ pub fn get_debug_thread_context( thread_id: u64, register_group: arm::RegisterGroup, ) -> Result { - unsafe extern "C" { - fn __nx_svc_get_debug_thread_context( - thread_context: *mut u8, // *mut arm::ThreadContext - debug_handle: Handle, - thread_id: u64, - register_group: u32, - ) -> ResultCode; - } - unsafe { let mut thread_context: arm::ThreadContext = Default::default(); - let rc = __nx_svc_get_debug_thread_context( - &raw mut thread_context as *mut _, + let rc = asm::get_debug_thread_context( + (&raw mut thread_context).cast(), debug_handle, thread_id, register_group.get(), @@ -1012,17 +1032,8 @@ pub fn set_debug_thread_context( thread_id: u64, register_group: arm::RegisterGroup, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_set_debug_thread_context( - debug_handle: Handle, - thread_id: u64, - thread_context: *const u8, // *const arm::ThreadContext - register_group: u32, - ) -> ResultCode; - } - unsafe { - let rc = __nx_svc_set_debug_thread_context( + let rc = asm::set_debug_thread_context( debug_handle, thread_id, &raw const thread_context as *const _, @@ -1041,20 +1052,11 @@ pub fn query_debug_process_memory( debug_handle: Handle, address: Address, ) -> Result<(MemoryInfo, PageInfo)> { - unsafe extern "C" { - fn __nx_svc_query_debug_process_memory( - out_info: *mut MemoryInfo, - out_page_info: *mut PageInfo, - debug_handle: Handle, - address: Address, - ) -> ResultCode; - } - unsafe { let mut memory_info: MemoryInfo = Default::default(); let mut page_info: PageInfo = 0; - let rc = __nx_svc_query_debug_process_memory( + let rc = asm::query_debug_process_memory( &mut memory_info, &mut page_info, debug_handle, @@ -1069,19 +1071,10 @@ pub unsafe fn read_debug_process_memory( debug_handle: Handle, read_address: usize, read_size: usize, - buffer: *mut u8, + buffer: MutAddress, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_read_debug_process_memory( - buffer: *mut u8, - debug_handle: Handle, - address: usize, - size: usize, - ) -> ResultCode; - } - unsafe { - let rc = __nx_svc_read_debug_process_memory(buffer, debug_handle, read_address, read_size); + let rc = asm::read_debug_process_memory(buffer, debug_handle, read_address, read_size); pack(rc, ()) } } @@ -1093,48 +1086,47 @@ pub unsafe fn write_debug_process_memory( write_size: usize, buffer: *const u8, ) -> Result<()> { - unsafe extern "C" { - fn __nx_svc_write_debug_process_memory( - debug_handle: Handle, - buffer: Address, - address: usize, - size: usize, - ) -> ResultCode; - } - unsafe { - let rc = - __nx_svc_write_debug_process_memory(debug_handle, buffer, write_address, write_size); + let rc = asm::write_debug_process_memory(debug_handle, buffer, write_address, write_size); pack(rc, ()) } } #[inline(always)] -pub unsafe fn manage_named_port(name: Address, max_sessions: i32) -> Result { - unsafe extern "C" { - fn __nx_svc_manage_named_port( - out_handle: *mut Handle, - name: Address, - max_sessions: i32, - ) -> ResultCode; +pub unsafe fn create_named_port( + name: &core::ffi::CStr, + max_sessions: i32, + is_light: bool, +) -> Result<(Handle, Handle)> { + unsafe { + let mut server_handle = 0; + let mut client_handle = 0; + + let rc = asm::create_named_port( + &mut server_handle, + &mut client_handle, + max_sessions, + is_light, + name.as_ptr().cast(), + ); + pack(rc, (server_handle, client_handle)) } +} +#[inline(always)] +pub unsafe fn manage_named_port(name: &core::ffi::CStr, max_sessions: i32) -> Result { unsafe { let mut handle: Handle = 0; - let rc = __nx_svc_manage_named_port(&mut handle, name, max_sessions); + let rc = asm::manage_named_port(&mut handle, name.as_ptr().cast(), max_sessions); pack(rc, handle) } } #[inline(always)] pub fn call_secure_monitor(mut inout: [u64; 8]) -> [u64; 8] { - unsafe extern "C" { - fn __nx_svc_call_secure_monitor(args: *mut u64); - } - unsafe { - __nx_svc_call_secure_monitor(inout.as_mut_ptr()); + asm::call_secure_monitor(inout.as_mut_ptr()); } inout } diff --git a/src/svc.s b/src/svc.s deleted file mode 100755 index dd2fc27ee..000000000 --- a/src/svc.s +++ /dev/null @@ -1,787 +0,0 @@ -FN_START __nx_svc_set_heap_size - str x0, [sp, #-16]! - svc 0x1 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_set_memory_permission - svc 0x2 - ret -FN_END - -FN_START __nx_svc_set_memory_attribute - svc 0x3 - ret -FN_END - -FN_START __nx_svc_map_memory - svc 0x4 - ret -FN_END - -FN_START __nx_svc_unmap_memory - svc 0x5 - ret -FN_END - -FN_START __nx_svc_query_memory - str x1, [sp, #-16]! - svc 0x6 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_exit_process - svc 0x7 - ret -FN_END - -FN_START __nx_svc_create_thread - str x0, [sp, #-16]! - svc 0x8 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_start_thread - svc 0x9 - ret -FN_END - -FN_START __nx_svc_exit_thread - svc 0xA - ret -FN_END - -FN_START __nx_svc_sleep_thread - svc 0xB - ret -FN_END - -FN_START __nx_svc_get_thread_priority - str x0, [sp, #-16]! - svc 0xC - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_set_thread_priority - svc 0xD - ret -FN_END - -FN_START __nx_svc_get_thread_core_mask - stp x0, x1, [sp, #-16]! - svc 0xE - ldp x3, x4, [sp], #16 - str w1, [x3] - str x2, [x4] - ret -FN_END - -FN_START __nx_svc_set_thread_core_mask - svc 0xF - ret -FN_END - -FN_START __nx_svc_get_current_processor_number - svc 0x10 - ret -FN_END - -FN_START __nx_svc_signal_event - svc 0x11 - ret -FN_END - -FN_START __nx_svc_clear_event - svc 0x12 - ret -FN_END - -FN_START __nx_svc_map_shared_memory - svc 0x13 - ret -FN_END - -FN_START __nx_svc_unmap_shared_memory - svc 0x14 - ret -FN_END - -FN_START __nx_svc_create_transfer_memory - str x0, [sp, #-16]! - svc 0x15 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_close_handle - svc 0x16 - ret -FN_END - -FN_START __nx_svc_reset_signal - svc 0x17 - ret -FN_END - -FN_START __nx_svc_wait_synchronization - str x0, [sp, #-16]! - svc 0x18 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_cancel_synchronization - svc 0x19 - ret -FN_END - -FN_START __nx_svc_arbitrate_lock - svc 0x1A - ret -FN_END - -FN_START __nx_svc_arbitrate_unlock - svc 0x1B - ret -FN_END - -FN_START __nx_svc_wait_process_wide_key_atomic - svc 0x1C - ret -FN_END - -FN_START __nx_svc_signal_process_wide_key - svc 0x1D - ret -FN_END - -FN_START __nx_svc_get_system_tick - svc 0x1E - ret -FN_END - -FN_START __nx_svc_connect_to_named_port - str x0, [sp, #-16]! - svc 0x1F - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_send_sync_request_light - svc 0x20 - ret -FN_END - -FN_START __nx_svc_send_sync_request - svc 0x21 - ret -FN_END - -FN_START __nx_svc_send_sync_request_with_user_buffer - svc 0x22 - ret -FN_END - -FN_START __nx_svc_send_async_request_with_user_buffer - str x0, [sp, #-16]! - svc 0x23 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_get_process_id - str x0, [sp, #-16]! - svc 0x24 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_get_thread_id - str x0, [sp, #-16]! - svc 0x25 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_break - svc 0x26 - ret -FN_END - -FN_START __nx_svc_output_debug_string - svc 0x27 - ret -FN_END - -FN_START __nx_svc_return_from_exception - svc 0x28 - ret -FN_END - -FN_START __nx_svc_get_info - str x0, [sp, #-16]! - svc 0x29 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_flush_entire_data_cache - svc 0x2A - ret -FN_END - -FN_START __nx_svc_flush_data_cache - svc 0x2B - ret -FN_END - -FN_START __nx_svc_map_physical_memory - svc 0x2C - ret -FN_END - -FN_START __nx_svc_unmap_physical_memory - svc 0x2D - ret -FN_END - -FN_START __nx_svc_get_debug_future_thread_info - stp x0, x1, [sp, #-16]! - svc 0x2E - ldp x6, x7, [sp], #16 - stp x1, x2, [x6] - stp x3, x4, [x6, #16] - str x5, [x7] - ret -FN_END - -FN_START __nx_svc_get_last_thread_info - stp x1, x2, [sp, #-16]! - str x0, [sp, #-16]! - svc 0x2F - ldr x7, [sp], #16 - stp x1, x2, [x7] - stp x3, x4, [x7, #16] - ldp x1, x2, [sp], #16 - str x5, [x1] - str w6, [x2] - ret -FN_END - -FN_START __nx_svc_get_resource_limit_limit_value - str x0, [sp, #-16]! - svc 0x30 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_get_resource_limit_current_value - str x0, [sp, #-16]! - svc 0x31 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_set_thread_activity - svc 0x32 - ret -FN_END - -FN_START __nx_svc_get_thread_context3 - svc 0x33 - ret -FN_END - -FN_START __nx_svc_wait_for_address - svc 0x34 - ret -FN_END - -FN_START __nx_svc_signal_to_address - svc 0x35 - ret -FN_END - -FN_START __nx_svc_synchronize_preemption_state - svc 0x36 - ret -FN_END - -FN_START __nx_svc_get_resource_limit_peak_value - str x0, [sp, #-16]! - svc 0x37 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_create_io_pool - str x0, [sp, #-16]! - svc 0x39 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_create_io_region - str x0, [sp, #-16]! - svc 0x3A - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_dump_info - svc 0x3C - ret -FN_END - -FN_START __nx_svc_kernel_debug - svc 0x3C - ret -FN_END - -FN_START __nx_svc_change_kernel_trace_state - svc 0x3D - ret -FN_END - -FN_START __nx_svc_create_session - stp x0, x1, [sp, #-16]! - svc 0x40 - ldp x3, x4, [sp], #16 - str w1, [x3] - str w2, [x4] - ret -FN_END - -FN_START __nx_svc_accept_session - str x0, [sp, #-16]! - svc 0x41 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_reply_and_receive_light - svc 0x42 - ret -FN_END - -FN_START __nx_svc_reply_and_receive - str x0, [sp, #-16]! - svc 0x43 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_reply_and_receive_with_user_buffer - str x0, [sp, #-16]! - svc 0x44 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_create_event - stp x0, x1, [sp, #-16]! - svc 0x45 - ldp x3, x4, [sp], #16 - str w1, [x3] - str w2, [x4] - ret -FN_END - -FN_START __nx_svc_map_io_region - svc 0x46 - ret -FN_END - -FN_START __nx_svc_unmap_io_region - svc 0x47 - ret -FN_END - -FN_START __nx_svc_map_physical_memory_unsafe - svc 0x48 - ret -FN_END - -FN_START __nx_svc_unmap_physical_memory_unsafe - svc 0x49 - ret -FN_END - -FN_START __nx_svc_set_unsafe_limit - svc 0x4A - ret -FN_END - -FN_START __nx_svc_create_code_memory - str x0, [sp, #-16]! - svc 0x4B - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_control_code_memory - svc 0x4C - ret -FN_END - -FN_START __nx_svc_sleep_system - svc 0x4D - ret -FN_END - -FN_START __nx_svc_read_write_register - str x0, [sp, #-16]! - svc 0x4E - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_set_process_activity - svc 0x4F - ret -FN_END - -FN_START __nx_svc_create_shared_memory - str x0, [sp, #-16]! - svc 0x50 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_map_transfer_memory - svc 0x51 - ret -FN_END - -FN_START __nx_svc_unmap_transfer_memory - svc 0x52 - ret -FN_END - -FN_START __nx_svc_create_interrupt_event - str x0, [sp, #-16]! - svc 0x53 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_query_physical_address - str x0, [sp, #-16]! - svc 0x54 - ldr x4, [sp], #16 - stp x1, x2, [x4] - str x3, [x4, #16] - ret -FN_END - -FN_START __nx_svc_query_io_mapping - stp x0, x1, [sp, #-16]! - svc 0x55 - ldp x3, x4, [sp], #16 - str x1, [x3] - str x2, [x4] - ret -FN_END - -FN_START __nx_svc_legacy_query_io_mapping - str x0, [sp, #-16]! - svc 0x55 - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_create_device_address_space - str x0, [sp, #-16]! - svc 0x56 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_attach_device_address_space - svc 0x57 - ret -FN_END - -FN_START __nx_svc_detach_device_address_space - svc 0x58 - ret -FN_END - -FN_START __nx_svc_map_device_address_space_by_force - svc 0x59 - ret -FN_END - -FN_START __nx_svc_map_device_address_space_aligned - svc 0x5A - ret -FN_END - -FN_START __nx_svc_map_device_address_space - str x0, [sp, #-16]! - svc 0x5B - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_unmap_device_address_space - svc 0x5C - ret -FN_END - -FN_START __nx_svc_invalidate_process_data_cache - svc 0x5D - ret -FN_END - -FN_START __nx_svc_store_process_data_cache - svc 0x5E - ret -FN_END - -FN_START __nx_svc_flush_process_data_cache - svc 0x5F - ret -FN_END - -FN_START __nx_svc_debug_active_process - str x0, [sp, #-16]! - svc 0x60 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_break_debug_process - svc 0x61 - ret -FN_END - -FN_START __nx_svc_terminate_debug_process - svc 0x62 - ret -FN_END - -FN_START __nx_svc_get_debug_event - svc 0x63 - ret -FN_END - -FN_START __nx_svc_legacy_continue_debug_event - svc 0x64 - ret -FN_END - -FN_START __nx_svc_continue_debug_event - svc 0x64 - ret -FN_END - -FN_START __nx_svc_get_process_list - str x0, [sp, #-16]! - svc 0x65 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_get_thread_list - str x0, [sp, #-16]! - svc 0x66 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_get_debug_thread_context - svc 0x67 - ret -FN_END - -FN_START __nx_svc_set_debug_thread_context - svc 0x68 - ret -FN_END - -FN_START __nx_svc_query_debug_process_memory - str x1, [sp, #-16]! - svc 0x69 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_read_debug_process_memory - svc 0x6A - ret -FN_END - -FN_START __nx_svc_write_debug_process_memory - svc 0x6B - ret -FN_END - -FN_START __nx_svc_set_hardware_break_point - svc 0x6C - ret -FN_END - -FN_START __nx_svc_get_debug_thread_param - stp x0, x1, [sp, #-16]! - svc 0x6D - ldp x3, x4, [sp], #16 - str x1, [x3] - str w2, [x4] - ret -FN_END - -FN_START __nx_svc_get_system_info - str x0, [sp, #-16]! - svc 0x6F - ldr x2, [sp], #16 - str x1, [x2] - ret -FN_END - -FN_START __nx_svc_create_port - stp x0, x1, [sp, #-16]! - svc 0x70 - ldp x3, x4, [sp], #16 - str w1, [x3] - str w2, [x4] - ret -FN_END - -FN_START __nx_svc_manage_named_port - str x0, [sp, #-16]! - svc 0x71 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_connect_to_port - str x0, [sp, #-16]! - svc 0x72 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_set_process_memory_permission - svc 0x73 - ret -FN_END - -FN_START __nx_svc_map_process_memory - svc 0x74 - ret -FN_END - -FN_START __nx_svc_unmap_process_memory - svc 0x75 - ret -FN_END - -FN_START __nx_svc_query_process_memory - str x1, [sp, #-16]! - svc 0x76 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_map_process_code_memory - svc 0x77 - ret -FN_END - -FN_START __nx_svc_unmap_process_code_memory - svc 0x78 - ret -FN_END - -FN_START __nx_svc_create_process - str x0, [sp, #-16]! - svc 0x79 - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_start_process - svc 0x7A - ret -FN_END - -FN_START __nx_svc_terminate_process - svc 0x7B - ret -FN_END - -FN_START __nx_svc_get_process_info - str x0, [sp, #-16]! - svc 0x7C - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_create_resource_limit - str x0, [sp, #-16]! - svc 0x7D - ldr x2, [sp], #16 - str w1, [x2] - ret -FN_END - -FN_START __nx_svc_set_resource_limit_limit_value - svc 0x7E - ret -FN_END - -FN_START __nx_svc_call_secure_monitor - str x0, [sp, #-16]! - mov x8, x0 - ldp x0, x1, [x8] - ldp x2, x3, [x8, #0x10] - ldp x4, x5, [x8, #0x20] - ldp x6, x7, [x8, #0x30] - svc 0x7F - ldr x8, [sp], #16 - stp x0, x1, [x8] - stp x2, x3, [x8, #0x10] - stp x4, x5, [x8, #0x20] - stp x6, x7, [x8, #0x30] - ret -FN_END diff --git a/src/svc/asm.rs b/src/svc/asm.rs new file mode 100644 index 000000000..687ee3fde --- /dev/null +++ b/src/svc/asm.rs @@ -0,0 +1,1463 @@ +use core::arch::naked_asm as nasm; + +use crate::arm; +use crate::macros::util::maybe_cfi; +use crate::result::ResultCode; + +use super::{ + BreakReason, DebugEvent, Handle, InfoId, LastThreadContext, LimitableResource, MemoryAttribute, + MemoryInfo, MemoryPermission, PageInfo, ThreadActivity, +}; + +#[unsafe(naked)] +pub unsafe extern "C" fn set_heap_size(out_address: *mut *mut u8, size: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x1", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_memory_permission( + address: *const u8, + size: usize, + value: MemoryPermission, +) -> ResultCode { + nasm!("svc 0x2", "ret") +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_memory_attribute( + address: *const u8, + size: usize, + mask: u32, + value: MemoryAttribute, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x3", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_memory( + address: *const u8, + source_address: *mut u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x4", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_memory( + address: *const u8, + source_address: *mut u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn query_memory( + out_info: *mut MemoryInfo, + out_page_info: *mut PageInfo, + address: *const u8, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x1, [sp, #-16]!", + "svc 0x6", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn exit_process() -> ! { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x7", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_thread( + handle: *mut Handle, + entry: unsafe extern "C" fn(*mut u8) -> !, + entry_arg: *const u8, + stack_top: *const u8, + priority: i32, + processor_id: i32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x8", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn start_thread(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x9", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn exit_thread() -> ! { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0xA", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn sleep_thread(timeout: i64) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0xB", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_thread_priority(out_priority: *mut i32, handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0xC", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_thread_priority(handle: Handle, priority: i32) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0xD", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_thread_core_mask( + core_mask: *mut i32, + core_affinity: *mut u64, + handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0xE", + "ldp x3, x4, [sp], #16", + "str w1, [x3]", + "str x2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_thread_core_mask( + handle: Handle, + preferred_core: i32, + affinity_mask: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0xF", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_current_processor_number() -> u32 { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x10", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn signal_event(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x11", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn clear_event(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x12", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_shared_memory( + handle: Handle, + address: *const u8, + size: usize, + permission: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x13", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_shared_memory( + handle: Handle, + address: *const u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x14", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_transfer_memory( + out_handle: *mut Handle, + address: *const u8, + size: usize, + permissions: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x15", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn close_handle(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x16", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn reset_signal(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x17", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn wait_synchronization( + out_index: *mut i32, + handles: *const Handle, + handle_count: u32, + timeout: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x18", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn cancel_synchronisation(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x19", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn arbitrate_lock( + thread_handle: Handle, + tag_location: *const u8, + tag: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x1A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn arbitrate_unlock(tag_location: *const u8) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x1B", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn wait_process_wide_key_atomic( + wait_location: *const u8, + tag_location: *const u8, + desired_tag: u32, + timeout: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x1C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn signal_process_wide_key( + tag_location: *const u8, + desired_tag: i32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x1D", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_system_tick() -> u64 { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x1E", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn connect_to_named_port(out_handle: *mut Handle, name: *const u8) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x1F", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn send_sync_request_light(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x20", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn send_sync_request(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x21", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn send_sync_request_with_user_data( + buffer: *mut u8, + size: usize, + session: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x22", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn send_async_request_with_user_data( + handle: *mut Handle, + buffer: *mut u8, + size: usize, + session: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x23", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_process_id( + out_process_id: *mut u64, + process_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x24", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_thread_id(out_thread_id: *mut u64, handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x25", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn r#break(reason: BreakReason, arg: *const u8, size: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x26", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn output_debug_string(msg: *const u8, len: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x27", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn return_from_exception(res: ResultCode) -> ! { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x28", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_info( + out_info: *mut u64, + id: InfoId, + handle: Handle, + sub_id: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x29", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn flush_entire_data_cache() -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x2A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn flush_data_cache(address: *const u8, len: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x2B", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_physical_memory(address: *const u8, len: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x2C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_physical_memory(address: *const u8, len: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x2D", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_debug_future_thread_info( + out_context: *mut LastThreadContext, + out_thread_id: *mut u64, + debug_proc_handle: Handle, + ns: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x2E", + "ldp x6, x7, [sp], #16", + "stp x1, x2, [x6]", + "stp x3, x4, [x6, #16]", + "str x5, [x7]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_last_thread_info( + out_context: *mut LastThreadContext, + out_tls_address: *mut u64, + out_flags: *mut u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x1, x2, [sp, #-16]!", + "str x0, [sp, #-16]!", + "svc 0x2F", + "ldr x7, [sp], #16", + "stp x1, x2, [x7]", + "stp x3, x4, [x7, #16]", + "ldp x1, x2, [sp], #16", + "str x5, [x1]", + "str w6, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_resource_limit_limit_value( + out_val: *mut i64, + resource_limit_handle: Handle, + limit_kind: LimitableResource, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x30", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_resource_limit_current_value( + out_val: *mut i64, + resource_limit_handle: Handle, + limit_kind: LimitableResource, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x31", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_thread_activity( + thread_handle: Handle, + thread_state: ThreadActivity, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x32", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_thread_context3( + out_context: *mut arm::ThreadContext, + thread_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x32", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn wait_for_address( + address: *const u8, + arbitration_type: u32, + value: u32, + timeout: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x34", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn signal_to_address( + address: *const u8, + signal: u32, + value: u32, + signal_count: i32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x35", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn synchronize_preemption_states() -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x36", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +/* + +get_resource_limit_peak_value +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x37", +"ldr x2, [sp], #16", +"str x1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_io_pool +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x39", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_io_region +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x3A", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +kernel_debug +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x3C", +"ret", +maybe_cfi!(".cfi_endproc"));} + +change_kernel_trace_state +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x3D", +"ret", +maybe_cfi!(".cfi_endproc"));} + +*/ + +#[unsafe(naked)] +pub unsafe extern "C" fn create_session( + out_server_handle: *mut Handle, + out_client_handle: *mut Handle, + is_light: bool, + unk_name: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x40", + "ldp x3, x4, [sp], #16", + "str w1, [x3]", + "str w2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn accept_session(out_session_handle: *mut Handle, handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x41", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn reply_and_receive_light(handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x42", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn reply_and_receive( + out_index: *mut i32, + handles: *const Handle, + handle_count: u32, + reply_target: Handle, + timeout: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x43", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn reply_and_receive_with_user_buffer( + out_index: *mut i32, + user_buffer: *mut u8, + buffer_size: usize, + handles: *const Handle, + handle_count: u32, + reply_target: Handle, + timeout: i64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x44", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_event( + out_server_handle: *mut Handle, + out_client_handle: *mut Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x45", + "ldp x3, x4, [sp], #16", + "str w1, [x3]", + "str w2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +/* + + +map_io_region +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x46", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_io_region +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x47", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_physical_memory_unsafe +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x48", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_physical_memory_unsafe +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x49", +"ret", +maybe_cfi!(".cfi_endproc"));} + +set_unsafe_limit +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x4A", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_code_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x4B", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +control_code_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x4C", +"ret", +maybe_cfi!(".cfi_endproc"));} + +sleep_system +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x4D", +"ret", +maybe_cfi!(".cfi_endproc"));} + +read_write_register +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x4E", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +set_process_activity +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x4F", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_shared_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x50", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_transfer_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x51", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_transfer_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x52", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_interrupt_event +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x53", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +query_physical_address +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x54", +"ldr x4, [sp], #16", +"stp x1, x2, [x4]", +"str x3, [x4, #16]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +query_io_mapping +{nasm!( +maybe_cfi!(".cfi_startproc"), +"stp x0, x1, [sp, #-16]!", +"svc 0x55", +"ldp x3, x4, [sp], #16", +"str x1, [x3]", +"str x2, [x4]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +legacy_query_io_mapping +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x55", +"ldr x2, [sp], #16", +"str x1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_device_address_space +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x56", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +attach_device_address_space +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x57", +"ret", +maybe_cfi!(".cfi_endproc"));} + +detach_device_address_space +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x58", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_device_address_space_by_force +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x59", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_device_address_space_aligned +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x5A", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_device_address_space +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x5B", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_device_address_space +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x5C", +"ret", +maybe_cfi!(".cfi_endproc"));} + +invalidate_process_data_cache +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x5D", +"ret", +maybe_cfi!(".cfi_endproc"));} + +store_process_data_cache +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x5E", +"ret", +maybe_cfi!(".cfi_endproc"));} + +flush_process_data_cache +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x5F", +"ret", +maybe_cfi!(".cfi_endproc"));} + +*/ + +#[unsafe(naked)] +pub unsafe extern "C" fn debug_active_process(out_handle: *mut Handle, process_id: u64) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x60", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn break_debug_process(debug_handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x61", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn terminate_debug_process(debug_handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x62", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_debug_event( + out_debug_event: *mut DebugEvent, + debug_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x63", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn continue_debug_event( + debug_handle: Handle, + flags: u32, + thread_ids: *const u64, + thread_id_count: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x64", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_process_list( + out_count: *mut u32, + out_process_ids: *mut u64, + process_id_count: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x65", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_thread_list( + out_count: *mut u32, + out_thread_ids: *mut u64, + thread_id_count: u32, + debug_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x66", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_debug_thread_context( + thread_context: *mut u8, // *mut arm::ThreadContext + debug_handle: Handle, + thread_id: u64, + register_group: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x67", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_debug_thread_context( + debug_handle: Handle, + thread_id: u64, + thread_context: *const u8, // *const arm::ThreadContext + register_group: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x68", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn query_debug_process_memory( + out_info: *mut MemoryInfo, + out_page_info: *mut PageInfo, + debug_handle: Handle, + address: *const u8, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x1, [sp, #-16]!", + "svc 0x69", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn read_debug_process_memory( + buffer: *mut u8, + debug_handle: Handle, + address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x6A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn write_debug_process_memory( + debug_handle: Handle, + buffer: *const u8, + address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x6B", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +/* + +set_hardware_break_point +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x6C", +"ret", +maybe_cfi!(".cfi_endproc"));} + +get_debug_thread_param +{nasm!( +maybe_cfi!(".cfi_startproc"), +"stp x0, x1, [sp, #-16]!", +"svc 0x6D", +"ldp x3, x4, [sp], #16", +"str x1, [x3]", +"str w2, [x4]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +get_system_info +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x6F", +"ldr x2, [sp], #16", +"str x1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +*/ + +#[unsafe(naked)] +pub unsafe extern "C" fn create_named_port( + server_handle: *mut Handle, + client_handle: *mut Handle, + max_sessions: i32, + is_light: bool, + name: *const u8, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x70", + "ldp x3, x4, [sp], #16", + "str w1, [x3]", + "str w2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn manage_named_port( + out_handle: *mut Handle, + name: *const u8, + max_sessions: i32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x71", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +/* + +connect_to_port +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x72", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +set_process_memory_permission +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x73", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_process_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x74", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_process_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x75", +"ret", +maybe_cfi!(".cfi_endproc"));} + +query_process_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x1, [sp, #-16]!", +"svc 0x76", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +map_process_code_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x77", +"ret", +maybe_cfi!(".cfi_endproc"));} + +unmap_process_code_memory +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x78", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_process +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x79", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +start_process +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x7A", +"ret", +maybe_cfi!(".cfi_endproc"));} + +terminate_process +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x7B", +"ret", +maybe_cfi!(".cfi_endproc"));} + +get_process_info +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x7C", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +create_resource_limit +{nasm!( +maybe_cfi!(".cfi_startproc"), +"str x0, [sp, #-16]!", +"svc 0x7D", +"ldr x2, [sp], #16", +"str w1, [x2]", +"ret", +maybe_cfi!(".cfi_endproc"));} + +set_resource_limit_limit_value +{nasm!( +maybe_cfi!(".cfi_startproc"), +"svc 0x7E", +"ret", +maybe_cfi!(".cfi_endproc"));} + + +*/ + +#[unsafe(naked)] +pub unsafe extern "C" fn call_secure_monitor(args: *mut u64) { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "mov x8, x0", + "ldp x0, x1, [x8]", + "ldp x2, x3, [x8, #0x10]", + "ldp x4, x5, [x8, #0x20]", + "ldp x6, x7, [x8, #0x30]", + "svc 0x7F", + "ldr x8, [sp], #16", + "stp x0, x1, [x8]", + "stp x2, x3, [x8, #0x10]", + "stp x4, x5, [x8, #0x20]", + "stp x6, x7, [x8, #0x30]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} diff --git a/src/thread.rs b/src/thread.rs index 7bfdb621a..02d0a0133 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -11,7 +11,6 @@ use crate::diag::abort::AbortLevel; use crate::result::*; use crate::svc; use crate::util; -use core::arch::asm; use core::cell::UnsafeCell; use core::fmt; use core::marker::PhantomData; @@ -1386,7 +1385,7 @@ pub struct ThreadLocalRegion { pub _disable_counter: u16, /// The interrupt flag pub _interrupt_flag: u16, - pub _cache_maintenance_flag: u8, // HOS v14.0.0.+ + pub cache_maintenance_flag: bool, // HOS v14.0.0.+ pub _reserved_1: [u8; 0x3], // These we are ignoring since we're going to use the libnx threadVars anyway and just not use anything in this region /*pub reserved_1: [u8; 0x4], @@ -1409,14 +1408,16 @@ const_assert!(core::mem::align_of::() >= 4); // for some assu /// Gets the current thread's [`ThreadLocalRegion`] address #[inline(always)] pub fn get_thread_local_region() -> *mut ThreadLocalRegion { - let tlr: *mut ThreadLocalRegion; - unsafe { - asm!( - "mrs {}, tpidrro_el0", - out(reg) tlr - ); + #[unsafe(naked)] + unsafe extern "C" fn __nx_thread_get_thread_local_region() -> *mut ThreadLocalRegion { + core::arch::naked_asm!( + maybe_cfi!(".cfi_startproc"), + "mrs x0, tpidrro_el0", + "ret", + maybe_cfi!(".cfi_endproc") + ) } - tlr + unsafe {__nx_thread_get_thread_local_region()} } pub(crate) unsafe fn current() -> *mut imp::Thread { diff --git a/src/wait.rs b/src/wait.rs index 416637578..fe22f53ba 100755 --- a/src/wait.rs +++ b/src/wait.rs @@ -138,7 +138,7 @@ type WaitFn = fn(&[W], i64) -> Result; fn handles_wait_fn(handles: &[svc::Handle], timeout: i64) -> Result { unsafe { - svc::wait_synchronization(handles.as_ptr(), handles.len() as u32, timeout) + svc::wait_synchronization(handles, timeout) .map(|idx| idx as usize) } } From 87d2d24d880ea99fde8f64c3765384c3688890aa Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 1 Jul 2025 10:07:14 +0930 Subject: [PATCH 10/19] Add documentation to the svc module --- src/diag/abort.rs | 4 +- src/svc.rs | 204 +++++++++++++++++++++++++++++++++++++++++----- src/svc/asm.rs | 43 ++++++---- 3 files changed, 214 insertions(+), 37 deletions(-) diff --git a/src/diag/abort.rs b/src/diag/abort.rs index c6981261a..35740e30d 100755 --- a/src/diag/abort.rs +++ b/src/diag/abort.rs @@ -78,11 +78,11 @@ fn do_abort(level: AbortLevel, rc: ResultCode) { } else if level == AbortLevel::ProcessExit() { rrt0::exit(rc); } else if level == AbortLevel::SvcBreak() { + let rc =rc.get_value(); let _ = unsafe { svc::r#break( svc::BreakReason::Panic, - &rc as *const _ as *const u8, - mem::size_of::(), + core::slice::from_raw_parts(&raw const rc as *const u8, 4) ) }; } diff --git a/src/svc.rs b/src/svc.rs index 930977d78..62b2a9378 100755 --- a/src/svc.rs +++ b/src/svc.rs @@ -1,5 +1,5 @@ -//! This module wraps svc calls provided by `svc.s`. -//! There is no function-level Safety docs, but the core requirement is that all raw pointers provided must be +//! This module wraps svc calls provided by `asm.rs`. +//! There is generally no function-level Safety docs, but the core requirement is that all raw pointers provided must be //! validated by the caller. use crate::arm; @@ -370,7 +370,7 @@ pub unsafe fn unmap_memory( /// Query information about an address. Will always fetch the lowest page-aligned mapping that contains the provided address. /// -/// # SAFETY: null pointers are OK here, as we are just querying memory properties +/// null pointers are OK here, as we are just querying memory properties. #[inline(always)] #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn query_memory(address: Address) -> Result<(MemoryInfo, PageInfo)> { @@ -383,11 +383,15 @@ pub fn query_memory(address: Address) -> Result<(MemoryInfo, PageInfo)> { } } +/// What it says on the tin. #[inline(always)] pub fn exit_process() -> ! { unsafe { asm::exit_process() } } +/// Creates a thread. +/// +/// The pointer to the thread arguments and stack memory _must_ live at least as long as the thread is alive. #[inline(always)] pub unsafe fn create_thread( entry: ThreadEntrypointFn, @@ -411,6 +415,7 @@ pub unsafe fn create_thread( } } +/// Starts executing a prepared thread by handle (received from [`create_thread`]). #[inline(always)] pub fn start_thread(handle: Handle) -> Result<()> { unsafe { @@ -419,11 +424,13 @@ pub fn start_thread(handle: Handle) -> Result<()> { } } +/// What it says on the tin. #[inline(always)] pub fn exit_thread() -> ! { unsafe { asm::exit_thread() } } +/// Sleep the current thread for at least `timeout` nanoseconds or yields if passed a value from [`YieldType`][`crate::thread::YieldType`] #[inline(always)] pub fn sleep_thread(timeout: i64) -> Result<()> { unsafe { @@ -432,6 +439,7 @@ pub fn sleep_thread(timeout: i64) -> Result<()> { } } +/// Gets a thread's priority. #[inline(always)] pub fn get_thread_priority(handle: Handle) -> Result { unsafe { @@ -442,6 +450,7 @@ pub fn get_thread_priority(handle: Handle) -> Result { } } +/// Sets a thread's priority. #[inline(always)] pub fn set_thread_priority(handle: Handle, priority: i32) -> Result<()> { unsafe { @@ -450,6 +459,8 @@ pub fn set_thread_priority(handle: Handle, priority: i32) -> Result<()> { } } + +/// Gets a thread's core mask. #[inline(always)] pub fn get_thread_core_mask(handle: Handle) -> Result<(i32, u64)> { unsafe { @@ -460,6 +471,7 @@ pub fn get_thread_core_mask(handle: Handle) -> Result<(i32, u64)> { } } +/// Sets a thread's core mask. #[inline(always)] pub fn set_thread_core_mask(handle: Handle, preferred_core: i32, affinity_mask: u32) -> Result<()> { unsafe { @@ -468,11 +480,13 @@ pub fn set_thread_core_mask(handle: Handle, preferred_core: i32, affinity_mask: } } +/// Gets the processor number (core) that the current thread is executing on. #[inline(always)] pub fn get_current_processor_number() -> u32 { unsafe { asm::get_current_processor_number() } } +// Sets an event's signalled status. #[inline(always)] pub fn signal_event(handle: Handle) -> Result<()> { unsafe { @@ -481,6 +495,7 @@ pub fn signal_event(handle: Handle) -> Result<()> { } } +// Clears an event's signalled status. #[inline(always)] pub fn clear_event(handle: Handle) -> Result<()> { unsafe { @@ -489,6 +504,7 @@ pub fn clear_event(handle: Handle) -> Result<()> { } } +/// Maps a block of shared memory. #[inline(always)] pub unsafe fn map_shared_memory( handle: Handle, @@ -502,6 +518,7 @@ pub unsafe fn map_shared_memory( } } +/// Unmaps a block of shared memory. #[inline(always)] pub unsafe fn unmap_shared_memory(handle: Handle, address: Address, size: Size) -> Result<()> { unsafe { @@ -510,6 +527,10 @@ pub unsafe fn unmap_shared_memory(handle: Handle, address: Address, size: Size) } } +/// Creates a block of transfer memory. +/// +/// The memory will be reprotected with `permissions` after creation (usually set to none). +/// The original memory permissions will be restored when the handle is closed. #[inline(always)] #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn create_transfer_memory( @@ -525,6 +546,7 @@ pub fn create_transfer_memory( } } +/// Closes a handle, decrementing the reference count of the corresponding kernel object. #[inline(always)] pub fn close_handle(handle: Handle) -> Result<()> { unsafe { @@ -533,6 +555,7 @@ pub fn close_handle(handle: Handle) -> Result<()> { } } +/// Resets a signal. #[inline(always)] pub fn reset_signal(handle: Handle) -> Result<()> { unsafe { @@ -541,6 +564,9 @@ pub fn reset_signal(handle: Handle) -> Result<()> { } } +/// Waits on one or more synchronization objects, optionally with a timeout. +/// +/// The max number of handles is `0x40` (64). This is a Horizon kernel limitation. #[inline(always)] pub unsafe fn wait_synchronization( handles: &[Handle], @@ -548,25 +574,35 @@ pub unsafe fn wait_synchronization( ) -> Result { unsafe { let mut index: i32 = 0; - let rc = asm::wait_synchronization(&mut index, handles.as_ptr(), handles.len() as u32, timeout); pack(rc, index) } } +/// The same as [`wait_synchronization`] for a single handle #[inline(always)] pub fn wait_synchronization_one(handle: Handle, timeout: i64) -> Result<()> { - unsafe { wait_synchronization(&[handle], timeout).map(|_| ()) } + unsafe { + let mut index: i32 = 0; + let rc = asm::wait_synchronization(&mut index, &handle, 1u32, timeout); + pack(rc, ()) + } } +/// If the referenced thread is currently in a synchronization call ([`wait_synchronization`], [`reply_and_receive`] +/// or [`reply_and_receive_light`]), that call will be interrupted and return `0xec01`([`ResultCancelled`][`rc::ResultCancelled`]) . +/// If that thread is not currently executing such a synchronization call, the next call to a synchronization call will return `0xec01``. +/// +/// This doesn't take force-pause (activity/debug pause) into account. #[inline(always)] -pub fn cancel_synchronisation(handle: Handle) -> Result<()> { +pub fn cancel_synchronisation(thread_handle: Handle) -> Result<()> { unsafe { - let rc = asm::cancel_synchronisation(handle); + let rc = asm::cancel_synchronisation(thread_handle); pack(rc, ()) } } +/// Arbitrates a mutex lock operation in userspace. #[inline(always)] pub unsafe fn arbitrate_lock(thread_handle: Handle, tag_location: Address, tag: u32) -> Result<()> { unsafe { @@ -575,6 +611,7 @@ pub unsafe fn arbitrate_lock(thread_handle: Handle, tag_location: Address, tag: } } +/// Arbitrates a mutex unlock operation in userspace. #[inline(always)] pub unsafe fn arbitrate_unlock(tag_location: Address) -> Result<()> { unsafe { @@ -583,6 +620,7 @@ pub unsafe fn arbitrate_unlock(tag_location: Address) -> Result<()> { } } +/// Performs a condition variable wait operation in userspace. #[inline(always)] pub unsafe fn wait_process_wide_key_atomic( wait_location: Address, @@ -597,6 +635,7 @@ pub unsafe fn wait_process_wide_key_atomic( } } +/// Performs a condition variable wake-up operation in userspace. #[inline(always)] pub unsafe fn signal_process_wide_key(tag_location: Address, desired_tag: i32) -> Result<()> { unsafe { @@ -605,11 +644,13 @@ pub unsafe fn signal_process_wide_key(tag_location: Address, desired_tag: i32) - } } +/// Gets the current system tick. #[inline(always)] pub fn get_system_tick() -> u64 { unsafe { asm::get_system_tick() } } +/// Connects to a registered named port. #[inline(always)] pub unsafe fn connect_to_named_port(name: &core::ffi::CStr) -> Result { unsafe { @@ -620,6 +661,7 @@ pub unsafe fn connect_to_named_port(name: &core::ffi::CStr) -> Result { } } +/// Sends a light IPC synchronization request to a session. #[inline(always)] pub fn send_sync_request_light(handle: Handle) -> Result<()> { unsafe { @@ -628,6 +670,7 @@ pub fn send_sync_request_light(handle: Handle) -> Result<()> { } } +/// Sends an IPC synchronization request to a session. #[inline(always)] pub fn send_sync_request(handle: Handle) -> Result<()> { unsafe { @@ -636,6 +679,9 @@ pub fn send_sync_request(handle: Handle) -> Result<()> { } } +/// Sends an IPC synchronization request to a session from an user allocated buffer. +/// +/// The buffer size must be a multiple of the system page size (0x1000). #[inline(always)] pub unsafe fn send_sync_request_with_user_data( buffer: &mut [u8], @@ -647,6 +693,9 @@ pub unsafe fn send_sync_request_with_user_data( } } +/// Sends an IPC synchronization request to a session from an user allocated buffer (asynchronous version). +/// +/// The buffer size must be a multiple of the system page size (0x1000). #[inline(always)] pub unsafe fn send_async_request_with_user_data( buffer: &mut [u8], @@ -659,6 +708,7 @@ pub unsafe fn send_async_request_with_user_data( } } +/// Gets the PID associated with a process. #[inline(always)] pub fn get_process_id(process_handle: Handle) -> Result { unsafe { @@ -669,6 +719,7 @@ pub fn get_process_id(process_handle: Handle) -> Result { } } +/// Gets the TID associated with a process. #[inline(always)] pub fn get_thread_id(handle: Handle) -> Result { unsafe { @@ -679,14 +730,18 @@ pub fn get_thread_id(handle: Handle) -> Result { } } +/// Breaks execution +/// +/// The `debug_data` buffer is passed to a debugging instance if one is attached. #[inline(always)] -pub unsafe fn r#break(reason: BreakReason, arg: Address, size: Size) -> Result<()> { +pub fn r#break(reason: BreakReason, debug_data: &[u8]) -> Result<()> { unsafe { - let rc = asm::r#break(reason, arg, size); + let rc = asm::r#break(reason, debug_data.as_ptr(), debug_data.len()); pack(rc, ()) } } +/// Outputs debug text, if used during debugging. #[inline(always)] pub unsafe fn output_debug_string(msg: &core::ffi::CStr) -> Result<()> { unsafe { @@ -695,6 +750,7 @@ pub unsafe fn output_debug_string(msg: &core::ffi::CStr) -> Result<()> { } } +/// Returns from an exception. #[inline(always)] pub fn return_from_exception(res: ResultCode) -> ! { unsafe { @@ -702,6 +758,9 @@ pub fn return_from_exception(res: ResultCode) -> ! { } } +/// Retrieves information about the system, or a certain kernel object, depending on the value of `id`. +/// +/// `handle` is for particular kernel objects, but `INVALID_HANDLE` is used to retrieve information about the system. #[inline(always)] pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { unsafe { @@ -712,7 +771,14 @@ pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { } } +/* +/// Flushes the entire data cache (by set/way). +/// +/// This is a privileged syscall and may not be available. +/// +/// This syscall has dangerous side effects and should not be used. #[inline(always)] +#[deprecated] pub unsafe fn flush_entire_data_cache() -> Result<()> { unsafe { let rc = asm::flush_entire_data_cache(); @@ -720,7 +786,13 @@ pub unsafe fn flush_entire_data_cache() -> Result<()> { } } + */ + +/// Flushes data cache for a virtual address range. +/// +/// [`cache_flush`][`crate::arm::cache_flush`] should be used instead whenever possible. #[inline(always)] +#[deprecated] pub unsafe fn flush_data_cache(address: Address, len: Size) -> Result<()> { unsafe { let rc = asm::flush_data_cache(address, len); @@ -728,6 +800,7 @@ pub unsafe fn flush_data_cache(address: Address, len: Size) -> Result<()> { } } +/// Maps new heap memory at the desired address. [3.0.0+] #[inline(always)] pub unsafe fn map_physical_memory(address: Address, len: Size) -> Result<()> { unsafe { @@ -736,6 +809,7 @@ pub unsafe fn map_physical_memory(address: Address, len: Size) -> Result<()> { } } +/// Unmaps memorry mapped by [`map_physical_memory`]. [3.0.0+] #[inline(always)] pub unsafe fn unmap_physical_memory(address: Address, len: Size) -> Result<()> { unsafe { @@ -744,6 +818,12 @@ pub unsafe fn unmap_physical_memory(address: Address, len: Size) -> Result<()> { } } +/// Gets information about a thread that will be scheduled in the future. [5.0.0+] +/// +/// `ns` is the nanoseconds in the future when the thread information will be sampled +/// by the kernel. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_future_thread_info( debug_proc_handle: Handle, @@ -762,6 +842,7 @@ pub fn get_debug_future_thread_info( } } +/// Gets information about the previously-scheduled thread. #[inline(always)] pub fn get_last_thread_info() -> Result<(LastThreadContext, u64, u32)> { unsafe { @@ -773,6 +854,9 @@ pub fn get_last_thread_info() -> Result<(LastThreadContext, u64, u32)> { } } +/// Gets the maximum value a LimitableResource can have, for a Resource Limit handle. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_resource_limit_limit_value( resource_limit_handle: Handle, @@ -786,6 +870,9 @@ pub fn get_resource_limit_limit_value( } } +/// Gets the current value a LimitableResource has, for a Resource Limit handle. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_resource_limit_current_value( resource_limit_handle: Handle, @@ -799,6 +886,7 @@ pub fn get_resource_limit_current_value( } } +/// Pauses/unpauses a thread. #[inline(always)] pub fn set_thread_activity(thread_handle: Handle, thread_state: ThreadActivity) -> Result<()> { unsafe { @@ -807,6 +895,7 @@ pub fn set_thread_activity(thread_handle: Handle, thread_state: ThreadActivity) } } +/// Dumps the registers of a thread paused by [`set_thread_activity`] (register groups: all) #[inline(always)] pub fn get_thread_context3(thread_handle: Handle) -> Result<()> { unsafe { @@ -816,6 +905,7 @@ pub fn get_thread_context3(thread_handle: Handle) -> Result<()> { } } +/// Arbitrates an address depending on type and value. [4.0.0+] #[inline(always)] pub unsafe fn wait_for_address( address: Address, @@ -829,6 +919,7 @@ pub unsafe fn wait_for_address( } } +/// Signals (and updates) an address depending on type and value. [4.0.0+] #[inline(always)] pub unsafe fn signal_to_address( address: Address, @@ -842,6 +933,7 @@ pub unsafe fn signal_to_address( } } +/// Sets thread preemption state (used during abort/panic). [8.0.0+] #[inline(always)] pub unsafe fn synchronize_preemption_state() -> Result<()> { unsafe { @@ -850,6 +942,9 @@ pub unsafe fn synchronize_preemption_state() -> Result<()> { } } +/// Creates an IPC session. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn create_session(is_light: bool, unk_name: u64) -> Result<(Handle, Handle)> { unsafe { @@ -861,6 +956,7 @@ pub fn create_session(is_light: bool, unk_name: u64) -> Result<(Handle, Handle)> } } +/// Accepts an IPC session. #[inline(always)] pub fn accept_session(handle: Handle) -> Result { unsafe { @@ -871,6 +967,9 @@ pub fn accept_session(handle: Handle) -> Result { } } +/// Performs light IPC input/output. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn reply_and_receive_light(handle: Handle) -> Result<()> { unsafe { @@ -879,6 +978,9 @@ pub fn reply_and_receive_light(handle: Handle) -> Result<()> { } } +/// Performs IPC input/output. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn reply_and_receive( handles: *const Handle, @@ -894,6 +996,9 @@ pub unsafe fn reply_and_receive( } } +/// Performs IPC input/output on a user-allocated buffer. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn reply_and_receive_with_user_buffer( user_buffer: &mut [u8], @@ -917,6 +1022,7 @@ pub unsafe fn reply_and_receive_with_user_buffer( } } +/// Creates a system event. #[inline(always)] pub fn create_event() -> Result<(Handle, Handle)> { unsafe { @@ -928,16 +1034,21 @@ pub fn create_event() -> Result<(Handle, Handle)> { } } +/// Debugs an active process. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn debug_active_process(process_id: u64) -> Result { unsafe { let mut handle: Handle = 0; - let rc = asm::debug_active_process(&mut handle, process_id); pack(rc, handle) } } +/// Breaks an active debugging session. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn break_debug_process(debug_handle: Handle) -> Result<()> { unsafe { @@ -946,6 +1057,9 @@ pub fn break_debug_process(debug_handle: Handle) -> Result<()> { } } +/// Terminates the process of an active debugging session +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn terminate_debug_process(debug_handle: Handle) -> Result<()> { unsafe { @@ -954,6 +1068,9 @@ pub fn terminate_debug_process(debug_handle: Handle) -> Result<()> { } } +/// Gets an incoming debug event from a debugging session. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_event(debug_handle: Handle) -> Result { unsafe { @@ -964,6 +1081,9 @@ pub fn get_debug_event(debug_handle: Handle) -> Result { } } +/// Continues a debugging session. [3.0.0+] +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn continue_debug_event(debug_handle: Handle, flags: u32, thread_ids: &[u64]) -> Result<()> { unsafe { @@ -977,20 +1097,24 @@ pub fn continue_debug_event(debug_handle: Handle, flags: u32, thread_ids: &[u64] } } +///Retrieves a list of all running processes. Returns the hnumber of PIDs written to the buffer. #[inline(always)] -pub fn get_process_list(process_list: &mut [u64]) -> Result { +pub fn get_process_list(process_id_list: &mut [u64]) -> Result { unsafe { let mut count: u32 = 0; let rc = asm::get_process_list( &mut count, - process_list.as_mut_ptr(), - process_list.len() as u32, + process_id_list.as_mut_ptr(), + process_id_list.len() as u32, ); pack(rc, count as usize) } } +/// Retrieves a list of all threads for a debug handle (or zero). Returns the hnumber of thread IDs written to the buffer. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_thread_list(debug_handle: Handle, thread_id_list: &mut [u64]) -> Result { unsafe { @@ -1006,6 +1130,9 @@ pub fn get_thread_list(debug_handle: Handle, thread_id_list: &mut [u64]) -> Resu } } +/// Queries the thread context for a thread under debugging. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_thread_context( debug_handle: Handle, @@ -1025,6 +1152,9 @@ pub fn get_debug_thread_context( } } +/// Writes the thread context (scoped by `register_group`) back into a thread under debugging. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub fn set_debug_thread_context( debug_handle: Handle, @@ -1043,6 +1173,11 @@ pub fn set_debug_thread_context( } } + +/// Gets the memory metadata for an address in a process under debugging. +/// +/// This is a privileged syscall and may not be available. +/// /// # Safety /// /// null pointers are OK here, as we are just querying the memory's information @@ -1050,7 +1185,7 @@ pub fn set_debug_thread_context( #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn query_debug_process_memory( debug_handle: Handle, - address: Address, + address: usize, ) -> Result<(MemoryInfo, PageInfo)> { unsafe { let mut memory_info: MemoryInfo = Default::default(); @@ -1066,19 +1201,24 @@ pub fn query_debug_process_memory( } } +/// Reads memory from an address in a process under debugging. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn read_debug_process_memory( debug_handle: Handle, read_address: usize, - read_size: usize, - buffer: MutAddress, + buffer: &mut [u8], ) -> Result<()> { unsafe { - let rc = asm::read_debug_process_memory(buffer, debug_handle, read_address, read_size); + let rc = asm::read_debug_process_memory(buffer.as_mut_ptr(), debug_handle, read_address, buffer.len()); pack(rc, ()) } } +/// Reads memory to an address in a process under debugging. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn write_debug_process_memory( debug_handle: Handle, @@ -1092,6 +1232,10 @@ pub unsafe fn write_debug_process_memory( } } + +/// Creates a named port. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn create_named_port( name: &core::ffi::CStr, @@ -1113,6 +1257,9 @@ pub unsafe fn create_named_port( } } +/// Manages a named port. +/// +/// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn manage_named_port(name: &core::ffi::CStr, max_sessions: i32) -> Result { unsafe { @@ -1123,10 +1270,27 @@ pub unsafe fn manage_named_port(name: &core::ffi::CStr, max_sessions: i32) -> Re } } +/// Connects a named port. +/// +/// This is a privileged syscall and may not be available. +#[inline(always)] +pub unsafe fn connect_named_port(client_session: Handle) -> Result { + unsafe { + let mut session_handle: Handle = 0; + + let rc = asm::connect_to_port(&mut session_handle, client_session); + pack(rc, session_handle) + } +} + + +/// Calls a secure monitor function (TrustZone, EL3). +/// +/// This is a privileged syscall and may not be available. #[inline(always)] -pub fn call_secure_monitor(mut inout: [u64; 8]) -> [u64; 8] { +pub fn call_secure_monitor(mut secmon_args: [u64; 8]) -> [u64; 8] { unsafe { - asm::call_secure_monitor(inout.as_mut_ptr()); + asm::call_secure_monitor(secmon_args.as_mut_ptr()); } - inout + secmon_args } diff --git a/src/svc/asm.rs b/src/svc/asm.rs index 687ee3fde..7ce47f06c 100644 --- a/src/svc/asm.rs +++ b/src/svc/asm.rs @@ -394,7 +394,10 @@ pub unsafe extern "C" fn get_system_tick() -> u64 { } #[unsafe(naked)] -pub unsafe extern "C" fn connect_to_named_port(out_handle: *mut Handle, name: *const u8) -> ResultCode { +pub unsafe extern "C" fn connect_to_named_port( + out_handle: *mut Handle, + name: *const u8, +) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), "str x0, [sp, #-16]!", @@ -785,7 +788,10 @@ pub unsafe extern "C" fn create_session( } #[unsafe(naked)] -pub unsafe extern "C" fn accept_session(out_session_handle: *mut Handle, handle: Handle) -> ResultCode { +pub unsafe extern "C" fn accept_session( + out_session_handle: *mut Handle, + handle: Handle, +) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), "str x0, [sp, #-16]!", @@ -1088,7 +1094,10 @@ maybe_cfi!(".cfi_endproc"));} */ #[unsafe(naked)] -pub unsafe extern "C" fn debug_active_process(out_handle: *mut Handle, process_id: u64) -> ResultCode { +pub unsafe extern "C" fn debug_active_process( + out_handle: *mut Handle, + process_id: u64, +) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), "str x0, [sp, #-16]!", @@ -1218,7 +1227,7 @@ pub unsafe extern "C" fn query_debug_process_memory( out_info: *mut MemoryInfo, out_page_info: *mut PageInfo, debug_handle: Handle, - address: *const u8, + address: usize, ) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), @@ -1330,18 +1339,22 @@ pub unsafe extern "C" fn manage_named_port( ); } -/* - -connect_to_port -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x72", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn connect_to_port(session: *mut Handle, port_handle: Handle) -> ResultCode { + { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x72", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); + } +} +/* set_process_memory_permission {nasm!( maybe_cfi!(".cfi_startproc"), From 1787ce05a9459cb4def80a54809a3fff92f027e3 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 1 Jul 2025 10:22:55 +0930 Subject: [PATCH 11/19] Fix check and clippy lints. --- src/arm.rs | 2 +- src/console.rs | 12 +++++------- src/diag/abort.rs | 1 - src/mem.rs | 2 -- src/socket.rs | 26 ++++++++++++-------------- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/arm.rs b/src/arm.rs index 1b703deb8..2a6095109 100755 --- a/src/arm.rs +++ b/src/arm.rs @@ -163,7 +163,7 @@ pub struct ThreadContext { /// * `address`: Memory address. /// * `size`: Memory size. #[inline(always)] -#[deny(clippy::not_unsafe_ptr_arg_deref)] +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn cache_flush(address: *mut u8, size: usize) { // Equivalent to `cache_flush2` commented out below, but ends up being better hand-written // than compiler optimised. diff --git a/src/console.rs b/src/console.rs index 9a343b48c..9709eaede 100644 --- a/src/console.rs +++ b/src/console.rs @@ -124,13 +124,11 @@ pub mod vty { for y in y..(y + height as i32) { for x in x..(x + width as i32) { - if let Some(color) = color_iter.next() { - if (0..self.canvas.surface.height().cast_signed()).contains(&y) - && (0..self.canvas.surface.width().cast_signed()).contains(&x) - { - self.buffer[(x + y * self.canvas.surface.width() as i32) as usize] = - color; - } + if let Some(color) = color_iter.next() + && (0..self.canvas.surface.height().cast_signed()).contains(&y) + && (0..self.canvas.surface.width().cast_signed()).contains(&x) + { + self.buffer[(x + y * self.canvas.surface.width() as i32) as usize] = color; } } } diff --git a/src/diag/abort.rs b/src/diag/abort.rs index 35740e30d..75fd395dc 100755 --- a/src/diag/abort.rs +++ b/src/diag/abort.rs @@ -4,7 +4,6 @@ use crate::mem::alloc; use crate::result::*; use crate::rrt0; use crate::svc; -use core::mem; #[cfg(feature = "services")] use crate::ipc::sf; diff --git a/src/mem.rs b/src/mem.rs index 833002914..06a9bb206 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,7 +1,5 @@ //! Memory (heap) support and utils -use core::usize; - use crate::result::ResultBase; use crate::svc; pub mod alloc; diff --git a/src/socket.rs b/src/socket.rs index ce77e4ac7..f12463ddd 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -955,21 +955,19 @@ pub mod net { /// # Examples /// /// ```no_run - /// use nx::socket::net::UdpSocket; + /// + /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST, Some(34254))?; /// - /// fn main() { - /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST, Some(34254))?; + /// // Receives a single datagram message on the socket. If `buf` is too small to hold + /// // the message, it will be cut off. + /// let mut buf = [0; 10]; + /// let (amt, src_ip, src_port) = socket.recv_from(&mut buf)?; /// - /// // Receives a single datagram message on the socket. If `buf` is too small to hold - /// // the message, it will be cut off. - /// let mut buf = [0; 10]; - /// let (amt, src_ip, src_port) = socket.recv_from(&mut buf)?; - /// - /// // Redeclare `buf` as slice of the received data and send reverse data back to origin. - /// let buf = &mut buf[..amt]; - /// buf.reverse(); - /// socket.send_to(buf, (src_ip, src_port))?; - /// } + /// // Redeclare `buf` as slice of the received data and send reverse data back to origin. + /// let buf = &mut buf[..amt]; + /// buf.reverse(); + /// socket.send_to(buf, (src_ip, src_port))?; + /// /// ``` pub struct UdpSocket(i32); @@ -1320,7 +1318,7 @@ pub mod net { self.0, IpProto::IP as _, IpOptions::MulticastTimeToLive as _, - Buffer::from_other_var(&(ttl as u8)), + Buffer::from_var(&ttl), )? { return ResultCode::new_err(nx::result::pack_value( rc::RESULT_MODULE, From 982b5ba198970e40c96e8ec0418b7647a333c37a Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 1 Jul 2025 14:53:06 +0930 Subject: [PATCH 12/19] Fix support for unwinding a panicking thread. --- src/thread.rs | 7 ++++--- src/thread/scoped.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/thread.rs b/src/thread.rs index 02d0a0133..35c50f24e 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -805,7 +805,7 @@ impl Clone for JoinInner<'static, T> { } impl JoinInner<'_, T> { - fn join(mut self) -> Result { + fn join(&mut self) -> Result { let _ = self.native.join(); Arc::get_mut(&mut self.packet) .unwrap() @@ -814,6 +814,7 @@ impl JoinInner<'_, T> { .take() .unwrap() } + fn wait_exit(&self, timeout: Option) -> crate::result::Result<()> { self.native.join_timeout(timeout) } @@ -941,8 +942,8 @@ impl JoinHandle { /// }).unwrap(); /// join_handle.join().expect("Couldn't join on the associated thread"); /// ``` - pub fn join(self) -> Result { - self.0.clone().join() + pub fn join(mut self) -> Result { + self.0.join() } /// Waits for the associated thread to finish, with a timeout (in nanoseconds) diff --git a/src/thread/scoped.rs b/src/thread/scoped.rs index 74f0e3c2a..b0abcba65 100644 --- a/src/thread/scoped.rs +++ b/src/thread/scoped.rs @@ -296,7 +296,7 @@ impl ScopedJoinHandle<'_, T> { /// assert!(t.join().is_err()); /// }); /// ``` - pub fn join(self) -> Result { + pub fn join(mut self) -> Result { self.0.join() } From 92ad61166ef498e67604a6d1597e936185cc9ce6 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Wed, 2 Jul 2025 17:32:46 +0930 Subject: [PATCH 13/19] Better docs in the canvas module, and use the provenence API for a ptr->usize conversion. --- src/gpu/canvas.rs | 72 ++++++++++++++++++++++++++++++++++++---------- src/gpu/surface.rs | 2 +- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/gpu/canvas.rs b/src/gpu/canvas.rs index 7de8fbc7a..372d355bc 100644 --- a/src/gpu/canvas.rs +++ b/src/gpu/canvas.rs @@ -185,16 +185,30 @@ pub(crate) mod sealed { use super::{AlphaBlend, ColorFormat, PixelFormat}; pub trait CanvasColorFormat: Copy + Default + 'static { + /// The raw type that will be written into the backing buffer; type RawType: num_traits::PrimInt + 'static; + + /// The native color format used const COLOR_FORMAT: ColorFormat; - const BYTES_PER_PIXEL: u32 = Self::COLOR_FORMAT.bytes_per_pixel(); - const BYTES_PER_PIXEL_LOG2: u32 = Self::BYTES_PER_PIXEL.ilog2(); + /// The native pixel format const PIXEL_FORMAT: PixelFormat; + /// The size of the raw pixel in bytes (must be the same size as the RawType) + const BYTES_PER_PIXEL: u32 = Self::COLOR_FORMAT.bytes_per_pixel(); + + /// Create a new default instance of the type fn new() -> Self; + + /// Create an instance of the type scaled from standard 888 color values fn new_scaled(r: u8, g: u8, b: u8, a: u8) -> Self; + /// Transmute from the raw backing type fn from_raw(raw: Self::RawType) -> Self; + /// Transmute to the raw backing type fn to_raw(self) -> Self::RawType; + /// Alpha blend the pixels together fn blend_with(self, other: Self, blend: AlphaBlend) -> Self; + /// Scale the color's alpha channel + /// + /// `alpha` must be between 0 and 1, or the implementation should panic. fn scale_alpha(self, alpha: f32) -> Self; } } @@ -206,6 +220,7 @@ pub struct CanvasManager { #[allow(clippy::too_many_arguments)] impl CanvasManager { + /// Computes the heap memory required to contain all the frame buffers for the provided parameters. pub const fn total_heap_required( width: u32, height: u32, @@ -260,6 +275,7 @@ impl CanvasManager { } /// Creates a new managed layer (application/applet window) that can be drawn on overlay elements. + /// /// These layers exist on top of stray layers and application/applet UIs, and can be Z-order vertically layered over each other. /// The GPU provides the alpha blending for all layers based on the committed frame. #[inline(always)] @@ -289,8 +305,10 @@ impl CanvasManager { self.surface.wait_vsync_event(timeout.unwrap_or(-1)) } - /// Check out a canvas/framebuffer to draw a new frame. This frame is buffered to a linear buffer before being - /// swizzled into the backing buffer during frame-commit. This provides better performance for line-based renderers + /// Check out a canvas/framebuffer to draw a new frame. + /// + /// This frame is buffered to a linear buffer before being swizzled + /// into the backing buffer during frame-commit. This provides better performance for line-based renderers /// as writes will be sequential in memory. There are cache misses during commit, but that is still better performance /// than mapped page churn for GPU-mapped memory. #[inline(always)] @@ -353,6 +371,7 @@ impl CanvasManager { } /// Check out a canvas/framebuffer to draw a new frame, without a linear buffer. + /// /// This can be used in memory constrained environments such as sysmodules, but also provides slightly better performance /// for frames that draw in block rather than scan-lines (e.g. JPEG decoding). #[inline(always)] @@ -384,6 +403,9 @@ impl CanvasManager { pub trait Canvas { type ColorFormat: sealed::CanvasColorFormat; + /// Draw a single pixel to the canvas + /// + /// out-of-bounds co-ordinates should be ignored by the implementation. fn draw_single(&mut self, x: i32, y: i32, color: Self::ColorFormat, blend: AlphaBlend); fn height(&self) -> u32; fn width(&self) -> u32; @@ -396,6 +418,9 @@ pub trait Canvas { } } + /// Draw a single-color rectangle to the canvas + /// + /// out-of-bounds co-ordinates should be ignored by the implementation. fn draw_rect( &mut self, x: i32, @@ -418,6 +443,9 @@ pub trait Canvas { } } + /// Draw a straight line from the start co-ordinates to the end co-ordinates + /// + /// Start/end co-ordinates may be outside the screen, but out-of-bounds pixel co-ordinates should be ignored by the implementation. fn draw_line( &mut self, start: (i32, i32), @@ -432,6 +460,9 @@ pub trait Canvas { } } + /// Draw an empty circle from the centre co-ordinates and radius. + /// + /// The shape does not need to be fully in-bounds, but out-of-bounds pixel co-ordinates should be ignored by the implementation. fn draw_circle( &mut self, x: i32, @@ -447,6 +478,9 @@ pub trait Canvas { } } + /// Draw an filled circle from the centre co-ordinates and radius. + /// + /// The shape does not need to be fully in-bounds, but out-of-bounds pixel co-ordinates should be ignored by the implementation. fn draw_circle_filled( &mut self, cx: i32, @@ -470,6 +504,9 @@ pub trait Canvas { } } + /// Draw text in a true-type font to the canvas. + /// + /// The text does not need to be fully in-bounds, but out-of-bounds pixel co-ordinates should be ignored by the implementation. #[cfg(feature = "truetype")] fn draw_font_text( &mut self, @@ -537,6 +574,9 @@ pub trait Canvas { } } + /// Draw text in a 8x8 monospace font to the canvas. + /// + /// The text does not need to be fully in-bounds, but out-of-bounds pixel co-ordinates should be ignored by the implementation. #[cfg(feature = "fonts")] #[inline(always)] fn draw_ascii_bitmap_text( @@ -551,6 +591,9 @@ pub trait Canvas { self.draw_bitmap_text(text, &font8x8::BASIC_FONTS, color, scale, x, y, blend); } + /// Draw text to the canvas, with a unicode font from the [`font8x8`] crate. + /// + /// The text does not need to be fully in-bounds, but out-of-bounds pixel co-ordinates should be ignored by the implementation. #[cfg(feature = "fonts")] fn draw_bitmap_text( &mut self, @@ -613,6 +656,7 @@ pub struct BufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { } impl BufferedCanvas<'_, ColorFormat> { + /// Write from the pixel buffer to the backing framebuffer in a memory efficient way for GPU page boundaries. fn convert_buffers(&mut self) { let block_config: BlockLinearHeights = self.manager.surface.get_block_linear_config(); let block_height_gobs = block_config.block_height(); @@ -672,10 +716,9 @@ impl Canvas for BufferedCanvas<'_, ColorFormat> ) }; - let _: () = raw_buffer - .iter_mut() - .map(|pixel_slot| *pixel_slot = raw_color) - .collect(); + for pixel_slot in raw_buffer.iter_mut() { + *pixel_slot = raw_color; + } } fn height(&self) -> u32 { self.manager.surface.height() @@ -725,6 +768,7 @@ impl Drop for BufferedCanvas<'_, ColorFormat> { } } +/// A Canvas checked out from the manager where draws are directly writed to the backing framebuffer memory. pub struct UnbufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { slot: i32, base_pointer: usize, @@ -734,8 +778,9 @@ pub struct UnbufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { } impl UnbufferedCanvas<'_, ColorFormat> { - pub fn raw_buffer(&mut self) -> &mut [u8] { - unsafe { core::slice::from_raw_parts_mut(self.base_pointer as *mut u8, self.buffer_size) } + /// Gets a mutable reference to the raw framebuffer + pub fn raw_buffer(&mut self) -> &mut [ColorFormat::RawType] { + unsafe { core::slice::from_raw_parts_mut(core::ptr::with_exposed_provenance_mut(self.base_pointer), self.buffer_size / core::mem::size_of::()) } } pub fn draw_single(&mut self, x: i32, y: i32, color: ColorFormat, blend: AlphaBlend) { @@ -770,14 +815,14 @@ impl UnbufferedCanvas<'_, ColorFormat> { } } - const BYTES_PER_PIXEL: u32 = ColorFormat::BYTES_PER_PIXEL; + /// Calculate the actual offset of the pixel in the swizzled framebuffer from the x/y co-ordinates. fn xy_to_gob_pixel_offset(&self, x: u32, y: u32) -> usize { let block_config = self.manager.surface.get_block_linear_config(); let block_height = block_config.block_height(); let mut gob_offset = (y / (8 * block_height)) * 512 * block_height * (self.manager.surface.pitch() / 64) - + (x * Self::BYTES_PER_PIXEL / 64) * 512 * block_height + + (x * ColorFormat::COLOR_FORMAT.bytes_per_pixel() / 64) * 512 * block_height + (y % (8 * block_height) / 8) * 512; // Figure 46 in the TRM is in pixel space, even though each GOB in a block is sequential in memory. @@ -792,9 +837,6 @@ impl UnbufferedCanvas<'_, ColorFormat> { + (x % 16); (gob_offset / ColorFormat::COLOR_FORMAT.bytes_per_pixel()) as usize - - // the above calculations from the erista TRM are for byte offsets, but we have an array of [`Color`]s so we need to adjust - //gob_offset + (gob_pixel_offset * bytes_per_pixel) } } diff --git a/src/gpu/surface.rs b/src/gpu/surface.rs index 3eac2f789..791819597 100755 --- a/src/gpu/surface.rs +++ b/src/gpu/surface.rs @@ -297,7 +297,7 @@ impl Surface { flags: ioctl::AllocFlags::ReadOnly, align: alloc::PAGE_ALIGNMENT as u32, kind: Kind::Pitch, - address: surface.buffer_data.ptr as usize, + address: surface.buffer_data.ptr.expose_provenance(), ..Default::default() }; surface.do_ioctl(&mut ioctl_alloc)?; From c512172648f6b0ed2546f69e25d3b1828250ccfe Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 3 Jul 2025 14:38:55 +0930 Subject: [PATCH 14/19] Can't help myself with changes. --- Cargo.toml | 6 ++--- src/console.rs | 55 +++++++++++++++++++++++++++++++++++----- src/elf.rs | 18 +++++-------- src/fs.rs | 5 ++++ src/gpu/canvas.rs | 7 +++-- src/ipc.rs | 12 +-------- src/ipc/sf.rs | 13 +++------- src/ipc/sf/bsd.rs | 17 ++++++++++--- src/ipc/sf/hid/shmem.rs | 13 +++++----- src/lib.rs | 8 +----- src/macros/ipc/client.rs | 6 ++--- src/rrt0.rs | 3 +++ src/socket.rs | 2 +- src/svc.rs | 14 +++++----- src/svc/asm.rs | 2 +- src/util.rs | 1 + 16 files changed, 110 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a1bf9a40..4376a1abe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ include = [ [dependencies] paste = "1.0" -logpacket = { git = "https://github.com/aarch64-switch-rs/logpacket" } +logpacket = { git = "https://github.com/aarch64-switch-rs/logpacket", tag = "0.1.0"} arrayvec = { version = "0.7.4", default-features = false } static_assertions = "1.1.0" lock_api = { version = "0.4.12", features = ["nightly"] } @@ -76,8 +76,8 @@ default = [] services = [] smc = [] gpu = ["services"] -vty = ["console", "dep:embedded-term", "dep:embedded-graphics-core"] -console = ["canvas", "dep:font8x8"] +vty = ["canvas", "dep:embedded-term", "dep:embedded-graphics-core"] +console = ["fonts"] canvas = ["gpu", "dep:line_drawing"] fonts = ["canvas", "dep:ab_glyph", "dep:font8x8"] truetype = ["fonts"] diff --git a/src/console.rs b/src/console.rs index 9709eaede..a28c25fae 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,6 +1,9 @@ //! Console Services -/// Virtuall TTY functionality +/// Virtual TTY functionality +/// +/// The types contained are used to create a tty-like environment, that emulate an +/// ANSI console (e.g. by wrapping the canvas in a [`embedded_term::TextOnGraphic`]). #[cfg(feature = "vty")] pub mod vty { @@ -16,14 +19,20 @@ pub mod vty { pub use embedded_graphics_core::pixelcolor::{Rgb888, RgbColor}; pub use embedded_graphics_core::primitives::rectangle::Rectangle; + /// Type alias for a drawable text-buffer backed console. + /// + /// The console state is stored in a text buffer, and draws are pushed through a Canvas + /// implementation that keeps a persistant pixel buffer between draw calls. + pub type TextBufferConsole = embedded_term::Console>; + /// Canvas/Framebuffer type that keeps a single buffer that is /// flushed to the display on change. - pub struct PersistantBufferedCanvas { + pub struct PersistentBufferedCanvas { buffer: Box<[Rgb888]>, canvas: CanvasManager, } - impl PersistantBufferedCanvas { + impl PersistentBufferedCanvas { /// Wraps and existing `CanvasManager` instance #[inline(always)] pub fn new(canvas: CanvasManager) -> Self { @@ -80,7 +89,7 @@ pub mod vty { } } - impl OriginDimensions for PersistantBufferedCanvas { + impl OriginDimensions for PersistentBufferedCanvas { #[inline(always)] fn size(&self) -> Size { Size { @@ -90,7 +99,7 @@ pub mod vty { } } - impl DrawTarget for PersistantBufferedCanvas { + impl DrawTarget for PersistentBufferedCanvas { type Color = Rgb888; type Error = crate::result::ResultCode; fn draw_iter(&mut self, pixels: I) -> Result<()> @@ -153,7 +162,14 @@ pub mod vty { } } } + +#[cfg(feature = "console")] pub mod scrollback { + //! Console types that are really just text buffers that you can push data into. + //! + //! These types are useful if you want to log data to the screen as text, but can't do edits or backtracking + //! like the vty module. + use core::num::NonZeroU16; use crate::{ @@ -173,12 +189,17 @@ pub mod scrollback { sync::Arc, }; + /// A channel-like object for sending strings to the console for display. + /// + /// When all clones of this object are dropped, the strong count of the inner `Arc` will drop to zero and the `Weak` + /// handle in the background thread will no longer be able to upgrade and read the data. This will cause the background thread to exit. #[derive(Clone)] pub struct BackgroundWriter { inner: Arc>>, } impl BackgroundWriter { + /// Create a new console to live in a background thread. pub fn new( gpu_ctx: Arc>, history_limit: u16, @@ -222,6 +243,7 @@ pub mod scrollback { }) } + /// Writes a string into the cross-thread buffer. #[inline(always)] pub fn write(&self, message: impl ToString) { self.inner.lock().push_back(message.to_string()); @@ -231,13 +253,18 @@ pub mod scrollback { /// This console creates a full-screen layer that will just scroll through provided strings pub struct ScrollbackConsole { canvas: CanvasManager, + /// The foreground color of the text pub text_color: RGBA4, + /// The maximum lines of text to keep, excluding the active line pub history_limit: u16, + /// The maximum number of chars per line of text. pub line_max_chars: u16, + /// Controls whether the console will automatically wrap to the next line pub line_wrap: bool, scrollback_history: alloc::collections::VecDeque, - pub scrollback_history_offset: u16, + scrollback_history_offset: u16, current_line: String, + /// Scale of the text when drawing pub scale: u8, } @@ -245,6 +272,7 @@ pub mod scrollback { unsafe impl Sync for ScrollbackConsole {} impl ScrollbackConsole { + /// Create a new instance of the console. #[inline(always)] pub fn new( gpu_ctx: Arc>, @@ -273,6 +301,9 @@ pub mod scrollback { }) } + /// Attempts to scroll up through the scroll buffer. + /// + /// Only takes affect if there are more lines of text than can be displayed on the screen. #[inline(always)] pub fn scroll_up(&mut self) { let max_line_count = self.max_line_count(); @@ -286,6 +317,10 @@ pub mod scrollback { } } + /// Attempts to scroll down through the scroll buffer + /// + /// Only takes affect if there are more lines of text than can be displayed on the screen, + /// and the current scroll location is not at the most recent line. #[inline(always)] pub fn scroll_down(&mut self) { self.scrollback_history_offset = self.scrollback_history_offset.saturating_sub(1); @@ -316,6 +351,10 @@ pub mod scrollback { } } + /// Writes a pre-formatted line directly to the history, bypassing the current line + /// + /// Panics if the line length is longer then the maximum displayable characters in a line, + /// or if the string contains a newline character. #[inline(always)] pub fn push_history_line(&mut self, line: String) { let real_max_len = @@ -344,6 +383,7 @@ pub mod scrollback { } } + /// Writes a string to the console buffer. pub fn write(&mut self, text: impl AsRef) { let mut text = text.as_ref(); @@ -358,11 +398,13 @@ pub mod scrollback { (self.canvas.surface.height() - 4) / (10 * self.scale as u32) } + /// Renders the console to the screen. pub fn draw(&mut self) -> Result<()> { let max_line_count = self.max_line_count(); self.canvas.render(Some(RGBA4::new()), |canvas| { let mut line_y = 2 + 8 * self.scale as i32; // leave a bit of a gap + // We take one more from the history if we're not displaying the current line let max_history_lines = if self.scrollback_history_offset == 0 { max_line_count - 1 } else { @@ -408,6 +450,7 @@ pub mod scrollback { }) } + /// Wait for a vsync event to ensure that the previously submitted frame has been fully rendered to the display. #[inline(always)] pub fn wait_vsync_event(&self, timeout: Option) -> Result<()> { self.canvas.wait_vsync_event(timeout) diff --git a/src/elf.rs b/src/elf.rs index 3e701e9e8..ff1a44969 100755 --- a/src/elf.rs +++ b/src/elf.rs @@ -88,6 +88,7 @@ pub struct Rel { /// Represents an ELF Rela type. #[derive(Copy, Clone)] #[repr(C)] +#[allow(missing_docs)] pub struct Rela { pub offset: usize, pub info: Info, @@ -161,21 +162,16 @@ pub unsafe fn relocate_with_dyn(base_address: *mut u8, start_dyn: *const Dyn) { /// This is obviously not a great option to use with Rust's upcoming strict/exposed providence APIs, but works fine here as /// the Switch has a single address space and the memory will have a static lifetime that is longer than the currently running code. #[derive(Debug)] -pub struct EhFrameHdrPtr(AtomicUsize); +pub(crate) struct EhFrameHdrPtr(AtomicUsize); impl EhFrameHdrPtr { - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self(AtomicUsize::new(0)) } - pub fn set(&self, val: *const u8) { - self.0.store(val as usize, SeqCst); - } -} - -impl Default for EhFrameHdrPtr { - fn default() -> Self { - Self::new() + /// Stores the pointer to the EhFrameHdr section + pub(crate) fn set(&self, val: *const u8) { + self.0.store(val.expose_provenance(), SeqCst); } } @@ -186,7 +182,7 @@ unsafe impl unwinding::custom_eh_frame_finder::EhFrameFinder for EhFrameHdrPtr { match self.0.load(SeqCst) { 0 => None, ptr => Some(FrameInfo { - text_base: None, + text_base: Some(crate::rrt0::TEXT_BASE_ADDRESS.load(SeqCst)), kind: FrameInfoKind::EhFrameHdr(ptr), }), } diff --git a/src/fs.rs b/src/fs.rs index 9601ba66b..c64564d94 100755 --- a/src/fs.rs +++ b/src/fs.rs @@ -626,6 +626,11 @@ impl FileAccessor { } } +impl core::fmt::Write for FileAccessor { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.write_array(s.as_bytes()).map_err(|_| core::fmt::Error) + } +} /// Represents a wrapper type to simplify directory access pub struct DirectoryAccessor { dir: Arc, diff --git a/src/gpu/canvas.rs b/src/gpu/canvas.rs index 372d355bc..10bf5706b 100644 --- a/src/gpu/canvas.rs +++ b/src/gpu/canvas.rs @@ -339,7 +339,7 @@ impl CanvasManager { runner(&mut canvas) } - /// Renders a pre-preprared buffer of pixels (represented by their color values) directly to the screen. + /// Renders a pre-prepared buffer of pixels (represented by their color values) directly to the screen. /// /// A maximum of `(width * height)` pixels are read from the input buffer line-by-line to the screen. Extra /// pixels are discarded, and no error is returned for partial refreshes. @@ -768,7 +768,10 @@ impl Drop for BufferedCanvas<'_, ColorFormat> { } } -/// A Canvas checked out from the manager where draws are directly writed to the backing framebuffer memory. +/// A Canvas where draws are directly written to the backing framebuffer memory. +/// +/// Users of this object are expected to write to the screen efficiently to manage memory controller pressure, +/// or to accept lower performance for lower memory usage. pub struct UnbufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { slot: i32, base_pointer: usize, diff --git a/src/ipc.rs b/src/ipc.rs index d277f4463..9c8997197 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -956,17 +956,7 @@ impl CommandContext { return Ok((static_desc.get_address(), static_desc.get_size())); } else if OUT { let buf_size = match FIXED_SIZE { - true => sf::Buffer::< - IN, - OUT, - MAP_ALIAS, - POINTER, - FIXED_SIZE, - AUTO_SELECT, - ALLOW_NON_SECURE, - ALLOW_NON_DEVICE, - T, - >::get_expected_size(), + true => core::mem::size_of::(), false => { self.ensure_pointer_size_walker(raw_data_walker); self.pointer_size_walker.advance_get::() as usize diff --git a/src/ipc/sf.rs b/src/ipc/sf.rs index 767ec986d..6f5f8aab9 100644 --- a/src/ipc/sf.rs +++ b/src/ipc/sf.rs @@ -1,7 +1,6 @@ use core::{marker::PhantomData, mem::MaybeUninit}; use super::*; -use crate::util; use alloc::{ string::{String, ToString}, vec::Vec, @@ -55,7 +54,7 @@ impl< unsafe { Self::from_ptr::<'a>( var as *const U as *const u8, - size_of::() / Self::get_expected_size(), + size_of::(), ) } } @@ -86,10 +85,6 @@ impl< T, > { - pub const fn get_expected_size() -> usize { - // Calculate align-padded size of each element in the buffer (in case a type has a larger alignment than its size) - util::const_usize_max(mem::size_of::(), mem::align_of::()) - } // TODO: ensure that sizeof(T) is a multiple of size @@ -102,7 +97,7 @@ impl< pub const unsafe fn new<'a: 'borrow>(addr: *mut u8, size: usize) -> Self { Self { buf: addr as *mut T, - count: size / Self::get_expected_size(), + count: size / core::mem::size_of::(), _lifetime: PhantomData, } } @@ -178,7 +173,7 @@ impl< } pub const fn get_size(&self) -> usize { - self.count * Self::get_expected_size() + self.count * core::mem::size_of::() } pub const fn get_count(&self) -> usize { @@ -309,7 +304,7 @@ impl< unsafe { Self::from_ptr::<'a>( var as *const U as *const _, - size_of::() / Self::get_expected_size(), + size_of::(), ) } } diff --git a/src/ipc/sf/bsd.rs b/src/ipc/sf/bsd.rs index d40b65e81..988ec16a9 100644 --- a/src/ipc/sf/bsd.rs +++ b/src/ipc/sf/bsd.rs @@ -12,7 +12,7 @@ use core::time::Duration as TimeSpec; pub mod rc; -pub const SOL_SOCKET: i32 = (0xffffffff_u32).cast_signed(); +pub const SOL_SOCKET: i32 = 0xffff; #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] @@ -267,7 +267,7 @@ impl BsdResult { //fn convert_errno(_errno: i32) {} }*/ -#[derive(Copy, Clone, Debug, Default, Request, Response)] +#[derive(Copy, Clone, Default, Request, Response)] #[repr(C)] pub struct SocketAddrRepr { /// The actual size in bytes of the Socket. @@ -284,9 +284,18 @@ pub struct SocketAddrRepr { /// The min size we are working with for the true types. The real size is based on `self.actual_length` and `self.family`. pub(crate) _zero: [u8; 8], } - const_assert!(core::mem::size_of::() == 16); +impl core::fmt::Debug for SocketAddrRepr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("SocketAddrRepr") + .field("family", &self.family) + .field("address", &self.addr) + .field("port", &u16::from_be(self.port)) + .finish_non_exhaustive() + } +} + impl FromStr for SocketAddrRepr { type Err = core::net::AddrParseError; fn from_str(s: &str) -> core::result::Result { @@ -748,7 +757,7 @@ pub enum IpProto { ICMP = 1, TCP = 6, UDP = 17, - Socket = 0xffffffff_u32.cast_signed() as _ + Socket = 0xffff } define_bit_set! { diff --git a/src/ipc/sf/hid/shmem.rs b/src/ipc/sf/hid/shmem.rs index 16287edf4..30955cd34 100755 --- a/src/ipc/sf/hid/shmem.rs +++ b/src/ipc/sf/hid/shmem.rs @@ -454,7 +454,6 @@ impl SharedMemoryFormat { /// /// It is the caller's responsibility to make sure the returned struct does not outlive the shared memory mapping. pub unsafe fn from_shmem_ptr(ptr: *const u8) -> Result { - result_return_if!(ptr.is_null() || !ptr.is_aligned_to(8), ResultInvalidAddress); let firmware_version = version::get_version(); // Safety - The calls to `cast()` should be safe as we only access it though the checked `as_ref()` calls, @@ -462,27 +461,27 @@ impl SharedMemoryFormat { unsafe { if SharedMemoryFormatV1::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V1( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV2::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V2( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV3::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V3( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV4::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V4( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV5::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V5( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV6::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V6( - ptr.cast::().as_ref_unchecked(), + ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, )) } else { unreachable!( diff --git a/src/lib.rs b/src/lib.rs index 8dff5c713..e49e4ff5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,9 +41,6 @@ #![no_std] // needed to implement the APIs for collection types with custom allocators, and doing raw allocations #![feature(allocator_api)] -// needed for implementing the mem::Shared type with dyn-compatibility -#![feature(coerce_unsized)] -#![feature(unsize)] // needed to specify weak linkage on some items #![feature(linkage)] // needed for the implementation of the threads module @@ -52,9 +49,6 @@ #![feature(try_blocks)] // used for ergonomics reading UTF16 strings #![feature(str_from_utf16_endian)] -// for manually pre-checked pointer to reference conversion -#![feature(ptr_as_ref_unchecked)] -#![feature(pointer_is_aligned_to)] //#![warn(missing_docs)] @@ -141,7 +135,7 @@ pub mod rand; #[cfg(feature = "la")] pub mod la; -#[cfg(feature = "console")] +#[cfg(any(feature = "console", feature = "vty"))] #[macro_use] pub mod console; diff --git a/src/macros/ipc/client.rs b/src/macros/ipc/client.rs index 10934c139..fd70c103d 100755 --- a/src/macros/ipc/client.rs +++ b/src/macros/ipc/client.rs @@ -6,7 +6,7 @@ /// /// ``` /// -/// // This already creates the client-IPC type and impls IObject and the SF trait below +/// // This already creates the client-IPC type and implements IObject and the SF trait below /// ipc_sf_define_default_client_for_interface!(ExampleInterface); /// /// ipc_sf_define_interface_trait! { @@ -20,7 +20,7 @@ macro_rules! ipc_sf_define_default_client_for_interface { ($t:ident) => { paste::paste! { - /// The default client for the `$t` trait. All implementors of the trait need to read their session in accordance with this Types IPC Parameter traits. + #[doc = concat!("The default client for the `", stringify!($t), "` trait. All implementors of the trait need to read their session in accordance with this Types IPC Parameter traits.")] pub struct $t { #[doc(hidden)] pub (crate) session: $crate::ipc::sf::Session @@ -107,7 +107,7 @@ macro_rules! ipc_sf_define_default_client_for_interface { #[macro_export] macro_rules! ipc_client_define_client_default { ($t:ident) => { - /// Default client object for the $t service + #[doc = concat!("Default client object for the `", stringify!($t), "` service")] #[allow(missing_docs)] pub struct $t { pub(crate) session: $crate::ipc::sf::Session, diff --git a/src/rrt0.rs b/src/rrt0.rs index 9ad91a821..276a8a33b 100644 --- a/src/rrt0.rs +++ b/src/rrt0.rs @@ -48,6 +48,7 @@ use crate::{ipc::sf, service, service::set}; use core::arch::asm; use core::mem; use core::ptr; +use core::sync::atomic::AtomicUsize; use atomic_enum::atomic_enum; @@ -138,6 +139,7 @@ pub fn get_module_name() -> ModulePath { static G_EXIT_FN: sync::Mutex> = sync::Mutex::new(None); static G_MAIN_THREAD: sync::Mutex> = sync::Mutex::new(None); +pub (crate) static TEXT_BASE_ADDRESS: AtomicUsize = AtomicUsize::new(0); static EH_FRAME_HDR_SECTION: elf::EhFrameHdrPtr = elf::EhFrameHdrPtr::new(); /// Exits the current process @@ -376,6 +378,7 @@ unsafe extern "C" fn __nx_rrt0_entry(arg0: usize, arg1: usize) -> ! { mod0.zero_bss_section(); let eh_hdr_ptr_start = mod0.get_eh_frame_header_start(); + TEXT_BASE_ADDRESS.store(self_base_address.expose_provenance(), core::sync::atomic::Ordering::Relaxed); EH_FRAME_HDR_SECTION.set(eh_hdr_ptr_start); let _ = unwinding::custom_eh_frame_finder::set_custom_eh_frame_finder(&EH_FRAME_HDR_SECTION); diff --git a/src/socket.rs b/src/socket.rs index f12463ddd..a775bee2c 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -666,8 +666,8 @@ pub mod net { let socket_server = socket_server_handle .as_ref() .ok_or(rc::ResultNotInitialized::make())?; + let ipaddr = SocketAddrRepr::from((ip, port)); - //let ipaddr = SocketAddrRepr::from_str(ipaddr).map_err(|_| rc::ResultInvalidSocketString::make())?; let listenfd = match socket_server.service.socket( super::SocketDomain::INet, super::SocketType::Stream, diff --git a/src/svc.rs b/src/svc.rs index 62b2a9378..ac746a801 100755 --- a/src/svc.rs +++ b/src/svc.rs @@ -595,9 +595,9 @@ pub fn wait_synchronization_one(handle: Handle, timeout: i64) -> Result<()> { /// /// This doesn't take force-pause (activity/debug pause) into account. #[inline(always)] -pub fn cancel_synchronisation(thread_handle: Handle) -> Result<()> { +pub fn cancel_synchronization(thread_handle: Handle) -> Result<()> { unsafe { - let rc = asm::cancel_synchronisation(thread_handle); + let rc = asm::cancel_synchronization(thread_handle); pack(rc, ()) } } @@ -656,7 +656,7 @@ pub unsafe fn connect_to_named_port(name: &core::ffi::CStr) -> Result { unsafe { let mut handle: Handle = 0; - let rc = asm::connect_to_named_port(&mut handle, name.as_ptr()); + let rc = asm::connect_to_named_port(&mut handle, name.as_ptr().cast()); pack(rc, handle) } } @@ -745,7 +745,7 @@ pub fn r#break(reason: BreakReason, debug_data: &[u8]) -> Result<()> { #[inline(always)] pub unsafe fn output_debug_string(msg: &core::ffi::CStr) -> Result<()> { unsafe { - let rc = asm::output_debug_string(msg.as_ptr(), msg.count_bytes()); + let rc = asm::output_debug_string(msg.as_ptr().cast(), msg.count_bytes()); pack(rc, ()) } } @@ -809,7 +809,7 @@ pub unsafe fn map_physical_memory(address: Address, len: Size) -> Result<()> { } } -/// Unmaps memorry mapped by [`map_physical_memory`]. [3.0.0+] +/// Unmaps memory mapped by [`map_physical_memory`]. [3.0.0+] #[inline(always)] pub unsafe fn unmap_physical_memory(address: Address, len: Size) -> Result<()> { unsafe { @@ -1097,7 +1097,7 @@ pub fn continue_debug_event(debug_handle: Handle, flags: u32, thread_ids: &[u64] } } -///Retrieves a list of all running processes. Returns the hnumber of PIDs written to the buffer. +///Retrieves a list of all running processes. Returns the number of PIDs written to the buffer. #[inline(always)] pub fn get_process_list(process_id_list: &mut [u64]) -> Result { unsafe { @@ -1112,7 +1112,7 @@ pub fn get_process_list(process_id_list: &mut [u64]) -> Result { } } -/// Retrieves a list of all threads for a debug handle (or zero). Returns the hnumber of thread IDs written to the buffer. +/// Retrieves a list of all threads for a debug handle (or zero). Returns the number of thread IDs written to the buffer. /// /// This is a privileged syscall and may not be available. #[inline(always)] diff --git a/src/svc/asm.rs b/src/svc/asm.rs index 7ce47f06c..9e07c01df 100644 --- a/src/svc/asm.rs +++ b/src/svc/asm.rs @@ -322,7 +322,7 @@ pub unsafe extern "C" fn wait_synchronization( } #[unsafe(naked)] -pub unsafe extern "C" fn cancel_synchronisation(handle: Handle) -> ResultCode { +pub unsafe extern "C" fn cancel_synchronization(handle: Handle) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), "svc 0x19", diff --git a/src/util.rs b/src/util.rs index 4afab3803..9f824d83a 100755 --- a/src/util.rs +++ b/src/util.rs @@ -144,6 +144,7 @@ pub(crate) const fn const_usize_min(a: usize, b: usize) -> usize { // TODO: const min traits if a > b { b } else { a } } +#[allow(dead_code)] pub(crate) const fn const_usize_max(a: usize, b: usize) -> usize { // TODO: const min traits if a < b { b } else { a } From f78fcaaf9f50241c0dac63bc8f3fbcfe5d5dbce0 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Fri, 4 Jul 2025 10:58:17 +0930 Subject: [PATCH 15/19] Finish implementing asm.rs and update the readme to match. --- README.md | 12 +- src/svc.rs | 303 +++++++++---- src/svc/asm.rs | 1104 +++++++++++++++++++++++++++++++----------------- 3 files changed, 938 insertions(+), 481 deletions(-) diff --git a/README.md b/README.md index 7a4840155..a510fa788 100755 --- a/README.md +++ b/README.md @@ -57,26 +57,16 @@ - NRO Romfs support -- Finish implementing all SVCs +- Finish implementing all SVC wrappers. - Actual hw-rendering? (maybe as a separate lib like [deko3d](https://github.com/devkitPro/deko3d)?) - Finish SMC support -- Finish waitable support - -- Improve library applet support (specific implementations, etc.) - - Optimize IPC code to generate even better asm (like libnx or nnsdk) - Finish documenting still-undocumented modules (`ipc`, `svc` and `service`) -- Console support - -- `std` support - -- (low priority) 32-bit support (see the corresponding branch) - ## Credits - [libnx](https://github.com/switchbrew/libnx) and its contributors for being the base of this project. diff --git a/src/svc.rs b/src/svc.rs index ac746a801..dd7e62841 100755 --- a/src/svc.rs +++ b/src/svc.rs @@ -6,6 +6,7 @@ use crate::arm; use crate::ipc::sf::ncm; use crate::result::*; use crate::util; +use crate::util::ArrayString; use core::mem; use core::ptr; @@ -42,6 +43,15 @@ pub enum BreakReason { NotificationOnlyFlag = 0x80000000, } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u32)] +pub enum CodeMapOperation { + MapOwner = 0, + MapSlave, + UnmapOwner, + UnmapSlave, +} + #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] #[repr(u32)] pub enum MemoryState { @@ -106,30 +116,190 @@ pub struct MemoryInfo { #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(u32)] pub enum InfoId { + /// Bitmask of allowed Core IDs. CoreMask = 0, + /// Bitmask of allowed Thread Priorities. PriorityMask = 1, + /// Base of the Alias memory region. AliasRegionAddress = 2, + /// Size of the Alias memory region. AliasRegionSize = 3, + /// Base of the Heap memory region. HeapRegionAddress = 4, + /// Size of the Heap memory region. HeapRegionSize = 5, + /// Total amount of memory available for process. TotalMemorySize = 6, + /// Amount of memory currently used by process. UsedMemorySize = 7, + /// Whether current process is being debugged. DebuggerAttached = 8, + /// Current process's resource limit handle. ResourceLimit = 9, + /// Number of idle ticks on CPU. IdleTickCount = 10, + /// [2.0.0+] Random entropy for current process. RandomEntropy = 11, + /// [2.0.0+] Base of the process's address space. AslrRegionAddress = 12, + /// [2.0.0+] Size of the process's address space. AslrRegionSize = 13, + /// [2.0.0+] Base of the Stack memory region. StackRegionAddress = 14, + /// [2.0.0+] Size of the Stack memory region. StackRegionSize = 15, + /// [3.0.0+] Total memory allocated for process memory management. SystemResourceSizeTotal = 16, + /// [3.0.0+] Amount of memory currently used by process memory management. SystemResourceSizeUsed = 17, + /// [3.0.0+] Program ID for the process. ProgramId = 18, + /// [4.0.0-4.1.0] Min/max initial process IDs. InitialProcessIdRange = 19, + /// [5.0.0+] Address of the process's exception context (for break). UserExceptionContextAddress = 20, + /// [6.0.0+] Total amount of memory available for process, excluding that for process memory management. TotalNonSystemMemorySize = 21, + /// [6.0.0+] Amount of memory used by process, excluding that for process memory management. UsedNonSystemMemorySize = 22, + /// [9.0.0+] Whether the specified process is an Application. IsApplication = 23, + /// [11.0.0+] The number of free threads available to the process's resource limit. + FreeThreadCount = 24, + /// [13.0.0+] Number of ticks spent on thread. + ThreadTickCount = 25, + /// [14.0.0+] Does process have access to SVC (only usable with \ref svcSynchronizePreemptionState at present). + IsSvcPermitted = 26, + /// [16.0.0+] Low bits of the physical address for a KIoRegion. + IoRegionHint = 27, + /// [18.0.0+] Extra size added to the reserved region. + AliasRegionExtraSize = 28, + /// [19.0.0+] Low bits of the process address for a KTransferMemory. + TransferMemoryHint = 34, + /// [1.0.0-12.1.0] Number of ticks spent on thread. + ThreadTickCountDeprecated = 0xF0000002, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u64)] +pub enum SystemInfoParam { + /// Total amount of DRAM available to system. + TotalPhysicalMemorySize = 0, + /// Current amount of DRAM used by system. + UsedPhysicalMemorySize = 1, + /// Min/max initial process IDs. + InitialProcessIdRange = 2, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +#[repr(u8)] +pub enum AddressSpaceType { + ThirtyTwoBit = 0, + SixtyFourBitDeprecated = 1, + ThirtyTwoBitWithoutAlias = 2, + SixtyFourBit = 3, + #[default] + Mask = 0x7 +} + +impl AddressSpaceType { + const fn into_bits(self) -> u8 { + self as _ + } + + const fn from_bits(val: u8) -> Self { + match val { + 0..=3 => unsafe {core::mem::transmute(val)}, + _ => Self::Mask + } + } +} + + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +#[repr(u8)] +pub enum MemoryPoolType { + Application = 0, + Applet = 1, + System = 2, + SystemNonSecure = 3, + #[default] + Mask = 0xF +} + +impl MemoryPoolType { + const fn into_bits(self) -> u8 { + self as _ + } + + const fn from_bits(val: u8) -> Self { + match val { + 0..=3 => unsafe {core::mem::transmute(val)}, + _ => Self::Mask + } + } +} + +#[bitfield_struct::bitfield(u32, order = Lsb)] +pub struct CreateProcessFlags { + pub is_64bit: bool, + #[bits(3, default = AddressSpaceType::Mask)] + pub address_space_flags: AddressSpaceType, + pub enable_debug: bool, + pub enable_aslr: bool, + pub is_application: bool, + #[bits(4, default = MemoryPoolType::Mask)] + pub memory_pool_type: MemoryPoolType, + pub optimise_memory_allocation: bool, + pub disable_device_address_space_merge: bool, + pub alias_region_extra_size: bool, + #[bits(18)] + _unused: u32, +} + +impl CreateProcessFlags { + pub const fn all() -> Self { + Self::new() + .with_is_64bit(true) + .with_address_space_flags(AddressSpaceType::Mask) + .with_enable_debug(true) + .with_enable_aslr(true) + .with_is_application(true) + .with_memory_pool_type(MemoryPoolType::Mask) + .with_optimise_memory_allocation(true) + .with_disable_device_address_space_merge(true) + .with_alias_region_extra_size(true) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct CreateProcessInfo { + pub name: ArrayString<12>, + pub version: u32, + pub program_id: u64, + pub code_address: usize, + pub code_num_pages: i32, + pub flags: u32, + pub resource_limit_handle: Handle, + pub system_resource_page_count: i32, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +pub enum DebugThreadParam { + ActualPriority = 0, + State = 1, + IdealCore = 2, + CurrentCore = 3, + CoreMask = 4, +} +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct PhysicalMemoryInfo { + physical_address: usize, + virtual_address: usize, + size: usize, } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] @@ -271,11 +441,11 @@ pub enum LimitableResource { #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(C)] -/// Thread Activity. -pub enum ThreadActivity { - /// Thread can run. +/// Thread/Process Scheduler State. +pub enum SchedulerState { + /// Can be scheduled. Runnable = 0, - /// Thread is paused. + /// Will not be scheduled. Paused = 1, } @@ -344,11 +514,7 @@ pub unsafe fn set_memory_attribute(address: Address, size: Size, set_uncached: b /// Source range gets reprotected to [`MemoryAttribute::None()`] (it can no longer be accessed), /// and [`MemoryAttribute::Borrowed()`] is set in the source page's [`MemoryAttribute`]. #[inline(always)] -pub unsafe fn map_memory( - address: Address, - source_address: MutAddress, - size: Size, -) -> Result<()> { +pub unsafe fn map_memory(address: Address, source_address: MutAddress, size: Size) -> Result<()> { unsafe { let rc = asm::map_memory(address, source_address, size); pack(rc, ()) @@ -357,11 +523,7 @@ pub unsafe fn map_memory( /// Unmaps a region that was previously mapped with [`map_memory`] #[inline(always)] -pub unsafe fn unmap_memory( - address: Address, - source_address: MutAddress, - size: Size, -) -> Result<()> { +pub unsafe fn unmap_memory(address: Address, source_address: MutAddress, size: Size) -> Result<()> { unsafe { let rc = asm::unmap_memory(address, source_address, size); pack(rc, ()) @@ -390,7 +552,7 @@ pub fn exit_process() -> ! { } /// Creates a thread. -/// +/// /// The pointer to the thread arguments and stack memory _must_ live at least as long as the thread is alive. #[inline(always)] pub unsafe fn create_thread( @@ -459,7 +621,6 @@ pub fn set_thread_priority(handle: Handle, priority: i32) -> Result<()> { } } - /// Gets a thread's core mask. #[inline(always)] pub fn get_thread_core_mask(handle: Handle) -> Result<(i32, u64)> { @@ -528,7 +689,7 @@ pub unsafe fn unmap_shared_memory(handle: Handle, address: Address, size: Size) } /// Creates a block of transfer memory. -/// +/// /// The memory will be reprotected with `permissions` after creation (usually set to none). /// The original memory permissions will be restored when the handle is closed. #[inline(always)] @@ -565,16 +726,14 @@ pub fn reset_signal(handle: Handle) -> Result<()> { } /// Waits on one or more synchronization objects, optionally with a timeout. -/// +/// /// The max number of handles is `0x40` (64). This is a Horizon kernel limitation. #[inline(always)] -pub unsafe fn wait_synchronization( - handles: &[Handle], - timeout: i64, -) -> Result { +pub unsafe fn wait_synchronization(handles: &[Handle], timeout: i64) -> Result { unsafe { let mut index: i32 = 0; - let rc = asm::wait_synchronization(&mut index, handles.as_ptr(), handles.len() as u32, timeout); + let rc = + asm::wait_synchronization(&mut index, handles.as_ptr(), handles.len() as u32, timeout); pack(rc, index) } } @@ -592,8 +751,8 @@ pub fn wait_synchronization_one(handle: Handle, timeout: i64) -> Result<()> { /// If the referenced thread is currently in a synchronization call ([`wait_synchronization`], [`reply_and_receive`] /// or [`reply_and_receive_light`]), that call will be interrupted and return `0xec01`([`ResultCancelled`][`rc::ResultCancelled`]) . /// If that thread is not currently executing such a synchronization call, the next call to a synchronization call will return `0xec01``. -/// -/// This doesn't take force-pause (activity/debug pause) into account. +/// +/// This doesn't take force-pause (activity/debug pause) into account. #[inline(always)] pub fn cancel_synchronization(thread_handle: Handle) -> Result<()> { unsafe { @@ -680,13 +839,10 @@ pub fn send_sync_request(handle: Handle) -> Result<()> { } /// Sends an IPC synchronization request to a session from an user allocated buffer. -/// +/// /// The buffer size must be a multiple of the system page size (0x1000). #[inline(always)] -pub unsafe fn send_sync_request_with_user_data( - buffer: &mut [u8], - handle: Handle, -) -> Result<()> { +pub unsafe fn send_sync_request_with_user_data(buffer: &mut [u8], handle: Handle) -> Result<()> { unsafe { let rc = asm::send_sync_request_with_user_data(buffer.as_mut_ptr(), buffer.len(), handle); pack(rc, ()) @@ -694,7 +850,7 @@ pub unsafe fn send_sync_request_with_user_data( } /// Sends an IPC synchronization request to a session from an user allocated buffer (asynchronous version). -/// +/// /// The buffer size must be a multiple of the system page size (0x1000). #[inline(always)] pub unsafe fn send_async_request_with_user_data( @@ -703,7 +859,12 @@ pub unsafe fn send_async_request_with_user_data( ) -> Result { unsafe { let mut out_handle = 0; - let rc = asm::send_async_request_with_user_data(&mut out_handle, buffer.as_mut_ptr(), buffer.len(), session); + let rc = asm::send_async_request_with_user_data( + &mut out_handle, + buffer.as_mut_ptr(), + buffer.len(), + session, + ); pack(rc, out_handle) } } @@ -731,7 +892,7 @@ pub fn get_thread_id(handle: Handle) -> Result { } /// Breaks execution -/// +/// /// The `debug_data` buffer is passed to a debugging instance if one is attached. #[inline(always)] pub fn r#break(reason: BreakReason, debug_data: &[u8]) -> Result<()> { @@ -753,13 +914,11 @@ pub unsafe fn output_debug_string(msg: &core::ffi::CStr) -> Result<()> { /// Returns from an exception. #[inline(always)] pub fn return_from_exception(res: ResultCode) -> ! { - unsafe { - asm::return_from_exception(res); - } + unsafe { asm::return_from_exception(res) } } /// Retrieves information about the system, or a certain kernel object, depending on the value of `id`. -/// +/// /// `handle` is for particular kernel objects, but `INVALID_HANDLE` is used to retrieve information about the system. #[inline(always)] pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { @@ -773,9 +932,9 @@ pub fn get_info(id: InfoId, handle: Handle, sub_id: u64) -> Result { /* /// Flushes the entire data cache (by set/way). -/// +/// /// This is a privileged syscall and may not be available. -/// +/// /// This syscall has dangerous side effects and should not be used. #[inline(always)] #[deprecated] @@ -789,7 +948,7 @@ pub unsafe fn flush_entire_data_cache() -> Result<()> { */ /// Flushes data cache for a virtual address range. -/// +/// /// [`cache_flush`][`crate::arm::cache_flush`] should be used instead whenever possible. #[inline(always)] #[deprecated] @@ -819,10 +978,10 @@ pub unsafe fn unmap_physical_memory(address: Address, len: Size) -> Result<()> { } /// Gets information about a thread that will be scheduled in the future. [5.0.0+] -/// +/// /// `ns` is the nanoseconds in the future when the thread information will be sampled /// by the kernel. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_future_thread_info( @@ -855,7 +1014,7 @@ pub fn get_last_thread_info() -> Result<(LastThreadContext, u64, u32)> { } /// Gets the maximum value a LimitableResource can have, for a Resource Limit handle. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_resource_limit_limit_value( @@ -871,7 +1030,7 @@ pub fn get_resource_limit_limit_value( } /// Gets the current value a LimitableResource has, for a Resource Limit handle. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_resource_limit_current_value( @@ -888,7 +1047,7 @@ pub fn get_resource_limit_current_value( /// Pauses/unpauses a thread. #[inline(always)] -pub fn set_thread_activity(thread_handle: Handle, thread_state: ThreadActivity) -> Result<()> { +pub fn set_thread_activity(thread_handle: Handle, thread_state: SchedulerState) -> Result<()> { unsafe { let rc = asm::set_thread_activity(thread_handle, thread_state); pack(rc, ()) @@ -943,7 +1102,7 @@ pub unsafe fn synchronize_preemption_state() -> Result<()> { } /// Creates an IPC session. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn create_session(is_light: bool, unk_name: u64) -> Result<(Handle, Handle)> { @@ -968,7 +1127,7 @@ pub fn accept_session(handle: Handle) -> Result { } /// Performs light IPC input/output. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn reply_and_receive_light(handle: Handle) -> Result<()> { @@ -979,7 +1138,7 @@ pub fn reply_and_receive_light(handle: Handle) -> Result<()> { } /// Performs IPC input/output. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn reply_and_receive( @@ -997,7 +1156,7 @@ pub unsafe fn reply_and_receive( } /// Performs IPC input/output on a user-allocated buffer. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn reply_and_receive_with_user_buffer( @@ -1035,7 +1194,7 @@ pub fn create_event() -> Result<(Handle, Handle)> { } /// Debugs an active process. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn debug_active_process(process_id: u64) -> Result { @@ -1047,7 +1206,7 @@ pub fn debug_active_process(process_id: u64) -> Result { } /// Breaks an active debugging session. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn break_debug_process(debug_handle: Handle) -> Result<()> { @@ -1058,7 +1217,7 @@ pub fn break_debug_process(debug_handle: Handle) -> Result<()> { } /// Terminates the process of an active debugging session -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn terminate_debug_process(debug_handle: Handle) -> Result<()> { @@ -1069,7 +1228,7 @@ pub fn terminate_debug_process(debug_handle: Handle) -> Result<()> { } /// Gets an incoming debug event from a debugging session. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_event(debug_handle: Handle) -> Result { @@ -1082,7 +1241,7 @@ pub fn get_debug_event(debug_handle: Handle) -> Result { } /// Continues a debugging session. [3.0.0+] -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn continue_debug_event(debug_handle: Handle, flags: u32, thread_ids: &[u64]) -> Result<()> { @@ -1113,7 +1272,7 @@ pub fn get_process_list(process_id_list: &mut [u64]) -> Result { } /// Retrieves a list of all threads for a debug handle (or zero). Returns the number of thread IDs written to the buffer. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_thread_list(debug_handle: Handle, thread_id_list: &mut [u64]) -> Result { @@ -1131,7 +1290,7 @@ pub fn get_thread_list(debug_handle: Handle, thread_id_list: &mut [u64]) -> Resu } /// Queries the thread context for a thread under debugging. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn get_debug_thread_context( @@ -1143,7 +1302,7 @@ pub fn get_debug_thread_context( let mut thread_context: arm::ThreadContext = Default::default(); let rc = asm::get_debug_thread_context( - (&raw mut thread_context).cast(), + &raw mut thread_context, debug_handle, thread_id, register_group.get(), @@ -1153,7 +1312,7 @@ pub fn get_debug_thread_context( } /// Writes the thread context (scoped by `register_group`) back into a thread under debugging. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn set_debug_thread_context( @@ -1166,18 +1325,17 @@ pub fn set_debug_thread_context( let rc = asm::set_debug_thread_context( debug_handle, thread_id, - &raw const thread_context as *const _, + &raw const thread_context, register_group.get(), ); pack(rc, ()) } } - /// Gets the memory metadata for an address in a process under debugging. -/// +/// /// This is a privileged syscall and may not be available. -/// +/// /// # Safety /// /// null pointers are OK here, as we are just querying the memory's information @@ -1202,7 +1360,7 @@ pub fn query_debug_process_memory( } /// Reads memory from an address in a process under debugging. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn read_debug_process_memory( @@ -1211,13 +1369,18 @@ pub unsafe fn read_debug_process_memory( buffer: &mut [u8], ) -> Result<()> { unsafe { - let rc = asm::read_debug_process_memory(buffer.as_mut_ptr(), debug_handle, read_address, buffer.len()); + let rc = asm::read_debug_process_memory( + buffer.as_mut_ptr(), + debug_handle, + read_address, + buffer.len(), + ); pack(rc, ()) } } /// Reads memory to an address in a process under debugging. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn write_debug_process_memory( @@ -1232,9 +1395,8 @@ pub unsafe fn write_debug_process_memory( } } - /// Creates a named port. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn create_named_port( @@ -1258,7 +1420,7 @@ pub unsafe fn create_named_port( } /// Manages a named port. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn manage_named_port(name: &core::ffi::CStr, max_sessions: i32) -> Result { @@ -1271,7 +1433,7 @@ pub unsafe fn manage_named_port(name: &core::ffi::CStr, max_sessions: i32) -> Re } /// Connects a named port. -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub unsafe fn connect_named_port(client_session: Handle) -> Result { @@ -1283,9 +1445,8 @@ pub unsafe fn connect_named_port(client_session: Handle) -> Result { } } - /// Calls a secure monitor function (TrustZone, EL3). -/// +/// /// This is a privileged syscall and may not be available. #[inline(always)] pub fn call_secure_monitor(mut secmon_args: [u64; 8]) -> [u64; 8] { diff --git a/src/svc/asm.rs b/src/svc/asm.rs index 9e07c01df..0bf56f25b 100644 --- a/src/svc/asm.rs +++ b/src/svc/asm.rs @@ -1,12 +1,13 @@ use core::arch::naked_asm as nasm; -use crate::arm; use crate::macros::util::maybe_cfi; use crate::result::ResultCode; +use crate::svc::{CreateProcessInfo, DebugThreadParam, SystemInfoParam}; +use crate::{arm, svc::PhysicalMemoryInfo}; use super::{ - BreakReason, DebugEvent, Handle, InfoId, LastThreadContext, LimitableResource, MemoryAttribute, - MemoryInfo, MemoryPermission, PageInfo, ThreadActivity, + BreakReason, CodeMapOperation, DebugEvent, Handle, InfoId, LastThreadContext, + LimitableResource, MemoryAttribute, MemoryInfo, MemoryPermission, PageInfo, SchedulerState, }; #[unsafe(naked)] @@ -657,7 +658,7 @@ pub unsafe extern "C" fn get_resource_limit_current_value( #[unsafe(naked)] pub unsafe extern "C" fn set_thread_activity( thread_handle: Handle, - thread_state: ThreadActivity, + thread_state: SchedulerState, ) -> ResultCode { nasm!( maybe_cfi!(".cfi_startproc"), @@ -720,53 +721,79 @@ pub unsafe extern "C" fn synchronize_preemption_states() -> ResultCode { ); } -/* - -get_resource_limit_peak_value -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x37", -"ldr x2, [sp], #16", -"str x1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_io_pool -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x39", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn get_resource_limit_peak_value( + out_value: *mut i64, + resource_limit_handle: Handle, + limit_kind: LimitableResource, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x37", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -create_io_region -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x3A", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn create_io_pool(pool_type: u32) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x39", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -kernel_debug -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x3C", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn create_io_region( + io_region_handle: *mut Handle, + io_pool_handle: Handle, + physical_addres: *mut u8, + size: usize, + permissions: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x3A", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -change_kernel_trace_state -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x3D", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn kernel_debug( + debug_type: u32, + debug_arg0: u64, + debug_arg1: u64, + debug_arg2: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x3C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -*/ +#[unsafe(naked)] +pub unsafe extern "C" fn change_kernel_trace_state(tracing_state: u32) { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x3D", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} #[unsafe(naked)] pub unsafe extern "C" fn create_session( @@ -870,228 +897,411 @@ pub unsafe extern "C" fn create_event( ); } -/* - - -map_io_region -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x46", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_io_region -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x47", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_physical_memory_unsafe -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x48", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_physical_memory_unsafe -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x49", -"ret", -maybe_cfi!(".cfi_endproc"));} - -set_unsafe_limit -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x4A", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_code_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x4B", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -control_code_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x4C", -"ret", -maybe_cfi!(".cfi_endproc"));} - -sleep_system -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x4D", -"ret", -maybe_cfi!(".cfi_endproc"));} - -read_write_register -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x4E", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -set_process_activity -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x4F", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_shared_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x50", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_transfer_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x51", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_transfer_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x52", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_interrupt_event -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x53", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -query_physical_address -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x54", -"ldr x4, [sp], #16", -"stp x1, x2, [x4]", -"str x3, [x4, #16]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -query_io_mapping -{nasm!( -maybe_cfi!(".cfi_startproc"), -"stp x0, x1, [sp, #-16]!", -"svc 0x55", -"ldp x3, x4, [sp], #16", -"str x1, [x3]", -"str x2, [x4]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -legacy_query_io_mapping -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x55", -"ldr x2, [sp], #16", -"str x1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_device_address_space -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x56", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -attach_device_address_space -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x57", -"ret", -maybe_cfi!(".cfi_endproc"));} - -detach_device_address_space -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x58", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_device_address_space_by_force -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x59", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_device_address_space_aligned -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x5A", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_device_address_space -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x5B", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_device_address_space -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x5C", -"ret", -maybe_cfi!(".cfi_endproc"));} - -invalidate_process_data_cache -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x5D", -"ret", -maybe_cfi!(".cfi_endproc"));} - -store_process_data_cache -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x5E", -"ret", -maybe_cfi!(".cfi_endproc"));} - -flush_process_data_cache -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x5F", -"ret", -maybe_cfi!(".cfi_endproc"));} - -*/ +#[unsafe(naked)] +pub unsafe extern "C" fn map_io_region( + io_region_handle: Handle, + address: *mut u8, + size: usize, + permissions: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x46", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_io_region( + io_region_handle: Handle, + address: *mut u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x47", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_physical_memory_unsafe(address: *mut u8, size: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x48", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_physical_memory_unsafe(address: *mut u8, size: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x49", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_unsafe_limit(size: usize) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x4A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_code_memory( + code_memory_handle: *mut Handle, + source_address: *mut u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x4B", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn control_code_memory( + code_memory_handle: Handle, + operation_type: CodeMapOperation, + destination_address: *mut u8, + size: usize, + permission: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x4C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn sleep_system() -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x4D", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn read_write_register( + mmio_val: u32, + register_addres: usize, + read_write_mask: u32, + in_val: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x4E", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_process_activity( + process: Handle, + paused: SchedulerState, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x4F", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_shared_memory( + shmem_handle: *mut Handle, + size: usize, + local_permission: MemoryPermission, + other_permission: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x50", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_transfer_memory( + tmem_handle: Handle, + address: *mut u8, + size: usize, + permissions: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x51", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_transfer_memory( + tmem_handle: Handle, + address: *mut u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x52", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_interrupt_event( + int_handle: *mut Handle, + irq_number: u64, + flags: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x53", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn query_physical_address( + mem_info: PhysicalMemoryInfo, + virtual_address: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x54", + "ldr x4, [sp], #16", + "stp x1, x2, [x4]", + "str x3, [x4, #16]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn query_io_mapping( + virtual_address: *mut usize, + virtual_size: *mut usize, + physical_address: usize, + physical_size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x55", + "ldp x3, x4, [sp], #16", + "str x1, [x3]", + "str x2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn legacy_query_io_mapping( + virtual_address: *mut usize, + physical_address: usize, + physical_size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x55", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_device_address_space( + device_handle: *mut Handle, + device_address: usize, + device_mem_size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x56", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn attach_device_address_space( + device: usize, + device_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x57", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn detach_device_address_space( + device: usize, + device_handle: Handle, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x58", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_device_address_space_by_force( + handle: Handle, + process_handle: Handle, + map_addresss: usize, + device_mem_size: usize, + device_address: usize, + options: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x59", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_device_address_space_aligned( + handle: Handle, + process_handle: Handle, + map_addresss: usize, + device_mem_size: usize, + device_address: usize, + options: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_device_address_space( + mapped_address_size: *mut usize, + handle: Handle, + process_handle: Handle, + map_addresss: usize, + device_mem_size: usize, + device_address: usize, + options: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x5B", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_device_address_space( + handle: Handle, + process_handle: Handle, + map_addresss: usize, + device_mem_size: usize, + device_address: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn invalidate_process_data_cache( + proc_handle: Handle, + address: *const u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5D", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn store_process_data_cache( + proc_handle: Handle, + address: *const u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5E", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn flush_process_data_cache( + proc_handle: Handle, + address: *const u8, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x5F", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} #[unsafe(naked)] pub unsafe extern "C" fn debug_active_process( @@ -1194,7 +1404,7 @@ pub unsafe extern "C" fn get_thread_list( #[unsafe(naked)] pub unsafe extern "C" fn get_debug_thread_context( - thread_context: *mut u8, // *mut arm::ThreadContext + thread_context: *mut arm::ThreadContext, // *mut arm::ThreadContext debug_handle: Handle, thread_id: u64, register_group: u32, @@ -1211,7 +1421,7 @@ pub unsafe extern "C" fn get_debug_thread_context( pub unsafe extern "C" fn set_debug_thread_context( debug_handle: Handle, thread_id: u64, - thread_context: *const u8, // *const arm::ThreadContext + thread_context: *const arm::ThreadContext, register_group: u32, ) -> ResultCode { nasm!( @@ -1270,37 +1480,57 @@ pub unsafe extern "C" fn write_debug_process_memory( ); } -/* - -set_hardware_break_point -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x6C", -"ret", -maybe_cfi!(".cfi_endproc"));} - -get_debug_thread_param -{nasm!( -maybe_cfi!(".cfi_startproc"), -"stp x0, x1, [sp, #-16]!", -"svc 0x6D", -"ldp x3, x4, [sp], #16", -"str x1, [x3]", -"str w2, [x4]", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn set_hardware_break_point( + which: u32, + flags: u64, + value: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x6C", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -get_system_info -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x6F", -"ldr x2, [sp], #16", -"str x1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} +#[unsafe(naked)] +pub unsafe extern "C" fn get_debug_thread_param( + out_64: *mut u64, + out_32: *mut u32, + debug_handle: Handle, + thread_id: u64, + param: DebugThreadParam, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "stp x0, x1, [sp, #-16]!", + "svc 0x6D", + "ldp x3, x4, [sp], #16", + "str x1, [x3]", + "str w2, [x4]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} -*/ +#[unsafe(naked)] +pub unsafe extern "C" fn get_system_info( + out_info: *mut u64, + id0: SystemInfoParam, + handle: Handle, + id1: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x6F", + "ldr x2, [sp], #16", + "str x1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} #[unsafe(naked)] pub unsafe extern "C" fn create_named_port( @@ -1354,105 +1584,181 @@ pub unsafe extern "C" fn connect_to_port(session: *mut Handle, port_handle: Hand } } -/* -set_process_memory_permission -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x73", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_process_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x74", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_process_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x75", -"ret", -maybe_cfi!(".cfi_endproc"));} - -query_process_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x1, [sp, #-16]!", -"svc 0x76", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -map_process_code_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x77", -"ret", -maybe_cfi!(".cfi_endproc"));} - -unmap_process_code_memory -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x78", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_process -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x79", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -start_process -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x7A", -"ret", -maybe_cfi!(".cfi_endproc"));} - -terminate_process -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x7B", -"ret", -maybe_cfi!(".cfi_endproc"));} - -get_process_info -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x7C", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -create_resource_limit -{nasm!( -maybe_cfi!(".cfi_startproc"), -"str x0, [sp, #-16]!", -"svc 0x7D", -"ldr x2, [sp], #16", -"str w1, [x2]", -"ret", -maybe_cfi!(".cfi_endproc"));} - -set_resource_limit_limit_value -{nasm!( -maybe_cfi!(".cfi_startproc"), -"svc 0x7E", -"ret", -maybe_cfi!(".cfi_endproc"));} - - -*/ +#[unsafe(naked)] +pub unsafe extern "C" fn set_process_memory_permission( + process_handle: Handle, + address: usize, + size: usize, + permissions: MemoryPermission, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x73", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_process_memory( + destination_address: *mut u8, + proc_handle: Handle, + source_address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x74", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_process_memory( + destination_address: *mut u8, + proc_handle: Handle, + source_address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x75", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn query_process_memory( + out_memory_info: *mut MemoryInfo, + out_page_info: *mut PageInfo, + proc_handle: Handle, + address: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x1, [sp, #-16]!", + "svc 0x76", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn map_process_code_memory( + proc_handle: Handle, + destination_address: usize, + source_address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x77", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn unmap_process_code_memory( + proc_handle: Handle, + destination_address: usize, + source_address: usize, + size: usize, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x78", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_process( + out_handle: *mut Handle, + proc_info: *const CreateProcessInfo, + capabilities: *const u32, + capability_count: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x79", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn start_process( + proc_handle: Handle, + main_thread_priority: i32, + default_cpu_core: i32, + main_thread_stack_size: u32, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x7A", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn terminate_process(proc_handle: Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x7B", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn get_process_info(out_info: *mut i64) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x7C", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn create_resource_limit(out_handle: *mut Handle) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "str x0, [sp, #-16]!", + "svc 0x7D", + "ldr x2, [sp], #16", + "str w1, [x2]", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn set_resource_limit_limit_value( + resource_limit_handle: Handle, + limit_kind: LimitableResource, + value: u64, +) -> ResultCode { + nasm!( + maybe_cfi!(".cfi_startproc"), + "svc 0x7E", + "ret", + maybe_cfi!(".cfi_endproc") + ); +} #[unsafe(naked)] pub unsafe extern "C" fn call_secure_monitor(args: *mut u64) { From 9695008af86ba0b1ef9b6bf575d3ee5f61601172 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Fri, 4 Jul 2025 13:24:09 +0930 Subject: [PATCH 16/19] Fix more doc generation errors. --- nx-derive/src/ipc_traits.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/nx-derive/src/ipc_traits.rs b/nx-derive/src/ipc_traits.rs index 7cb20c513..595dc5f98 100644 --- a/nx-derive/src/ipc_traits.rs +++ b/nx-derive/src/ipc_traits.rs @@ -36,7 +36,7 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result syn::Result Option<::nx::result::Result<()>> { - let version = ::nx::version::get_version(); - match req_id { - #( - #handle_request_matches - ),* - _ => None - } + /// The dynamic dispatch function that calls into the IPC server functions. This should only be called from the [`ServerManager`][`::nx::ipc::server::ServerManager`] and not from client code. + /// + /// Examples for implementing [`ISessionObject`][`::nx::ipc::server::ISessionObject`] or [`IMitmServerOject`][`::nx::ipc::server::IMitmServerObject`] can be found in the [examples](https://github.com/aarch64-switch-rs/examples/) crate. + fn try_handle_request_by_id(&mut self, req_id: u32, protocol: ::nx::ipc::CommandProtocol, ctx: &mut ::nx::ipc::server::ServerContext) -> Option<::nx::result::Result<()>> { + let version = ::nx::version::get_version(); + match req_id { + #( + #handle_request_matches + ),* + _ => None } + } } }) } From ea73bfd9bb86c1ab1140e97b7d6fc967c380a981 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Wed, 9 Jul 2025 11:05:45 +0930 Subject: [PATCH 17/19] Fix clobbered nfp changes. --- src/ipc/sf/nfp.rs | 40 +++++++++++++++++++++++----------------- src/mii.rs | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/ipc/sf/nfp.rs b/src/ipc/sf/nfp.rs index 1c154a76f..b8d13a045 100644 --- a/src/ipc/sf/nfp.rs +++ b/src/ipc/sf/nfp.rs @@ -43,6 +43,7 @@ pub enum DeviceState { TagRemoved = 3, TagMounted = 4, Unavailable = 5, + Finalized = 6, } #[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug)] @@ -69,17 +70,10 @@ const_assert!(core::mem::size_of::() == 0x4); #[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug)] #[repr(C)] -pub struct TagId { +pub struct TagInfo { pub uuid: [u8; 10], pub uuid_length: u8, pub reserved_1: [u8; 0x15], -} -const_assert!(core::mem::size_of::() == 0x20); - -#[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug)] -#[repr(C)] -pub struct TagInfo { - pub uid: TagId, pub protocol: u32, pub tag_type: u32, pub reserved_2: [u8; 0x30], @@ -112,10 +106,11 @@ const_assert!(core::mem::size_of::() == 0x40); #[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug)] #[repr(C)] pub struct ModelInfo { - pub character_id: [u8; 3], - pub series_id: u8, - pub numbering_id: u16, - pub nfp_type: u8, + pub game_character_id: u16, + pub character_variant: u8, + pub series: u8, + pub model_number: u16, + pub figure_type: u8, pub reserved: [u8; 0x39], } const_assert!(core::mem::size_of::() == 0x40); @@ -140,15 +135,26 @@ pub enum ApplicationAreaVersion { NintendoSwitch = 3, } +#[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug, Default)] +#[repr(u8)] +pub enum ConsoleFamily { + // Note: unofficial name + #[default] + Default = 0, + NintendoWiiU = 1, + Nintendo3DS = 2, + NintendoSwitch = 3, +} + #[derive(Request, Response, Copy, Clone, PartialEq, Eq, Debug)] #[repr(C)] pub struct AdminInfo { - pub app_id: ncm::ProgramId, + pub program_id: ncm::ProgramId, pub access_id: AccessId, - pub terminal_id_crc32_change_counter: u16, + pub crc32_change_counter: u16, pub flags: AdminInfoFlags, - pub unk: u8, - pub app_area_version: ApplicationAreaVersion, + pub tag_type: u8, + pub console_family: ConsoleFamily, pub pad: [u8; 0x7], pub reserved: [u8; 0x28], } @@ -160,7 +166,7 @@ pub struct RegisterInfoPrivate { pub mii_store_data: mii::StoreData, pub first_write_date: Date, pub name: util::ArrayString<41>, - pub font_region: u8, + pub unk: u8, pub reserved: [u8; 0x8E], } const_assert!(core::mem::size_of::() == 0x100); diff --git a/src/mii.rs b/src/mii.rs index e3ea9e5a7..84210bbe2 100644 --- a/src/mii.rs +++ b/src/mii.rs @@ -3,7 +3,7 @@ use crate::result::*; use crate::sync::{Mutex, MutexGuard}; -use crate::service::mii::*; +pub use crate::service::mii::*; static G_STATIC_SRV: Mutex> = Mutex::new(None); static G_DB_SRV: Mutex> = Mutex::new(None); From 052fb35160011878b4969bdaa2d1c7123b62d295 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Wed, 9 Jul 2025 11:47:35 +0930 Subject: [PATCH 18/19] `cargo fmt --all` --- nx-derive/src/ipc_traits.rs | 62 ++++++++++++++++++++++--------------- nx-derive/src/lib.rs | 2 +- src/applet.rs | 4 +-- src/arm.rs | 2 +- src/console.rs | 17 +++++----- src/diag/abort.rs | 4 +-- src/gpu/canvas.rs | 11 +++++-- src/gpu/surface.rs | 12 ++----- src/ipc/sf.rs | 17 ++-------- src/ipc/sf/bsd.rs | 21 ++++++------- src/ipc/sf/hid/shmem.rs | 24 ++++++++++---- src/lib.rs | 2 -- src/macros.rs | 2 +- src/macros/util.rs | 3 +- src/mem.rs | 8 +++-- src/mem/alloc.rs | 3 +- src/rrt0.rs | 7 +++-- src/socket.rs | 38 ++++++++++------------- src/svc.rs | 13 ++++---- src/thread.rs | 2 +- src/wait.rs | 5 +-- 21 files changed, 131 insertions(+), 128 deletions(-) diff --git a/nx-derive/src/ipc_traits.rs b/nx-derive/src/ipc_traits.rs index 595dc5f98..ae0ed5d4b 100644 --- a/nx-derive/src/ipc_traits.rs +++ b/nx-derive/src/ipc_traits.rs @@ -3,11 +3,11 @@ use std::str::FromStr; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ + AngleBracketedGenericArguments, AttrStyle, FnArg, GenericArgument, Path, PathSegment, + ReturnType, TraitItem, TraitItemFn, Type, TypePath, punctuated::Punctuated, spanned::Spanned, token::{Gt, Lt, Mut, PathSep}, - AngleBracketedGenericArguments, AttrStyle, FnArg, GenericArgument, Path, PathSegment, - ReturnType, TraitItem, TraitItemFn, Type, TypePath, }; pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result { @@ -19,20 +19,17 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result syn::Result(tokens.clone())?); } else { - return Err(stringify_error(fn_item.span(), "Only the `ipc_rid`, `version`, `no_wrap_return`, and `return_session` attrs are supported on ipc trait functions (plus doc comments)")); + return Err(stringify_error( + fn_item.span(), + "Only the `ipc_rid`, `version`, `no_wrap_return`, and `return_session` attrs are supported on ipc trait functions (plus doc comments)", + )); } } else if let syn::Attribute { meta: syn::Meta::Path(p), @@ -163,7 +163,10 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result syn::Result syn::Result { return Err(stringify_error( - client_fn.sig.output.span(), - "Only tuple types, paren-wrapped types, or paths are supported for return types", - )); + client_fn.sig.output.span(), + "Only tuple types, paren-wrapped types, or paths are supported for return types", + )); } }, } @@ -367,7 +373,10 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result impl I{}Server + 'static", @@ -376,7 +385,10 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result(FromStr::from_str(out_type_ident.as_str())?)?; } else { - return Err(stringify_error(server_fn.sig.output.span(), "Output type be a raw type name (the base name of the traits) the return type is marked as a session type")); + return Err(stringify_error( + server_fn.sig.output.span(), + "Output type be a raw type name (the base name of the traits) the return type is marked as a session type", + )); } } @@ -489,7 +501,7 @@ pub fn ipc_trait(_args: TokenStream, ipc_trait: TokenStream) -> syn::Result Option<::nx::result::Result<()>> { let version = ::nx::version::get_version(); diff --git a/nx-derive/src/lib.rs b/nx-derive/src/lib.rs index 1e8098c4a..dd4b2324a 100644 --- a/nx-derive/src/lib.rs +++ b/nx-derive/src/lib.rs @@ -2,7 +2,7 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, DeriveInput}; +use syn::{DeriveInput, parse_macro_input}; mod ipc_traits; diff --git a/src/applet.rs b/src/applet.rs index 2e71b6660..751d3aa04 100644 --- a/src/applet.rs +++ b/src/applet.rs @@ -77,8 +77,8 @@ impl ProxyCommon for AppletProxy { } /// global AppletAttribute used for openning the applet proxy for the program -/// -/// TODO - make a better way to override this value +/// +/// TODO - make a better way to override this value #[linkage = "weak"] #[unsafe(export_name = "__nx_applet_attribute")] pub static APPLET_ATTRIBUTE: AppletAttribute = AppletAttribute::zero(); diff --git a/src/arm.rs b/src/arm.rs index 2a6095109..4cafc23b1 100755 --- a/src/arm.rs +++ b/src/arm.rs @@ -182,7 +182,7 @@ pub fn cache_flush(address: *mut u8, size: usize) { "mov x10, x1", "mov w1, #1", "mrs x0, tpidrro_el0", - "strb w1, [x0, #0x104] ",// Set flag at TLR[0x104] for kernel + "strb w1, [x0, #0x104] ", // Set flag at TLR[0x104] for kernel "2:", "dc civac, x8", "add x8, x8, x9", diff --git a/src/console.rs b/src/console.rs index a28c25fae..55621a8bd 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,7 +1,7 @@ //! Console Services /// Virtual TTY functionality -/// +/// /// The types contained are used to create a tty-like environment, that emulate an /// ANSI console (e.g. by wrapping the canvas in a [`embedded_term::TextOnGraphic`]). #[cfg(feature = "vty")] @@ -20,10 +20,11 @@ pub mod vty { pub use embedded_graphics_core::primitives::rectangle::Rectangle; /// Type alias for a drawable text-buffer backed console. - /// + /// /// The console state is stored in a text buffer, and draws are pushed through a Canvas /// implementation that keeps a persistant pixel buffer between draw calls. - pub type TextBufferConsole = embedded_term::Console>; + pub type TextBufferConsole = + embedded_term::Console>; /// Canvas/Framebuffer type that keeps a single buffer that is /// flushed to the display on change. @@ -166,7 +167,7 @@ pub mod vty { #[cfg(feature = "console")] pub mod scrollback { //! Console types that are really just text buffers that you can push data into. - //! + //! //! These types are useful if you want to log data to the screen as text, but can't do edits or backtracking //! like the vty module. @@ -190,7 +191,7 @@ pub mod scrollback { }; /// A channel-like object for sending strings to the console for display. - /// + /// /// When all clones of this object are dropped, the strong count of the inner `Arc` will drop to zero and the `Weak` /// handle in the background thread will no longer be able to upgrade and read the data. This will cause the background thread to exit. #[derive(Clone)] @@ -302,7 +303,7 @@ pub mod scrollback { } /// Attempts to scroll up through the scroll buffer. - /// + /// /// Only takes affect if there are more lines of text than can be displayed on the screen. #[inline(always)] pub fn scroll_up(&mut self) { @@ -318,7 +319,7 @@ pub mod scrollback { } /// Attempts to scroll down through the scroll buffer - /// + /// /// Only takes affect if there are more lines of text than can be displayed on the screen, /// and the current scroll location is not at the most recent line. #[inline(always)] @@ -352,7 +353,7 @@ pub mod scrollback { } /// Writes a pre-formatted line directly to the history, bypassing the current line - /// + /// /// Panics if the line length is longer then the maximum displayable characters in a line, /// or if the string contains a newline character. #[inline(always)] diff --git a/src/diag/abort.rs b/src/diag/abort.rs index 75fd395dc..f6ff9cfd7 100755 --- a/src/diag/abort.rs +++ b/src/diag/abort.rs @@ -77,11 +77,11 @@ fn do_abort(level: AbortLevel, rc: ResultCode) { } else if level == AbortLevel::ProcessExit() { rrt0::exit(rc); } else if level == AbortLevel::SvcBreak() { - let rc =rc.get_value(); + let rc = rc.get_value(); let _ = unsafe { svc::r#break( svc::BreakReason::Panic, - core::slice::from_raw_parts(&raw const rc as *const u8, 4) + core::slice::from_raw_parts(&raw const rc as *const u8, 4), ) }; } diff --git a/src/gpu/canvas.rs b/src/gpu/canvas.rs index 10bf5706b..ef4784ba2 100644 --- a/src/gpu/canvas.rs +++ b/src/gpu/canvas.rs @@ -769,8 +769,8 @@ impl Drop for BufferedCanvas<'_, ColorFormat> { } /// A Canvas where draws are directly written to the backing framebuffer memory. -/// -/// Users of this object are expected to write to the screen efficiently to manage memory controller pressure, +/// +/// Users of this object are expected to write to the screen efficiently to manage memory controller pressure, /// or to accept lower performance for lower memory usage. pub struct UnbufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { slot: i32, @@ -783,7 +783,12 @@ pub struct UnbufferedCanvas<'fb, ColorFormat: sealed::CanvasColorFormat> { impl UnbufferedCanvas<'_, ColorFormat> { /// Gets a mutable reference to the raw framebuffer pub fn raw_buffer(&mut self) -> &mut [ColorFormat::RawType] { - unsafe { core::slice::from_raw_parts_mut(core::ptr::with_exposed_provenance_mut(self.base_pointer), self.buffer_size / core::mem::size_of::()) } + unsafe { + core::slice::from_raw_parts_mut( + core::ptr::with_exposed_provenance_mut(self.base_pointer), + self.buffer_size / core::mem::size_of::(), + ) + } } pub fn draw_single(&mut self, x: i32, y: i32, color: ColorFormat, blend: AlphaBlend) { diff --git a/src/gpu/surface.rs b/src/gpu/surface.rs index 791819597..3552af115 100755 --- a/src/gpu/surface.rs +++ b/src/gpu/surface.rs @@ -304,11 +304,7 @@ impl Surface { unsafe { nx::arm::cache_flush(surface.buffer_data.ptr, total_framebuffer_size); - svc::set_memory_attribute( - surface.buffer_data.ptr, - total_framebuffer_size, - true - )?; + svc::set_memory_attribute(surface.buffer_data.ptr, total_framebuffer_size, true)?; } let usage = GraphicsAllocatorUsage::HardwareComposer() @@ -588,11 +584,7 @@ impl Drop for Surface { let _ = self.do_ioctl(&mut ioctl_free); unsafe { - svc::set_memory_attribute( - self.buffer_data.ptr, - self.buffer_data.layout.size(), - false - ) + svc::set_memory_attribute(self.buffer_data.ptr, self.buffer_data.layout.size(), false) }; let mut gpu_guard = self.gpu_ctx.write(); diff --git a/src/ipc/sf.rs b/src/ipc/sf.rs index 6f5f8aab9..886144d17 100644 --- a/src/ipc/sf.rs +++ b/src/ipc/sf.rs @@ -51,12 +51,7 @@ impl< > { pub const fn from_other_mut_var<'a: 'borrow, U>(var: &'a mut U) -> Self { - unsafe { - Self::from_ptr::<'a>( - var as *const U as *const u8, - size_of::(), - ) - } + unsafe { Self::from_ptr::<'a>(var as *const U as *const u8, size_of::()) } } } @@ -85,7 +80,6 @@ impl< T, > { - // TODO: ensure that sizeof(T) is a multiple of size /// Creates a `Buffer` from raw parts @@ -245,7 +239,7 @@ impl< const AUTO_SELECT: bool, const ALLOW_NON_SECURE: bool, const ALLOW_NON_DEVICE: bool, - T + T, > Buffer< 'borrow, @@ -301,12 +295,7 @@ impl< > { pub const fn from_other_var<'a: 'borrow, U>(var: &'a U) -> Self { - unsafe { - Self::from_ptr::<'a>( - var as *const U as *const _, - size_of::(), - ) - } + unsafe { Self::from_ptr::<'a>(var as *const U as *const _, size_of::()) } } } diff --git a/src/ipc/sf/bsd.rs b/src/ipc/sf/bsd.rs index 988ec16a9..35d286898 100644 --- a/src/ipc/sf/bsd.rs +++ b/src/ipc/sf/bsd.rs @@ -1,6 +1,7 @@ use nx_derive::{Request, Response}; -use crate::ipc::sf::{CopyHandle, InAutoSelectBuffer, InOutAutoSelectBuffer, OutAutoSelectBuffer, OutMapAliasBuffer, +use crate::ipc::sf::{ + CopyHandle, InAutoSelectBuffer, InOutAutoSelectBuffer, OutAutoSelectBuffer, OutMapAliasBuffer, ProcessId, }; use crate::result::Result; @@ -289,10 +290,10 @@ const_assert!(core::mem::size_of::() == 16); impl core::fmt::Debug for SocketAddrRepr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("SocketAddrRepr") - .field("family", &self.family) - .field("address", &self.addr) - .field("port", &u16::from_be(self.port)) - .finish_non_exhaustive() + .field("family", &self.family) + .field("address", &self.addr) + .field("port", &u16::from_be(self.port)) + .finish_non_exhaustive() } } @@ -434,7 +435,6 @@ pub enum ShutdownMode { Bidirectional = 2, } - /// `get/set_sock_opt` options for level `IpProto::IP` #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] @@ -493,7 +493,6 @@ pub enum IpOptions { OriginalDestinationAddress = 27, } - /// `get/set_sock_opt` options for level `SOL_SOCKET` #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] @@ -570,7 +569,6 @@ pub enum SocketOptions { MaxPacingRate = 0x1018, } - #[derive(Clone, Copy, Debug, Request, Response)] #[repr(C)] pub enum TcpOptions { @@ -616,9 +614,9 @@ pub struct Linger { #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] -pub struct IpMulticastRequest{ +pub struct IpMulticastRequest { pub multicast_addr: Ipv4Addr, - pub interface_addr: Ipv4Addr + pub interface_addr: Ipv4Addr, } impl Linger { @@ -738,7 +736,6 @@ pub enum SocketDomain { //INet6 = 28, - not supported? } - /// Valid socket types to create from the bsd service. #[derive(Copy, Clone, Debug, Request, Response)] #[repr(C)] @@ -757,7 +754,7 @@ pub enum IpProto { ICMP = 1, TCP = 6, UDP = 17, - Socket = 0xffff + Socket = 0xffff, } define_bit_set! { diff --git a/src/ipc/sf/hid/shmem.rs b/src/ipc/sf/hid/shmem.rs index 30955cd34..dd1ee906b 100755 --- a/src/ipc/sf/hid/shmem.rs +++ b/src/ipc/sf/hid/shmem.rs @@ -461,27 +461,39 @@ impl SharedMemoryFormat { unsafe { if SharedMemoryFormatV1::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V1( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV2::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V2( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV3::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V3( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV4::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V4( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV5::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V5( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else if SharedMemoryFormatV6::VERSION_INTERVAL.contains(firmware_version) { Ok(Self::V6( - ptr.cast::().as_ref().ok_or(ResultInvalidAddress::make())?, + ptr.cast::() + .as_ref() + .ok_or(ResultInvalidAddress::make())?, )) } else { unreachable!( diff --git a/src/lib.rs b/src/lib.rs index e49e4ff5e..92f9a6ec6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,9 +49,7 @@ #![feature(try_blocks)] // used for ergonomics reading UTF16 strings #![feature(str_from_utf16_endian)] - //#![warn(missing_docs)] - #![macro_use] use core::arch::global_asm; diff --git a/src/macros.rs b/src/macros.rs index a3f80e1ac..7220e78ca 100755 --- a/src/macros.rs +++ b/src/macros.rs @@ -12,4 +12,4 @@ pub mod result; pub mod rrt0; -pub mod util; \ No newline at end of file +pub mod util; diff --git a/src/macros/util.rs b/src/macros/util.rs index e0a85cc90..e00942c62 100755 --- a/src/macros/util.rs +++ b/src/macros/util.rs @@ -262,7 +262,6 @@ macro_rules! cur_fn_name { }}; } - // CFI directives cannot be used if neither debuginfo nor panic=unwind is enabled. // We don't have an easy way to check the former, so just check based on panic strategy. #[cfg(panic = "abort")] @@ -279,4 +278,4 @@ macro_rules! maybe_cfi { }; } -pub(crate) use maybe_cfi; \ No newline at end of file +pub(crate) use maybe_cfi; diff --git a/src/mem.rs b/src/mem.rs index 06a9bb206..ebf822691 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -21,8 +21,12 @@ pub fn wait_for_permission( let timeout = timeout.unwrap_or(usize::MAX); let mut time_taken: usize = 0; - while !svc::query_memory(address)?.0.permission.intersects(permission) { - result_return_if!( time_taken >= timeout, svc::rc::ResultTimedOut); + while !svc::query_memory(address)? + .0 + .permission + .intersects(permission) + { + result_return_if!(time_taken >= timeout, svc::rc::ResultTimedOut); time_taken = time_taken.saturating_add(100_000); let _ = crate::thread::sleep(100_000); } diff --git a/src/mem/alloc.rs b/src/mem/alloc.rs index b19c30d32..d85f237f9 100644 --- a/src/mem/alloc.rs +++ b/src/mem/alloc.rs @@ -11,13 +11,12 @@ use core::ops::IndexMut; use core::ptr; use core::ptr::NonNull; -use ::alloc::alloc::{Global, Allocator, AllocError, Layout}; +use ::alloc::alloc::{AllocError, Allocator, Global, Layout}; pub const PAGE_ALIGNMENT: usize = 0x1000; pub mod rc; - impl From for ResultCode { fn from(_value: AllocError) -> Self { ResultCode::new(rc::ResultOutOfMemory::get_value()) diff --git a/src/rrt0.rs b/src/rrt0.rs index 276a8a33b..2309ac7f9 100644 --- a/src/rrt0.rs +++ b/src/rrt0.rs @@ -139,7 +139,7 @@ pub fn get_module_name() -> ModulePath { static G_EXIT_FN: sync::Mutex> = sync::Mutex::new(None); static G_MAIN_THREAD: sync::Mutex> = sync::Mutex::new(None); -pub (crate) static TEXT_BASE_ADDRESS: AtomicUsize = AtomicUsize::new(0); +pub(crate) static TEXT_BASE_ADDRESS: AtomicUsize = AtomicUsize::new(0); static EH_FRAME_HDR_SECTION: elf::EhFrameHdrPtr = elf::EhFrameHdrPtr::new(); /// Exits the current process @@ -378,7 +378,10 @@ unsafe extern "C" fn __nx_rrt0_entry(arg0: usize, arg1: usize) -> ! { mod0.zero_bss_section(); let eh_hdr_ptr_start = mod0.get_eh_frame_header_start(); - TEXT_BASE_ADDRESS.store(self_base_address.expose_provenance(), core::sync::atomic::Ordering::Relaxed); + TEXT_BASE_ADDRESS.store( + self_base_address.expose_provenance(), + core::sync::atomic::Ordering::Relaxed, + ); EH_FRAME_HDR_SECTION.set(eh_hdr_ptr_start); let _ = unwinding::custom_eh_frame_finder::set_custom_eh_frame_finder(&EH_FRAME_HDR_SECTION); diff --git a/src/socket.rs b/src/socket.rs index a775bee2c..7f1054d42 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -155,7 +155,7 @@ pub mod net { service::bsd::{BsdResult, ReadFlags, SendFlags, SocketAddrRepr}, }; - pub mod traits { + pub mod traits { use super::*; /// Trait for making a bsd-fd wrapper type usable in `super::poll` @@ -170,9 +170,9 @@ pub mod net { } /// Contains common functions for bsd-compatible socket-like types - /// + /// /// # Safety - /// + /// /// Implementors are responsible for synchonising any interior mutablility for types, if any exists. pub unsafe trait SocketCommon { /// gets the raw file descriptor for the type @@ -184,12 +184,12 @@ pub mod net { Self: Sized; /// Creates a new independently owned handle to the underlying socket. - /// + /// /// The returned object is a references the same stream that this /// object references. Both handles will read and write the same stream of /// data, and options set on one stream will be propagated to the other /// stream. - /// + /// /// This function is also why objects implementing this trait _should not_ contain any methods requiring mutable references. /// Consumers should expect that calls to these functions are synchronized by the implementation. fn try_clone(&self) -> Result @@ -197,7 +197,7 @@ pub mod net { Self: Sized; /// Reads data from the remote side into the provided buffer. - /// + /// /// Immediately returns an error if the socket is not connected. fn recv(&self, data: &mut [u8]) -> Result { let socket_server_handle = BSD_SERVICE.read(); @@ -367,7 +367,7 @@ pub mod net { } /// Sets the value for the `IP_TTL` option on this socket. - /// + /// /// This value sets the time-to-live field that is used in every packet sent /// from this socket. fn set_ttl(&self, ttl: u32) -> Result<()> { @@ -411,13 +411,13 @@ pub mod net { } /// Moves this TCP stream into or out of nonblocking mode. - /// + /// /// This will result in `read`, `write`, `recv` and `send` system operations /// becoming nonblocking, i.e., immediately returning from their calls. /// If the IO operation is successful, `Ok` is returned and no further /// action is required. If the IO operation could not be completed and needs /// to be retried, an error with the value set to `EAGAIN` is - /// returned. + /// returned. fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { const O_NONBLOCK: i32 = 0x4000; @@ -490,7 +490,7 @@ pub mod net { } /// Sets the read timeout to the timeout specified. - /// + /// /// If the value specified is [`None`], then [`SocketCommon::recv`] calls will block /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is /// passed to this method. @@ -517,7 +517,7 @@ pub mod net { } /// Returns the write timeout of this socket. - /// + /// /// If the timeout is [`None`], then [`SocketCommon::send`] calls will block indefinitely. fn send_timeout(&self) -> Result> { let socket_server_handle = BSD_SERVICE.read(); @@ -549,7 +549,7 @@ pub mod net { } /// Sets the write timeout to the timeout specified. - /// + /// /// If the value specified is [`None`], then [`write`] calls will block /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is /// passed to this method. @@ -576,7 +576,7 @@ pub mod net { } /// Gets the value of the `SO_ERROR` option on this socket. - /// + /// /// This will retrieve the stored error in the underlying socket, clearing /// the field in the process. This can be useful for checking errors between /// calls. @@ -666,7 +666,7 @@ pub mod net { let socket_server = socket_server_handle .as_ref() .ok_or(rc::ResultNotInitialized::make())?; - + let ipaddr = SocketAddrRepr::from((ip, port)); let listenfd = match socket_server.service.socket( super::SocketDomain::INet, @@ -955,7 +955,7 @@ pub mod net { /// # Examples /// /// ```no_run - /// + /// /// let socket = UdpSocket::bind(Ipv4Addr::LOCALHOST, Some(34254))?; /// /// // Receives a single datagram message on the socket. If `buf` is too small to hold @@ -967,7 +967,7 @@ pub mod net { /// let buf = &mut buf[..amt]; /// buf.reverse(); /// socket.send_to(buf, (src_ip, src_port))?; - /// + /// /// ``` pub struct UdpSocket(i32); @@ -1165,11 +1165,7 @@ pub mod net { /// Unlike `std::net::UdpSocket`, this method does not return length of the written data. /// All data is sent or an error is returned. #[inline(always)] - pub fn send_to>( - &self, - data: &[u8], - destination: A, - ) -> Result<()> { + pub fn send_to>(&self, data: &[u8], destination: A) -> Result<()> { self.send_to_impl(data, destination.into()) } diff --git a/src/svc.rs b/src/svc.rs index dd7e62841..f79f0b30f 100755 --- a/src/svc.rs +++ b/src/svc.rs @@ -199,7 +199,7 @@ pub enum AddressSpaceType { ThirtyTwoBitWithoutAlias = 2, SixtyFourBit = 3, #[default] - Mask = 0x7 + Mask = 0x7, } impl AddressSpaceType { @@ -209,13 +209,12 @@ impl AddressSpaceType { const fn from_bits(val: u8) -> Self { match val { - 0..=3 => unsafe {core::mem::transmute(val)}, - _ => Self::Mask + 0..=3 => unsafe { core::mem::transmute(val) }, + _ => Self::Mask, } } } - #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] #[repr(u8)] pub enum MemoryPoolType { @@ -224,7 +223,7 @@ pub enum MemoryPoolType { System = 2, SystemNonSecure = 3, #[default] - Mask = 0xF + Mask = 0xF, } impl MemoryPoolType { @@ -234,8 +233,8 @@ impl MemoryPoolType { const fn from_bits(val: u8) -> Self { match val { - 0..=3 => unsafe {core::mem::transmute(val)}, - _ => Self::Mask + 0..=3 => unsafe { core::mem::transmute(val) }, + _ => Self::Mask, } } } diff --git a/src/thread.rs b/src/thread.rs index 35c50f24e..29db5a8b2 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -1418,7 +1418,7 @@ pub fn get_thread_local_region() -> *mut ThreadLocalRegion { maybe_cfi!(".cfi_endproc") ) } - unsafe {__nx_thread_get_thread_local_region()} + unsafe { __nx_thread_get_thread_local_region() } } pub(crate) unsafe fn current() -> *mut imp::Thread { diff --git a/src/wait.rs b/src/wait.rs index fe22f53ba..95c1c2d9d 100755 --- a/src/wait.rs +++ b/src/wait.rs @@ -137,10 +137,7 @@ impl Waiter { type WaitFn = fn(&[W], i64) -> Result; fn handles_wait_fn(handles: &[svc::Handle], timeout: i64) -> Result { - unsafe { - svc::wait_synchronization(handles, timeout) - .map(|idx| idx as usize) - } + unsafe { svc::wait_synchronization(handles, timeout).map(|idx| idx as usize) } } fn waiters_wait_fn(_waiters: &[Waiter], _timeout: i64) -> Result { From 414e0406e05d4b606dca96702e91d0b21f90fe6b Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Wed, 9 Jul 2025 11:48:17 +0930 Subject: [PATCH 19/19] Removes unnecessary nightly feature. --- nx-derive/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/nx-derive/src/lib.rs b/nx-derive/src/lib.rs index dd4b2324a..fbd7139a1 100644 --- a/nx-derive/src/lib.rs +++ b/nx-derive/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(let_chains)] - use proc_macro::TokenStream; use quote::quote; use syn::{DeriveInput, parse_macro_input};