-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from LedgerHQ/ecalls
Ecalls general framework + IDLE, XSEND, XRECV, EXIT and FATAL. Basic LRU cache for pages.
- Loading branch information
Showing
27 changed files
with
1,857 additions
and
281 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,24 @@ | ||
|
||
#[cfg(target_arch = "riscv32")] | ||
use crate::ecalls_riscv as ecalls_module; | ||
|
||
#[cfg(not(target_arch = "riscv32"))] | ||
use crate::ecalls_native as ecalls_module; | ||
|
||
pub(crate) trait EcallsInterface { | ||
/// Shows the idle screen of the V-App | ||
fn ux_idle(); | ||
|
||
/// Exits the V-App with the given status code | ||
fn exit(status: i32) -> !; | ||
|
||
/// Prints a fatal error message and exits the V-App | ||
fn fatal(msg: *const u8, size: usize) -> !; | ||
|
||
/// Sends a buffer to the host | ||
fn xsend(buffer: *const u8, size: usize); | ||
|
||
/// Receives a buffer of at most `max_size` bytes from the host | ||
fn xrecv(buffer: *mut u8, max_size: usize) -> usize; | ||
} | ||
|
||
pub use ecalls_module::*; | ||
pub(crate) use ecalls_module::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,63 @@ | ||
pub fn ecall_ux_idle() { | ||
use std::io; | ||
use std::io::Write; | ||
|
||
use crate::ecalls::EcallsInterface; | ||
|
||
pub struct Ecall; | ||
|
||
impl EcallsInterface for Ecall { | ||
fn ux_idle() {} | ||
|
||
fn exit(status: i32) -> ! { | ||
std::process::exit(status); | ||
} | ||
|
||
fn fatal(msg: *const u8, size: usize) -> ! { | ||
// print the message as a panic | ||
let slice = unsafe { std::slice::from_raw_parts(msg, size) }; | ||
let msg = std::str::from_utf8(slice).unwrap(); | ||
panic!("{}", msg); | ||
} | ||
|
||
fn xsend(buffer: *const u8, size: usize) { | ||
let slice = unsafe { std::slice::from_raw_parts(buffer, size) }; | ||
for byte in slice { | ||
print!("{:02x}", byte); | ||
} | ||
print!("\n"); | ||
io::stdout().flush().expect("Failed to flush stdout"); | ||
} | ||
|
||
fn xrecv(buffer: *mut u8, max_size: usize) -> usize { | ||
// Request a hex string from the user; repeat until the input is valid | ||
// and at most max_size bytes long | ||
let (n_bytes_to_copy, bytes) = loop { | ||
let mut input = String::new(); | ||
io::stdout().flush().expect("Failed to flush stdout"); | ||
io::stdin() | ||
.read_line(&mut input) | ||
.expect("Failed to read line"); | ||
|
||
let input = input.trim(); | ||
|
||
let Ok(bytes) = hex::decode(input) else { | ||
println!( | ||
"Input too large, max size is {} bytes, please try again.", | ||
max_size | ||
); | ||
continue; | ||
}; | ||
if bytes.len() <= max_size { | ||
break (bytes.len(), bytes); | ||
} | ||
println!("Input too large, please try again."); | ||
}; | ||
|
||
// copy to the destination buffer | ||
unsafe { | ||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buffer, n_bytes_to_copy); | ||
} | ||
|
||
return n_bytes_to_copy; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,176 @@ | ||
use core::arch::asm; | ||
|
||
use crate::ecall_constants::*; | ||
|
||
pub fn ecall_ux_idle() { | ||
unsafe { | ||
asm!( | ||
"li a7, {0}", | ||
"ecall", | ||
const ECALL_UX_IDLE | ||
); | ||
use crate::ecalls::EcallsInterface; | ||
use common::ecall_constants::*; | ||
|
||
macro_rules! ecall0v { | ||
// ECALL with no arguments and no return value | ||
($fn_name:ident, $syscall_number:expr) => { | ||
fn $fn_name() { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
); | ||
} | ||
} | ||
}; | ||
} | ||
macro_rules! ecall0 { | ||
// ECALL with no arguments and returning a value | ||
($fn_name:ident, $syscall_number:expr, $ret_type:ty) => { | ||
fn $fn_name() -> $ret_type { | ||
let ret: $ret_type; | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
lateout("a0") ret // Return value in a0 | ||
); | ||
} | ||
ret | ||
} | ||
}; | ||
} | ||
macro_rules! ecall1v { | ||
// ECALL with 1 argument and no return value | ||
($fn_name:ident, $syscall_number:expr, ($arg1:ident: $arg1_type:ty)) => { | ||
fn $fn_name($arg1: $arg1_type) { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1 // First argument in a0 | ||
); | ||
} | ||
} | ||
}; | ||
} | ||
macro_rules! ecall1 { | ||
// ECALL with 1 argument and returning a value | ||
($fn_name:ident, $syscall_number:expr, ($arg1:ident: $arg1_type:ty), $ret_type:ty) => { | ||
fn $fn_name($arg1: $arg1_type) -> $ret_type { | ||
let ret: $ret_type; | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1, // First argument in a0 | ||
lateout("a0") ret // Return value in a0 | ||
); | ||
} | ||
ret | ||
} | ||
}; | ||
} | ||
macro_rules! ecall2v { | ||
// ECALL with 2 arguments and no return value | ||
($fn_name:ident, $syscall_number:expr, | ||
($arg1:ident: $arg1_type:ty), | ||
($arg2:ident: $arg2_type:ty)) => { | ||
fn $fn_name($arg1: $arg1_type, $arg2: $arg2_type) { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1, // First argument in a0 | ||
in("a1") $arg2 // Second argument in a1 | ||
); | ||
} | ||
} | ||
}; | ||
} | ||
macro_rules! ecall2 { | ||
// ECALL with 2 arguments and returning a value | ||
($fn_name:ident, $syscall_number:expr, | ||
($arg1:ident: $arg1_type:ty), | ||
($arg2:ident: $arg2_type:ty), $ret_type:ty) => { | ||
fn $fn_name($arg1: $arg1_type, $arg2: $arg2_type) -> $ret_type { | ||
let ret: $ret_type; | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1, // First argument in a0 | ||
in("a1") $arg2, // Second argument in a1 | ||
lateout("a0") ret // Return value in a0 | ||
); | ||
} | ||
ret | ||
} | ||
}; | ||
} | ||
macro_rules! ecall3v { | ||
// ECALL with 3 arguments and no return value | ||
($fn_name:ident, $syscall_number:expr, | ||
($arg1:ident: $arg1_type:ty), | ||
($arg2:ident: $arg2_type:ty), | ||
($arg3:ident: $arg3_type:ty)) => { | ||
fn $fn_name($arg1: $arg1_type, $arg2: $arg2_type, $arg3: $arg3_type) { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1, // First argument in a0 | ||
in("a1") $arg2, // Second argument in a1 | ||
in("a2") $arg3 // Third argument in a2 | ||
); | ||
} | ||
} | ||
}; | ||
} | ||
macro_rules! ecall3 { | ||
// ECALL with 3 arguments and returning a value | ||
($fn_name:ident, $syscall_number:expr, | ||
($arg1:ident: $arg1_type:ty), | ||
($arg2:ident: $arg2_type:ty), | ||
($arg3:ident: $arg3_type:ty), $ret_type:ty) => { | ||
fn $fn_name($arg1: $arg1_type, $arg2: $arg2_type, $arg3: $arg3_type) -> $ret_type { | ||
let ret: $ret_type; | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") $syscall_number, // Pass the syscall number in t0 | ||
in("a0") $arg1, // First argument in a0 | ||
in("a1") $arg2, // Second argument in a1 | ||
in("a2") $arg3, // Third argument in a2 | ||
lateout("a0") ret // Return value in a0 | ||
); | ||
} | ||
ret | ||
} | ||
}; | ||
} | ||
|
||
pub struct Ecall; | ||
|
||
impl EcallsInterface for Ecall { | ||
ecall0v!(ux_idle, ECALL_UX_IDLE); | ||
|
||
fn exit(status: i32) -> ! { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") ECALL_EXIT, | ||
in("a0") status, | ||
options(noreturn) | ||
); | ||
} | ||
} | ||
|
||
// fatal() and exit() are diverging, therefore we can't use the macro | ||
fn fatal(msg: *const u8, size: usize) -> ! { | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
in("t0") ECALL_FATAL, | ||
in("a0") msg, | ||
in("a1") size, | ||
options(noreturn) | ||
); | ||
} | ||
} | ||
|
||
ecall2v!(xsend, ECALL_XSEND, (buffer: *const u8), (size: usize)); | ||
ecall2!(xrecv, ECALL_XRECV, (buffer: *mut u8), (size: usize), usize); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
|
||
use crate::ecalls::*; | ||
use crate::ecalls::{Ecall, EcallsInterface}; | ||
|
||
pub fn ux_idle() { | ||
ecall_ux_idle() | ||
Ecall::ux_idle() | ||
} |
Oops, something went wrong.