Browse Source

Merge pull request #49 from Meziu/fix/automatic-ps

Static init of the PS service
pull/52/head
Meziu 3 years ago committed by GitHub
parent
commit
13eaba92e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ctru-rs/examples/futures-tokio.rs
  2. 12
      ctru-rs/examples/hashmaps.rs
  3. 14
      ctru-rs/src/lib.rs
  4. 71
      ctru-rs/src/services/ps.rs

2
ctru-rs/examples/futures-tokio.rs

@ -1,6 +1,5 @@
use ctru::console::Console; use ctru::console::Console;
use ctru::services::hid::KeyPad; use ctru::services::hid::KeyPad;
use ctru::services::ps::Ps;
use ctru::services::{Apt, Hid}; use ctru::services::{Apt, Hid};
use ctru::Gfx; use ctru::Gfx;
use std::time::Duration; use std::time::Duration;
@ -10,7 +9,6 @@ fn main() {
let gfx = Gfx::default(); let gfx = Gfx::default();
let hid = Hid::init().expect("Couldn't obtain HID controller"); let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller"); let apt = Apt::init().expect("Couldn't obtain APT controller");
let _ps = Ps::init().expect("Couldn't initialize PS service");
let _console = Console::init(gfx.top_screen.borrow_mut()); let _console = Console::init(gfx.top_screen.borrow_mut());
// Give ourselves up to 30% of the system core's time // Give ourselves up to 30% of the system core's time

12
ctru-rs/examples/hashmaps.rs

@ -2,23 +2,19 @@ use ctru::console::Console;
use ctru::gfx::Gfx; use ctru::gfx::Gfx;
use ctru::services::apt::Apt; use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad}; use ctru::services::hid::{Hid, KeyPad};
use ctru::services::ps::Ps;
fn main() { fn main() {
// Initialize services // Initialize services
//
// HashMaps generate hashes thanks to the 3DS' cryptografically secure generator.
// This generator is only active when activating the `PS` service.
// This service is automatically initialized in `ctru::init`
ctru::init(); ctru::init();
let apt = Apt::init().unwrap(); let apt = Apt::init().unwrap();
let hid = Hid::init().unwrap(); let hid = Hid::init().unwrap();
let gfx = Gfx::default(); let gfx = Gfx::default();
let _console = Console::init(gfx.top_screen.borrow_mut()); let _console = Console::init(gfx.top_screen.borrow_mut());
// HashMaps generate hashes thanks to the 3DS' criptografically secure generator.
// Sadly, this generator is only active when activating the `Ps` service.
// To do this, we have to make sure the `Ps` service handle is alive for the whole
// run time (or at least, when `HashMaps` are used).
// Not having a living `Ps` instance when using `HashMap`s results in a panic
let _ps = Ps::init().unwrap();
let mut map = std::collections::HashMap::new(); let mut map = std::collections::HashMap::new();
map.insert("A Key!", 102); map.insert("A Key!", 102);
map.insert("Another key?", 543); map.insert("Another key?", 543);

14
ctru-rs/src/lib.rs

@ -4,6 +4,12 @@
#![feature(custom_test_frameworks)] #![feature(custom_test_frameworks)]
#![test_runner(test_runner::run)] #![test_runner(test_runner::run)]
extern "C" fn services_deinit() {
unsafe {
ctru_sys::psExit();
}
}
/// Call this somewhere to force Rust to link some required crates /// Call this somewhere to force Rust to link some required crates
/// This is also a setup for some crate integration only available at runtime /// This is also a setup for some crate integration only available at runtime
/// ///
@ -12,6 +18,14 @@ pub fn init() {
linker_fix_3ds::init(); linker_fix_3ds::init();
pthread_3ds::init(); pthread_3ds::init();
// Initialize the PS service for random data generation
unsafe {
ctru_sys::psInit();
// Setup the deconstruction at the program's end
libc::atexit(services_deinit);
}
use std::panic::PanicInfo; use std::panic::PanicInfo;
let main_thread = thread::current().id(); let main_thread = thread::current().id();

71
ctru-rs/src/services/ps.rs

@ -1,13 +1,9 @@
//! Process Services (PS) module. This is used for miscellaneous utility tasks, but //! Process Services (PS) module. This is used for miscellaneous utility tasks, but
//! is particularly important because it is used to generate random data, which //! is particularly important because it is used to generate random data, which
//! is required for common things like [`HashMap`](std::collections::HashMap). //! is required for common things like [`HashMap`](std::collections::HashMap).
//! As such, it is initialized by default in `ctru::init` instead of having a safety handler
//! See also <https://www.3dbrew.org/wiki/Process_Services> //! See also <https://www.3dbrew.org/wiki/Process_Services>
/// PS handle. This must not be dropped in order for random generation
/// to work (in most cases, the lifetime of an application).
#[non_exhaustive]
pub struct Ps;
#[repr(u32)] #[repr(u32)]
pub enum AESAlgorithm { pub enum AESAlgorithm {
CbcEnc, CbcEnc,
@ -32,18 +28,7 @@ pub enum AESKeyType {
Keyslot39Nfc, Keyslot39Nfc,
} }
impl Ps { pub fn local_friend_code_seed() -> crate::Result<u64> {
/// Initialize the PS module.
pub fn init() -> crate::Result<Self> {
let r = unsafe { ctru_sys::psInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self)
}
}
pub fn local_friend_code_seed(&self) -> crate::Result<u64> {
let mut seed: u64 = 0; let mut seed: u64 = 0;
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) }; let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) };
@ -52,9 +37,9 @@ impl Ps {
} else { } else {
Ok(seed) Ok(seed)
} }
} }
pub fn device_id(&self) -> crate::Result<u32> { pub fn device_id() -> crate::Result<u32> {
let mut id: u32 = 0; let mut id: u32 = 0;
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) }; let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) };
@ -63,25 +48,15 @@ impl Ps {
} else { } else {
Ok(id) Ok(id)
} }
} }
pub fn generate_random_bytes(&self, out: &mut [u8]) -> crate::Result<()> { pub fn generate_random_bytes(out: &mut [u8]) -> crate::Result<()> {
let r = let r = unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) };
unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) };
if r < 0 { if r < 0 {
Err(r.into()) Err(r.into())
} else { } else {
Ok(()) Ok(())
} }
}
}
impl Drop for Ps {
fn drop(&mut self) {
unsafe {
ctru_sys::psExit();
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -92,8 +67,6 @@ mod tests {
#[test] #[test]
fn construct_hash_map() { fn construct_hash_map() {
let _ps = Ps::init().unwrap();
let mut input = vec![ let mut input = vec![
(1_i32, String::from("123")), (1_i32, String::from("123")),
(2, String::from("2")), (2, String::from("2")),
@ -108,34 +81,4 @@ mod tests {
assert_eq!(input, actual); assert_eq!(input, actual);
} }
#[test]
fn construct_hash_map_no_rand() {
// Without initializing PS, we can't use `libc::getrandom` and constructing
// a HashMap panics at runtime.
//
// If any test case successfully creates a HashMap before this test,
// the thread-local RandomState in std will be initialized. We spawn
// a new thread to actually create the hash map, since even in multi-threaded
// test environment there's a chance this test wouldn't panic because
// some other test case ran before it.
//
// One downside of this approach is that the panic handler for the panicking
// thread prints to the console, which is not captured by the default test
// harness and prints even when the test passes.
crate::thread::Builder::new()
.stack_size(0x20_0000)
.spawn(|| {
let map: HashMap<i32, String> = HashMap::from_iter([
(1_i32, String::from("123")),
(2, String::from("2")),
(6, String::from("six")),
]);
dbg!(map);
})
.unwrap()
.join()
.expect_err("should have panicked");
}
} }

Loading…
Cancel
Save