Browse Source

Static init of the PS service

pull/49/head
Andrea Ciliberti 3 years ago
parent
commit
ea854215f8
  1. 12
      ctru-rs/examples/hashmaps.rs
  2. 14
      ctru-rs/src/lib.rs
  3. 101
      ctru-rs/src/services/ps.rs

12
ctru-rs/examples/hashmaps.rs

@ -2,23 +2,19 @@ use ctru::console::Console; @@ -2,23 +2,19 @@ use ctru::console::Console;
use ctru::gfx::Gfx;
use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad};
use ctru::services::ps::Ps;
fn main() {
// Initialize services
//
// HashMaps generate hashes thanks to the 3DS' criptografically secure generator.
// This generator is only active when activating the `PS` service.
// This service is automatically initialized in `ctru::init`
ctru::init();
let apt = Apt::init().unwrap();
let hid = Hid::init().unwrap();
let gfx = Gfx::default();
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();
map.insert("A Key!", 102);
map.insert("Another key?", 543);

14
ctru-rs/src/lib.rs

@ -4,6 +4,12 @@ @@ -4,6 +4,12 @@
#![feature(custom_test_frameworks)]
#![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
/// This is also a setup for some crate integration only available at runtime
///
@ -12,6 +18,14 @@ pub fn init() { @@ -12,6 +18,14 @@ pub fn init() {
linker_fix_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;
let main_thread = thread::current().id();

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

@ -1,13 +1,9 @@ @@ -1,13 +1,9 @@
//! 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 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>
/// 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)]
pub enum AESAlgorithm {
CbcEnc,
@ -32,55 +28,34 @@ pub enum AESKeyType { @@ -32,55 +28,34 @@ pub enum AESKeyType {
Keyslot39Nfc,
}
impl Ps {
/// 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;
pub fn local_friend_code_seed() -> crate::Result<u64> {
let mut seed: u64 = 0;
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) };
if r < 0 {
Err(r.into())
} else {
Ok(seed)
}
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) };
if r < 0 {
Err(r.into())
} else {
Ok(seed)
}
}
pub fn device_id(&self) -> crate::Result<u32> {
let mut id: u32 = 0;
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) };
if r < 0 {
Err(r.into())
} else {
Ok(id)
}
}
pub fn device_id() -> crate::Result<u32> {
let mut id: u32 = 0;
pub fn generate_random_bytes(&self, out: &mut [u8]) -> crate::Result<()> {
let r =
unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) };
if r < 0 {
Err(r.into())
} else {
Ok(())
}
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) };
if r < 0 {
Err(r.into())
} else {
Ok(id)
}
}
impl Drop for Ps {
fn drop(&mut self) {
unsafe {
ctru_sys::psExit();
}
pub fn generate_random_bytes(out: &mut [u8]) -> crate::Result<()> {
let r = unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) };
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
@ -92,8 +67,6 @@ mod tests { @@ -92,8 +67,6 @@ mod tests {
#[test]
fn construct_hash_map() {
let _ps = Ps::init().unwrap();
let mut input = vec![
(1_i32, String::from("123")),
(2, String::from("2")),
@ -108,34 +81,4 @@ mod tests { @@ -108,34 +81,4 @@ mod tests {
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