mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
add hardware support for sha512/256
This commit is contained in:
parent
21a5ddb40f
commit
5526bc4100
3 changed files with 212 additions and 102 deletions
|
|
@ -76,15 +76,117 @@ impl Engine512 {
|
|||
}
|
||||
}
|
||||
|
||||
// a macro for common communications libraries for SHA2 hardware interfacing
|
||||
// you can't reference fields in a trait. looks like a macro is the accepted way
|
||||
// of not having to repeat this code over and over again.
|
||||
macro_rules! sha512_comms {
|
||||
() => {
|
||||
pub(crate) fn ensure_conn(&self) -> u32 {
|
||||
if HW_CONN.load(Ordering::Relaxed) == 0 {
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
HW_CONN.store(xns.request_connection_blocking(api::SERVER_NAME_SHA512).expect("Can't connect to Sha512 server"), Ordering::Relaxed);
|
||||
let trng = trng::Trng::new(&xns).expect("Can't connect to TRNG server");
|
||||
let id1 = trng.get_u64().unwrap();
|
||||
let id2 = trng.get_u32().unwrap();
|
||||
TOKEN[0].store((id1 >> 32) as u32, Ordering::Relaxed);
|
||||
TOKEN[1].store(id1 as u32, Ordering::Relaxed);
|
||||
TOKEN[2].store(id2, Ordering::Relaxed);
|
||||
}
|
||||
HW_CONN.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn is_idle(&self) -> Result<bool, xous::Error> {
|
||||
let response = send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::IsIdle.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("Couldn't make IsIdle query");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Err(xous::Error::InternalError)
|
||||
}
|
||||
}
|
||||
pub fn acquire_suspend_lock(&self) -> Result<bool, xous::Error> {
|
||||
let response = send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::AcquireSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("Couldn't issue AcquireSuspendLock message");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Err(xous::Error::InternalError)
|
||||
}
|
||||
}
|
||||
pub fn abort_suspend(&self) -> Result<(), xous::Error> {
|
||||
// we ignore the result and just turn it into () once we get anything back, as abort_suspend "can't fail"
|
||||
send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::AbortSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).map(|_| ())
|
||||
}
|
||||
pub(crate) fn try_acquire_hw(&mut self, config: Sha2Config) {
|
||||
if !self.in_progress && (self.strategy != FallbackStrategy::SoftwareOnly) {
|
||||
loop {
|
||||
let conn = self.ensure_conn(); // also ensures the ID
|
||||
let response = send_message(conn,
|
||||
Message::new_blocking_scalar(Opcode::AcquireExclusive.to_usize().unwrap(),
|
||||
TOKEN[0].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[1].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[2].load(Ordering::Relaxed) as usize,
|
||||
config.to_usize().unwrap(),
|
||||
)
|
||||
).expect("couldn't send AcquireExclusive message to Sha2 hardware!");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
self.use_soft = false;
|
||||
self.in_progress = true;
|
||||
break;
|
||||
} else {
|
||||
if self.strategy == FallbackStrategy::HardwareThenSoftware {
|
||||
self.use_soft = true;
|
||||
self.in_progress = true;
|
||||
break;
|
||||
} else {
|
||||
// this is hardware-exclusive mode, we block until we can get the hardware
|
||||
xous::yield_slice();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("AcquireExclusive had an unexpected error: {:?}", response);
|
||||
panic!("Internal error in AcquireExclusive");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn reset_hw(&mut self) {
|
||||
send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::Reset.to_usize().unwrap(),
|
||||
TOKEN[0].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[1].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[2].load(Ordering::Relaxed) as usize,
|
||||
0,
|
||||
)).expect("couldn't send reset to hardware");
|
||||
// reset internal flags
|
||||
self.length = 0;
|
||||
self.in_progress = false;
|
||||
self.use_soft = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The SHA-512 hash algorithm with the SHA-512 initial hash value.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha512 {
|
||||
/// software fallback engine
|
||||
engine: Engine512,
|
||||
/// whether or not this current hasher instance will use software or hardware acceleration
|
||||
use_soft: bool,
|
||||
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
|
||||
strategy: FallbackStrategy,
|
||||
/// software fallback engine
|
||||
engine: Engine512,
|
||||
/// track if a hash is in progress
|
||||
in_progress: bool,
|
||||
/// track the length of the message processed so far
|
||||
|
|
@ -106,53 +208,8 @@ impl Sha512 {
|
|||
length: 0,
|
||||
}
|
||||
}
|
||||
fn ensure_conn(&self) -> u32 {
|
||||
if HW_CONN.load(Ordering::Relaxed) == 0 {
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
HW_CONN.store(xns.request_connection_blocking(api::SERVER_NAME_SHA512).expect("Can't connect to Sha512 server"), Ordering::Relaxed);
|
||||
let trng = trng::Trng::new(&xns).expect("Can't connect to TRNG server");
|
||||
let id1 = trng.get_u64().unwrap();
|
||||
let id2 = trng.get_u32().unwrap();
|
||||
TOKEN[0].store((id1 >> 32) as u32, Ordering::Relaxed);
|
||||
TOKEN[1].store(id1 as u32, Ordering::Relaxed);
|
||||
TOKEN[2].store(id2, Ordering::Relaxed);
|
||||
}
|
||||
HW_CONN.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn is_idle(&self) -> Result<bool, xous::Error> {
|
||||
let response = send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::IsIdle.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("Couldn't make IsIdle query");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Err(xous::Error::InternalError)
|
||||
}
|
||||
}
|
||||
pub fn acquire_suspend_lock(&self) -> Result<bool, xous::Error> {
|
||||
let response = send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::AcquireSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("Couldn't issue AcquireSuspendLock message");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Err(xous::Error::InternalError)
|
||||
}
|
||||
}
|
||||
pub fn abort_suspend(&self) -> Result<(), xous::Error> {
|
||||
// we ignore the result and just turn it into () once we get anything back, as abort_suspend "can't fail"
|
||||
send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::AbortSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).map(|_| ())
|
||||
}
|
||||
// make all the boilerplate comms code shared between all sizes of digest
|
||||
sha512_comms!();
|
||||
}
|
||||
|
||||
impl Default for Sha512 {
|
||||
|
|
@ -167,6 +224,9 @@ impl Drop for Sha512 {
|
|||
// normally, we would de-allocate a connection but because the Digest API assumes that
|
||||
// all instances are fungible we can't do that, as the connection needs to be persistent
|
||||
// between invocations of the object.
|
||||
if !self.use_soft {
|
||||
self.reset_hw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,38 +236,7 @@ impl BlockInput for Sha512 {
|
|||
|
||||
impl Update for Sha512 {
|
||||
fn update(&mut self, input: impl AsRef<[u8]>) {
|
||||
if !self.in_progress && (self.strategy != FallbackStrategy::SoftwareOnly) {
|
||||
loop {
|
||||
let conn = self.ensure_conn(); // also ensures the ID
|
||||
let response = send_message(conn,
|
||||
Message::new_blocking_scalar(Opcode::AcquireExclusive.to_usize().unwrap(),
|
||||
TOKEN[0].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[1].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[2].load(Ordering::Relaxed) as usize,
|
||||
Sha2Config::Sha512.to_usize().unwrap(),
|
||||
)
|
||||
).expect("couldn't send AcquireExclusive message to Sha2 hardware!");
|
||||
if let xous::Result::Scalar1(result) = response {
|
||||
if result != 0 {
|
||||
self.use_soft = false;
|
||||
self.in_progress = true;
|
||||
break;
|
||||
} else {
|
||||
if self.strategy == FallbackStrategy::HardwareThenSoftware {
|
||||
self.use_soft = true;
|
||||
self.in_progress = true;
|
||||
break;
|
||||
} else {
|
||||
// this is hardware-exclusive mode, we block until we can get the hardware
|
||||
xous::yield_slice();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("AcquireExclusive had an unexpected error: {:?}", response);
|
||||
panic!("Internal error in AcquireExclusive");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.try_acquire_hw(Sha2Config::Sha512);
|
||||
// split the incoming blocks to page size and send to the engine
|
||||
if self.use_soft {
|
||||
self.engine.update(input.as_ref());
|
||||
|
|
@ -282,17 +311,7 @@ impl Reset for Sha512 {
|
|||
if self.use_soft {
|
||||
self.engine.reset(&H512);
|
||||
} else {
|
||||
send_message(self.ensure_conn(),
|
||||
Message::new_blocking_scalar(Opcode::Reset.to_usize().unwrap(),
|
||||
TOKEN[0].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[1].load(Ordering::Relaxed) as usize,
|
||||
TOKEN[2].load(Ordering::Relaxed) as usize,
|
||||
0,
|
||||
)).expect("couldn't send reset to hardware");
|
||||
// reset internal flags
|
||||
self.length = 0;
|
||||
self.in_progress = false;
|
||||
self.use_soft = true;
|
||||
self.reset_hw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -307,13 +326,46 @@ impl Reset for Sha512 {
|
|||
#[derive(Clone)]
|
||||
pub struct Sha512Trunc256 {
|
||||
engine: Engine512,
|
||||
/// whether or not this current hasher instance will use software or hardware acceleration
|
||||
use_soft: bool,
|
||||
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
|
||||
strategy: FallbackStrategy,
|
||||
/// track if a hash is in progress
|
||||
in_progress: bool,
|
||||
/// track the length of the message processed so far
|
||||
length: u64,
|
||||
}
|
||||
impl Sha512Trunc256 {
|
||||
// use this function instead of default for more control over configuration of the hardware engine
|
||||
pub fn new(maybe_strategy: Option<FallbackStrategy>) -> Self {
|
||||
let strategy = if let Some(strat) = maybe_strategy {
|
||||
strat
|
||||
} else {
|
||||
FallbackStrategy::HardwareThenSoftware
|
||||
};
|
||||
Sha512Trunc256 {
|
||||
use_soft: true,
|
||||
strategy,
|
||||
engine: Engine512::new(&H512_TRUNC_256),
|
||||
in_progress: false,
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
// make all the boilerplate comms code shared between all sizes of digest
|
||||
sha512_comms!();
|
||||
}
|
||||
|
||||
impl Drop for Sha512Trunc256 {
|
||||
fn drop(&mut self) {
|
||||
if !self.use_soft {
|
||||
self.reset_hw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Sha512Trunc256 {
|
||||
fn default() -> Self {
|
||||
Sha512Trunc256 {
|
||||
engine: Engine512::new(&H512_TRUNC_256),
|
||||
}
|
||||
Sha512Trunc256::new(Some(FallbackStrategy::HardwareThenSoftware))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +375,26 @@ impl BlockInput for Sha512Trunc256 {
|
|||
|
||||
impl Update for Sha512Trunc256 {
|
||||
fn update(&mut self, input: impl AsRef<[u8]>) {
|
||||
self.engine.update(input.as_ref());
|
||||
self.try_acquire_hw(Sha2Config::Sha512Trunc256);
|
||||
if self.use_soft {
|
||||
self.engine.update(input.as_ref());
|
||||
} else {
|
||||
for chunk in input.as_ref().chunks(3968) {
|
||||
// one SHA512 block (128 bytes) short of 4096 to give space for struct overhead in page remap handling
|
||||
let mut update = Sha2Update {
|
||||
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
|
||||
buffer: [0; 3968],
|
||||
len: 0,
|
||||
};
|
||||
self.length += (chunk.len() as u64) * 8; // we need to keep track of length in bits
|
||||
for (&src, dest) in chunk.iter().zip(&mut update.buffer) {
|
||||
*dest = src;
|
||||
}
|
||||
update.len = chunk.len() as u16;
|
||||
let buf = Buffer::into_buf(update).expect("couldn't map chunk into IPC buffer");
|
||||
buf.lend(self.ensure_conn(), Opcode::Update.to_u32().unwrap()).expect("hardware rejected our hash chunk!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -331,17 +402,55 @@ impl FixedOutputDirty for Sha512Trunc256 {
|
|||
type OutputSize = U32;
|
||||
|
||||
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
|
||||
self.engine.finish();
|
||||
let s = &self.engine.state[..4];
|
||||
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
|
||||
chunk.copy_from_slice(&v.to_be_bytes());
|
||||
if self.use_soft {
|
||||
self.engine.finish();
|
||||
let s = &self.engine.state[..4];
|
||||
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
|
||||
chunk.copy_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
} else {
|
||||
let result = Sha2Finalize {
|
||||
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
|
||||
result: Sha2Result::Uninitialized,
|
||||
length_in_bits: None,
|
||||
};
|
||||
let mut buf = Buffer::into_buf(result).expect("couldn't map memory for the return buffer");
|
||||
buf.lend_mut(self.ensure_conn(), Opcode::Finalize.to_u32().unwrap()).expect("couldn't finalize");
|
||||
|
||||
let returned: Sha2Finalize = buf.to_original().expect("couldn't decode return buffer");
|
||||
match returned.result {
|
||||
Sha2Result::Sha512Trunc256Result(s) => {
|
||||
if self.length != returned.length_in_bits.expect("hardware did not return a length field!") {
|
||||
panic!("Sha512 hardware did not hash as many bits as we had expected!")
|
||||
}
|
||||
for (dest, &src) in out.chunks_exact_mut(1).zip(s.iter()) {
|
||||
dest.copy_from_slice(&[src])
|
||||
}
|
||||
}
|
||||
Sha2Result::Sha512Result(_) => {
|
||||
panic!("Sha512 hardware returned the wrong type of buffer!");
|
||||
}
|
||||
Sha2Result::SuspendError => {
|
||||
panic!("Hardware was suspended during Sha512 operation, result is invalid.");
|
||||
}
|
||||
Sha2Result::Uninitialized => {
|
||||
panic!("Hardware didn't copy Sha512 hash result to the return buffer.");
|
||||
}
|
||||
Sha2Result::IdMismatch => {
|
||||
panic!("Hardware is not currently processing our block, finalize call has no meaning.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reset for Sha512Trunc256 {
|
||||
fn reset(&mut self) {
|
||||
self.engine.reset(&H512_TRUNC_256);
|
||||
if self.use_soft {
|
||||
self.engine.reset(&H512_TRUNC_256);
|
||||
} else {
|
||||
self.reset_hw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ fn xmain() -> ! {
|
|||
Some(Opcode::AcquireExclusive) => msg_blocking_scalar_unpack!(msg, id0, id1, id2, flags, {
|
||||
if client_id.is_none() && !SUSPEND_PENDING.load(Ordering::Relaxed) {
|
||||
client_id = Some([id0 as u32, id1 as u32, id2 as u32]);
|
||||
//log::trace!("giving {:x?} an exclusive lock", client_id);
|
||||
mode = Some(FromPrimitive::from_usize(flags).unwrap());
|
||||
SUSPEND_FAILURE.store(false, Ordering::Relaxed);
|
||||
HASH_IN_PROGRESS.store(true, Ordering::Relaxed);
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ impl<'a> ShellCmdApi<'a> for Sha {
|
|||
];
|
||||
|
||||
let mut pass: bool = true;
|
||||
let mut hasher = engine_sha512::Sha512Trunc256::new();
|
||||
let mut hasher = engine_sha512::Sha512Trunc256::new(Some(FallbackStrategy::WaitForHardware));
|
||||
|
||||
hasher.update(K_DATA);
|
||||
let digest = hasher.finalize();
|
||||
|
|
|
|||
Loading…
Reference in a new issue