initial implementations for call and call-method
This commit is contained in:
		
							parent
							
								
									cf89db1b52
								
							
						
					
					
						commit
						2604f47bec
					
				
					 7 changed files with 578 additions and 0 deletions
				
			
		
							
								
								
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					/target
 | 
				
			||||||
 | 
					/Cargo.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# as its usage is encouraged for testing architecture-specific code, do not include the .cargo directory
 | 
				
			||||||
 | 
					/.cargo
 | 
				
			||||||
							
								
								
									
										8
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "ofw"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
							
								
								
									
										16
									
								
								examples/client_ppc.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								examples/client_ppc.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					#![no_main]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use ofw::arch::EntryFunction;
 | 
				
			||||||
 | 
					use ofw::{call, service_result, ntstr, OFW_TRUE};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[no_mangle]
 | 
				
			||||||
 | 
					#[link_section = ".text"]
 | 
				
			||||||
 | 
					extern "C" fn _start(_r3: u32, _r4: u32, entry: EntryFunction) -> isize {
 | 
				
			||||||
 | 
					    let mut results = service_result!(1,1);
 | 
				
			||||||
 | 
					    call!(entry, results, ntstr!("test"), 1, 1, ntstr!("test"));
 | 
				
			||||||
 | 
					    if results.success {
 | 
				
			||||||
 | 
					        let _missing: bool = results.rets[0] == OFW_TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/arch/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/arch/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					// # Note to developers:
 | 
				
			||||||
 | 
					// architecture-specific code may be tested via the usage of a filein the following
 | 
				
			||||||
 | 
					// path: `/.cargo/config`. (where `/` is the root of the project)
 | 
				
			||||||
 | 
					// the file should contain the following:
 | 
				
			||||||
 | 
					// ```
 | 
				
			||||||
 | 
					// [build]
 | 
				
			||||||
 | 
					// # for powerpc
 | 
				
			||||||
 | 
					// target = "powerpc-unknown-linux-gnu"
 | 
				
			||||||
 | 
					// ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::Args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # PowerPC
 | 
				
			||||||
 | 
					#[cfg(target_arch = "powerpc")]
 | 
				
			||||||
 | 
					pub type EntryFunction = extern "C" fn (*mut Args) -> i32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # x86_64
 | 
				
			||||||
 | 
					/// note: x86_64 does not have an OpenFirmware implementation, this is for testing purposes only
 | 
				
			||||||
 | 
					#[cfg(target_arch = "x86_64")]
 | 
				
			||||||
 | 
					pub type EntryFunction = extern "C" fn (*mut Args) -> i32;
 | 
				
			||||||
							
								
								
									
										268
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,268 @@
 | 
				
			||||||
 | 
					#![no_std]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # OFW_TRUE
 | 
				
			||||||
 | 
					/// a constant representing the OpenFirmware "true" value
 | 
				
			||||||
 | 
					pub const OFW_TRUE: i32 = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # OFW_FALSE
 | 
				
			||||||
 | 
					/// a constant representing the OpenFirmware "false" value
 | 
				
			||||||
 | 
					pub const OFW_FALSE: i32 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # OFWBoolean
 | 
				
			||||||
 | 
					/// a type representing the OpenFirmware boolean type
 | 
				
			||||||
 | 
					/// can be either OFW_TRUE or OFW_FALSE
 | 
				
			||||||
 | 
					pub type OFWBoolean = i32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # PHandle
 | 
				
			||||||
 | 
					/// handle to a "package" in OpenFirmware
 | 
				
			||||||
 | 
					/// usually used to access properties
 | 
				
			||||||
 | 
					pub type PHandle = i32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # IHandle
 | 
				
			||||||
 | 
					/// handle to a package instance in OpenFirmware
 | 
				
			||||||
 | 
					/// usually used to call methods from a package
 | 
				
			||||||
 | 
					pub type IHandle = i32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::arch::EntryFunction;
 | 
				
			||||||
 | 
					use crate::ntstr::NTSTR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # ntstr
 | 
				
			||||||
 | 
					/// module for handling null-terminated strings
 | 
				
			||||||
 | 
					pub mod ntstr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # arch
 | 
				
			||||||
 | 
					/// module for architecture-specific code
 | 
				
			||||||
 | 
					pub mod arch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # Args
 | 
				
			||||||
 | 
					/// a base structure for all calls to the OpenFirmware entry function,
 | 
				
			||||||
 | 
					/// shouldn't be used directly but rather used as a base for other "service" structures
 | 
				
			||||||
 | 
					/// an example "service struct" is as follows
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// use ofw::{Args, OFWBoolean};
 | 
				
			||||||
 | 
					/// use ofw::ntstr::NTSTR;
 | 
				
			||||||
 | 
					/// /// # test
 | 
				
			||||||
 | 
					/// /// nargs: 1
 | 
				
			||||||
 | 
					/// /// nrets: 1
 | 
				
			||||||
 | 
					/// /// takes a service name string, and returns OFW_TRUE if the service is missing or OFW_FALSE if it is present
 | 
				
			||||||
 | 
					/// #[repr(C)]
 | 
				
			||||||
 | 
					/// pub struct TestService {
 | 
				
			||||||
 | 
					///    pub args: Args,
 | 
				
			||||||
 | 
					///    pub service: NTSTR,
 | 
				
			||||||
 | 
					///    pub missing: OFWBoolean,
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[repr(C)]
 | 
				
			||||||
 | 
					pub struct Args {
 | 
				
			||||||
 | 
					    pub service: NTSTR,
 | 
				
			||||||
 | 
					    // ieee 1275, 2.4.1 General conventions: "All numbers on the stack are signed integers, _32 bits[...]"
 | 
				
			||||||
 | 
					    pub nrets: i32,
 | 
				
			||||||
 | 
					    pub nargs: i32,
 | 
				
			||||||
 | 
					    // inheriting structs will continue from here
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # ServiceResult
 | 
				
			||||||
 | 
					/// a generic structure for accessing the results of a service call
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					pub struct ServiceResult<'a> {
 | 
				
			||||||
 | 
					    pub success: bool,
 | 
				
			||||||
 | 
					    buf: &'static mut [i32],
 | 
				
			||||||
 | 
					    pub rets: &'a [i32],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # call!
 | 
				
			||||||
 | 
					/// a macro for calling OpenFirmware services
 | 
				
			||||||
 | 
					/// ## example
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// use ofw::{call, service_result, ServiceResult};
 | 
				
			||||||
 | 
					/// use ofw::arch::EntryFunction;
 | 
				
			||||||
 | 
					/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
 | 
				
			||||||
 | 
					/// // <# of args>, <# of rets>
 | 
				
			||||||
 | 
					/// let mut results: ServiceResult = service_result!(1, 1);
 | 
				
			||||||
 | 
					/// // <entry function>, <service name>, <# of args>, <# of rets>, <args...>
 | 
				
			||||||
 | 
					/// call!(entry_fn, results, ntstr!("test"), 1, 1, ntstr!("test"));
 | 
				
			||||||
 | 
					/// if results.success {
 | 
				
			||||||
 | 
					///    let test_result: i32 = results.rets[0];
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! call {
 | 
				
			||||||
 | 
					    ($entry_fn:expr, $results:expr, $service:expr, $nargs:expr, $nrets:expr, $($args:expr),*) => {
 | 
				
			||||||
 | 
					        #[allow(unused_assignments)]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        let mut i = 0;
 | 
				
			||||||
 | 
					        $(
 | 
				
			||||||
 | 
					            $results.buf[i] = $args.into();
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        )*
 | 
				
			||||||
 | 
					        let success: bool = $crate::ofw_call($entry_fn, $service, $nargs, $nrets, $results.buf);
 | 
				
			||||||
 | 
					        $results.success = success;
 | 
				
			||||||
 | 
					        $results.rets = &$results.buf[$nargs as usize..];
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # call_method!
 | 
				
			||||||
 | 
					/// a macro for calling package methods
 | 
				
			||||||
 | 
					/// ## example
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// use ofw::arch::EntryFunction;
 | 
				
			||||||
 | 
					/// use ofw::{call_method, method_service_result, ServiceResult};
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
 | 
				
			||||||
 | 
					/// let package_instance: i32 = unimplemented!("get the package instance from somewhere");
 | 
				
			||||||
 | 
					/// // note the usage of the `method_service_result!` macro instead of `service_result!`
 | 
				
			||||||
 | 
					/// // this is required as the `call_method!` macro requires a
 | 
				
			||||||
 | 
					/// // slightly different ServiceResult struct configuration
 | 
				
			||||||
 | 
					/// // <# of args>, <# of rets>
 | 
				
			||||||
 | 
					/// let mut results: ServiceResult = method_service_result!(1, 1);
 | 
				
			||||||
 | 
					/// // <entry function>, <package instance>, <method name>, <# of args>, <# of rets>, <args...>
 | 
				
			||||||
 | 
					/// call_method!(entry_fn, results, package_instance, ntstr!("seek"), 2, 1, 0, 0);
 | 
				
			||||||
 | 
					/// if results.success {
 | 
				
			||||||
 | 
					///   let seek_result: i32 = results.rets[0];
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! call_method {
 | 
				
			||||||
 | 
					    ($entry_fn:expr, $package_instance:expr, $results:expr, $service:expr, $nargs:expr, $nrets:expr, $($args:expr),*) => {
 | 
				
			||||||
 | 
					        #[allow(unused_assignments)]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        let mut i = 0;
 | 
				
			||||||
 | 
					        $(
 | 
				
			||||||
 | 
					            $results.buf[i] = $args.into();
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        )*
 | 
				
			||||||
 | 
					        let success: bool = $crate::ofw_call_method($entry_fn, $package_instance, $service, $nargs, $nrets, $results.buf);
 | 
				
			||||||
 | 
					        $results.success = success && $results.buf[$nargs as usize] != 0;
 | 
				
			||||||
 | 
					        $results.rets = &$results.buf[$nargs as usize + 1..];
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # service_result!
 | 
				
			||||||
 | 
					/// a macro for creating a ServiceResult struct, used with the `call!` macro
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! service_result {
 | 
				
			||||||
 | 
					    ($nargs:expr, $nrets:expr) => { unsafe {
 | 
				
			||||||
 | 
					        static mut BUF: [i32; $nargs + $nrets] = [0; $nargs + $nrets];
 | 
				
			||||||
 | 
					        let rets: &[i32] = &[];
 | 
				
			||||||
 | 
					        let results = $crate::ServiceResult {
 | 
				
			||||||
 | 
					            success: false,
 | 
				
			||||||
 | 
					            buf: &mut BUF,
 | 
				
			||||||
 | 
					            rets,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        results
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # method_service_result!
 | 
				
			||||||
 | 
					/// a macro for creating a ServiceResult struct, used with the `call_method!` macro
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! method_service_result {
 | 
				
			||||||
 | 
					    ($nargs:expr, $nrets:expr) => { unsafe {
 | 
				
			||||||
 | 
					        static mut BUF: [i32; $nargs + $nrets + 1] = [0; $nargs + $nrets + 1];
 | 
				
			||||||
 | 
					        let rets: &[i32] = &[];
 | 
				
			||||||
 | 
					        let results = $crate::ServiceResult {
 | 
				
			||||||
 | 
					            success: false,
 | 
				
			||||||
 | 
					            buf: &mut BUF,
 | 
				
			||||||
 | 
					            rets,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        results
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # ofw_call
 | 
				
			||||||
 | 
					/// a function to call any OpenFirmware-provided service
 | 
				
			||||||
 | 
					/// usage of the macro `call!` is preferred, but this function can be used directly
 | 
				
			||||||
 | 
					/// ## example
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// use ofw::arch::EntryFunction;
 | 
				
			||||||
 | 
					/// use ofw::{ntstr, ofw_call};
 | 
				
			||||||
 | 
					/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
 | 
				
			||||||
 | 
					/// let mut args: [i32; 2] = [ntstr!("test") as i32, 0];
 | 
				
			||||||
 | 
					/// let was_service_successfully_called: bool = ofw_call(entry_fn, ntstr!("test"), 1, 1, &mut args);
 | 
				
			||||||
 | 
					/// let test_result: i32 = args[1];
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					pub fn ofw_call(entry_fn: EntryFunction, service: NTSTR, num_args: i32, num_rets: i32, args_and_rets: &mut [i32]) -> bool {
 | 
				
			||||||
 | 
					    #[repr(C)]
 | 
				
			||||||
 | 
					    struct GenericArgs {
 | 
				
			||||||
 | 
					        args: Args,
 | 
				
			||||||
 | 
					        buffer: *mut i32,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let mut generic_args = GenericArgs {
 | 
				
			||||||
 | 
					        args: Args {
 | 
				
			||||||
 | 
					            service,
 | 
				
			||||||
 | 
					            nrets: num_rets,
 | 
				
			||||||
 | 
					            nargs: num_args,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        buffer: args_and_rets.as_mut_ptr(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result: bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(target_arch = "powerpc")]
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result = entry_fn(&mut generic_args as *mut _ as *mut Args) == OFW_TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[cfg(target_arch = "x86_64")]
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result = entry_fn(&mut generic_args as *mut _ as *mut Args) == OFW_TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # ofw_call_method
 | 
				
			||||||
 | 
					/// a function to call a method provided by a package instance
 | 
				
			||||||
 | 
					/// usage of the `call_method!` macro is preferred, but this function can be used directly
 | 
				
			||||||
 | 
					/// ## example
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// // we will be calling the "seek" method on an imaginary block device
 | 
				
			||||||
 | 
					/// // the seek method takes 2 arguments, and returns 1 value
 | 
				
			||||||
 | 
					/// // addr_hi, addr_lo -- okay?
 | 
				
			||||||
 | 
					/// // however, we must add another cell in order to account for the catch-result
 | 
				
			||||||
 | 
					/// // (see ieee 1275, 6.3.2.2 Device tree)
 | 
				
			||||||
 | 
					/// use ofw::{ntstr, ofw_call_method};
 | 
				
			||||||
 | 
					/// // addr_hi, addr_lo, catch-result, okay?
 | 
				
			||||||
 | 
					/// let mut args: [i32; 4] = [0, 0, 0, 0];
 | 
				
			||||||
 | 
					/// // note that num_args and num_rets are not the same as the length of args, this is intentional
 | 
				
			||||||
 | 
					/// let was_service_successfully_called: bool = ofw_call_method(entry_fn, instance, ntstr!("seek"), 2, 1, &mut args);
 | 
				
			||||||
 | 
					/// if was_service_successfully_called && args[2] != 0 {
 | 
				
			||||||
 | 
					///     let seek_result: i32 = args[3];
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					pub fn ofw_call_method(entry_fn: EntryFunction, ihandle: IHandle, method: NTSTR, num_args: i32, num_rets: i32, args_and_rets: &mut [i32]) -> bool {
 | 
				
			||||||
 | 
					    #[repr(C)]
 | 
				
			||||||
 | 
					    struct CallMethodArgs {
 | 
				
			||||||
 | 
					        args: Args,
 | 
				
			||||||
 | 
					        method: NTSTR,
 | 
				
			||||||
 | 
					        ihandle: IHandle,
 | 
				
			||||||
 | 
					        buffer: *mut i32,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut call_method_args = CallMethodArgs {
 | 
				
			||||||
 | 
					        args: Args {
 | 
				
			||||||
 | 
					            service: ntstr!("call-method"),
 | 
				
			||||||
 | 
					            nrets: num_rets + 1,
 | 
				
			||||||
 | 
					            nargs: num_args + 2,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        method,
 | 
				
			||||||
 | 
					        ihandle,
 | 
				
			||||||
 | 
					        buffer: args_and_rets.as_mut_ptr(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let result: bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(target_arch = "powerpc")]
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result = entry_fn(&mut call_method_args as *mut _ as *mut Args) == OFW_TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[cfg(target_arch = "x86_64")]
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result = entry_fn(&mut call_method_args as *mut _ as *mut Args) == OFW_TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										94
									
								
								src/ntstr.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/ntstr.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,94 @@
 | 
				
			||||||
 | 
					use core::fmt::{Debug, Display, Formatter, Write};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # ntstr!
 | 
				
			||||||
 | 
					/// macro for creating a null-terminated string
 | 
				
			||||||
 | 
					/// ## example
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// use ofw::ntstr;
 | 
				
			||||||
 | 
					/// let ntstr = ntstr!("Hello, world!");
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// becomes
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// let ntstr = ofw::ntstr::NTSTR::new(b"Hello, world!\0".as_ptr());
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[macro_export]
 | 
				
			||||||
 | 
					macro_rules! ntstr {
 | 
				
			||||||
 | 
					    ($str:expr) => {
 | 
				
			||||||
 | 
					        $crate::ntstr::NTSTR::new(concat!($str, "\0").as_ptr())
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// # NTSTR
 | 
				
			||||||
 | 
					/// a null-terminated string, defined as a pointer to an array of u8 characters ending with a null byte
 | 
				
			||||||
 | 
					/// not mutable
 | 
				
			||||||
 | 
					#[repr(C)]
 | 
				
			||||||
 | 
					pub struct NTSTR {
 | 
				
			||||||
 | 
					    ptr: *const u8,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<usize> for NTSTR {
 | 
				
			||||||
 | 
					    fn from(ptr: usize) -> Self {
 | 
				
			||||||
 | 
					        Self { ptr: ptr as *const u8 }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<*const u8> for NTSTR {
 | 
				
			||||||
 | 
					    fn from(ptr: *const u8) -> Self {
 | 
				
			||||||
 | 
					        Self { ptr }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(clippy::from_over_into)]
 | 
				
			||||||
 | 
					impl Into<i32> for NTSTR {
 | 
				
			||||||
 | 
					    fn into(self) -> i32 {
 | 
				
			||||||
 | 
					        self.ptr as i32
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl NTSTR {
 | 
				
			||||||
 | 
					    pub const fn new(ptr: *const u8) -> Self {
 | 
				
			||||||
 | 
					        Self { ptr }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn print(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
 | 
				
			||||||
 | 
					        let mut i = 0;
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let a = unsafe { *self.ptr.add(i) };
 | 
				
			||||||
 | 
					            if a == 0 {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            f.write_char(a as char)?;
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialEq for NTSTR {
 | 
				
			||||||
 | 
					    fn eq(&self, other: &Self) -> bool {
 | 
				
			||||||
 | 
					        let mut i = 0;
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let a = unsafe { *self.ptr.add(i) };
 | 
				
			||||||
 | 
					            let b = unsafe { *other.ptr.add(i) };
 | 
				
			||||||
 | 
					            if a != b {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if a == 0 {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Debug for NTSTR {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
 | 
				
			||||||
 | 
					        self.print(f)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for NTSTR {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
 | 
				
			||||||
 | 
					        self.print(f)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										167
									
								
								src/tests.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/tests.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,167 @@
 | 
				
			||||||
 | 
					use crate::arch::EntryFunction;
 | 
				
			||||||
 | 
					use crate::{Args, call, call_method, IHandle, method_service_result, ntstr, OFW_FALSE, OFW_TRUE, service_result};
 | 
				
			||||||
 | 
					use crate::ntstr::NTSTR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn ntstr_macro() {
 | 
				
			||||||
 | 
					    let a = ntstr!("Hello, world!");
 | 
				
			||||||
 | 
					    let b = NTSTR::new(b"Hello, world!\0".as_ptr());
 | 
				
			||||||
 | 
					    assert_eq!(a, b);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_ofw() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct GenericArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut GenericArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("test"));
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 1);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1);
 | 
				
			||||||
 | 
					        // 1 offset from args in buffer
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(1)) = OFW_FALSE }; // set "missing" to false
 | 
				
			||||||
 | 
					        OFW_TRUE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let mut results = service_result!(1,1);
 | 
				
			||||||
 | 
					    call!(entry_fn, results, ntstr!("test"), 1, 1, ntstr!("test"));
 | 
				
			||||||
 | 
					    assert!(results.success);
 | 
				
			||||||
 | 
					    assert_eq!(results.rets[0], OFW_FALSE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_ofw_missing() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct GenericArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut GenericArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("test"));
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 1);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1);
 | 
				
			||||||
 | 
					        // 1 offset from args in buffer
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(1)) = OFW_TRUE }; // set "missing" to true
 | 
				
			||||||
 | 
					        OFW_TRUE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let mut results = service_result!(1,1);
 | 
				
			||||||
 | 
					    call!(entry_fn, results, ntstr!("test"), 1, 1, ntstr!("aioshjfoahwfi"));
 | 
				
			||||||
 | 
					    assert!(results.success);
 | 
				
			||||||
 | 
					    assert_eq!(results.rets[0], OFW_TRUE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_ofw_failure() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct GenericArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut GenericArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("this-method-does-not-exist"));
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 1);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1);
 | 
				
			||||||
 | 
					        OFW_FALSE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let mut results = service_result!(1,1);
 | 
				
			||||||
 | 
					    call!(entry_fn, results, ntstr!("this-method-does-not-exist"), 1, 1, ntstr!("aioshjfoahwfi"));
 | 
				
			||||||
 | 
					    assert!(!results.success);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_method_ofw() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct CallMethodArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            method: NTSTR,
 | 
				
			||||||
 | 
					            ihandle: IHandle,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut CallMethodArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("call-method"));
 | 
				
			||||||
 | 
					        assert_eq!(args.method, ntstr!("seek"));
 | 
				
			||||||
 | 
					        assert_eq!(args.ihandle, 0);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 2 + 2);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1 + 1);
 | 
				
			||||||
 | 
					        // 2 offset from args in buffer
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(2)) = 1 }; // set catch-result to non-zero
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(2 + 1)) = OFW_TRUE }; // set okay? to true
 | 
				
			||||||
 | 
					        OFW_TRUE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let ih: IHandle = 0;
 | 
				
			||||||
 | 
					    let mut results = method_service_result!(2,1);
 | 
				
			||||||
 | 
					    call_method!(entry_fn, ih, results, ntstr!("seek"), 2, 1, 0, 0);
 | 
				
			||||||
 | 
					    assert!(results.success);
 | 
				
			||||||
 | 
					    assert_eq!(results.rets[0], OFW_TRUE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_method_ofw_seek_failure() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct CallMethodArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            method: NTSTR,
 | 
				
			||||||
 | 
					            ihandle: IHandle,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut CallMethodArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("call-method"));
 | 
				
			||||||
 | 
					        assert_eq!(args.method, ntstr!("seek"));
 | 
				
			||||||
 | 
					        assert_eq!(args.ihandle, 0);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 2 + 2);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1 + 1);
 | 
				
			||||||
 | 
					        // 2 offset from args in buffer
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(2)) = 1 }; // set catch-result to non-zero
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(2 + 1)) = OFW_FALSE }; // set okay? to false
 | 
				
			||||||
 | 
					        OFW_TRUE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let ih: IHandle = 0;
 | 
				
			||||||
 | 
					    let mut results = method_service_result!(2,1);
 | 
				
			||||||
 | 
					    call_method!(entry_fn, ih, results, ntstr!("seek"), 2, 1, 0, 0);
 | 
				
			||||||
 | 
					    assert!(results.success);
 | 
				
			||||||
 | 
					    assert_eq!(results.rets[0], OFW_FALSE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[test]
 | 
				
			||||||
 | 
					fn call_method_ofw_failure() {
 | 
				
			||||||
 | 
					    pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
 | 
				
			||||||
 | 
					        #[repr(C)]
 | 
				
			||||||
 | 
					        struct CallMethodArgs {
 | 
				
			||||||
 | 
					            args: Args,
 | 
				
			||||||
 | 
					            method: NTSTR,
 | 
				
			||||||
 | 
					            ihandle: IHandle,
 | 
				
			||||||
 | 
					            buffer: *mut i32,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let args = unsafe { &mut *(args as *mut CallMethodArgs) };
 | 
				
			||||||
 | 
					        assert_eq!(args.args.service, ntstr!("call-method"));
 | 
				
			||||||
 | 
					        assert_eq!(args.method, ntstr!("asiofhawoptgh"));
 | 
				
			||||||
 | 
					        assert_eq!(args.ihandle, 0);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nargs, 2 + 2);
 | 
				
			||||||
 | 
					        assert_eq!(args.args.nrets, 1 + 1);
 | 
				
			||||||
 | 
					        // 2 offset from args in buffer
 | 
				
			||||||
 | 
					        unsafe { *(args.buffer.offset(2)) = 0 }; // set catch-result to 0 (caught error, package doesn't have this method)
 | 
				
			||||||
 | 
					        OFW_TRUE
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let entry_fn: EntryFunction = test_entry_function;
 | 
				
			||||||
 | 
					    let ih: IHandle = 0;
 | 
				
			||||||
 | 
					    let mut results = method_service_result!(2,1);
 | 
				
			||||||
 | 
					    call_method!(entry_fn, ih, results, ntstr!("asiofhawoptgh"), 2, 1, 0, 0);
 | 
				
			||||||
 | 
					    assert!(!results.success);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue