xous: fix memory messages in hosted environment

This fixes passing memory between clients and servers when running
n a hosted environment.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-10-29 19:40:41 +08:00
parent ca32e4f9c1
commit 7aab01c95d
3 changed files with 173 additions and 70 deletions

View file

@ -445,6 +445,7 @@ pub fn _xous_syscall(
if *ret != Result::WouldBlock {
return;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
})
});
@ -506,7 +507,10 @@ fn _xous_syscall_result(
// This thread_id doesn't exist in the mailbox, so read additional data.
let mut pkt = [0usize; 8];
let mut raw_bytes = [0u8; size_of::<usize>() * 9];
stream.read_exact(&mut raw_bytes).expect("Server shut down");
if let Err(e) = stream.read_exact(&mut raw_bytes) {
eprintln!("Server shut down: {}", e);
std::process::exit(0);
}
let mut raw_bytes_chunks = raw_bytes.chunks(size_of::<usize>());
@ -518,66 +522,71 @@ fn _xous_syscall_result(
*pkt_word = usize::from_le_bytes(word.try_into().unwrap());
}
let response = Result::from_args(pkt);
let mut response = Result::from_args(pkt);
// println!(" Response: {:?}", response);
// println!(" Response to {:?}: {:?}", call, response);
if Result::BlockedProcess == response {
// println!(" Waiting again");
continue;
}
match &call {
crate::SysCall::SendMessage(_, msg) | crate::SysCall::TrySendMessage(_, msg) => {
match msg {
crate::Message::MutableBorrow(crate::MemoryMessage {
id: _id,
buf,
offset: _offset,
valid: _valid,
}) => {
// Read the buffer back from the remote host.
use core::slice;
let mut data = unsafe {
slice::from_raw_parts_mut(buf.addr.get() as _, buf.size.get())
};
stream.read_exact(&mut data).expect("Server shut down");
// pkt.extend_from_slice(data);
}
crate::Message::Borrow(crate::MemoryMessage {
id: _id,
buf,
offset: _offset,
valid: _valid,
}) => {
// Read the buffer back from the remote host and ensure it's the same
use core::slice;
let mut check_data = Vec::new();
check_data.resize(buf.len(), 0);
let data =
unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) };
stream
.read_exact(&mut check_data)
.expect("Server shut down");
assert_eq!(data, check_data.as_slice());
// If the client is passing us memory, remap the array to our own space.
if let Result::Message(msg) = &mut response {
match &mut msg.body {
crate::Message::Move(ref mut memory_message)
| crate::Message::Borrow(ref mut memory_message)
| crate::Message::MutableBorrow(ref mut memory_message) => {
let data = vec![0u8; memory_message.buf.len()];
let mut data = std::mem::ManuallyDrop::new(data);
// println!("KERNEL: Reading {} bytes of data from network", data.len());
if let Err(e) = stream.read_exact(&mut data) {
eprintln!("Server shut down: {}", e);
std::process::exit(0);
}
let len = data.len();
let addr = data.as_mut_ptr();
crate::Message::Move(crate::MemoryMessage {
id: _id,
buf,
offset: _offset,
valid: _valid,
}) => {
// In a hosted environment, the message contents are leaked when
// it gets converted into a MemoryMessage. Now that the call is
// complete, free the memory.
mem::unmap_memory_post(*buf).unwrap();
}
// Nothing to do for Immutable borrow, since the memory can't change
crate::Message::Scalar(_) | crate::Message::BlockingScalar(_) => (),
memory_message.buf.addr = crate::MemoryAddress::new(addr as _).unwrap();
memory_message.buf.size = crate::MemorySize::new(len).unwrap();
}
_ => (),
}
}
// If the original call contained memory, then ensure the memory we get back is correct.
if let Some(mem) = call.memory() {
if call.is_borrow() || call.is_mutableborrow() {
// Read the buffer back from the remote host.
use core::slice;
let mut data =
unsafe { slice::from_raw_parts_mut(mem.addr.get() as _, mem.size.get()) };
let previous_data = if call.is_borrow() {
Some(data.to_vec())
} else {
None
};
if let Err(e) = stream.read_exact(&mut data) {
eprintln!("Server shut down: {}", e);
std::process::exit(0);
}
// If it is an immutable borrow, verify the contents haven't changed somehow
if let Some(previous_data) = previous_data {
assert_eq!(data, previous_data.as_slice());
}
}
_ => (),
if call.is_move() {
// In a hosted environment, the message contents are leaked when
// it gets converted into a MemoryMessage. Now that the call is
// complete, free the memory.
mem::unmap_memory_post(mem).unwrap();
}
if call.is_return_memory() {
let rebuilt = unsafe { Vec::from_raw_parts(mem.as_mut_ptr(), mem.len(), mem.len()) };
drop(rebuilt);
}
}
// Now that we have the Stream mutex, temporarily take the Mailbox mutex to see if
@ -622,22 +631,16 @@ fn _xous_syscall_to(
for word in &[nr, a1, a2, a3, a4, a5, a6, a7] {
pkt.extend_from_slice(&word.to_le_bytes());
}
match call {
crate::SysCall::SendMessage(_, ref msg) | crate::SysCall::TrySendMessage(_, ref msg) => {
match msg {
crate::Message::MutableBorrow(m)
| crate::Message::Borrow(m)
| crate::Message::Move(m) => {
use core::slice;
let data: &[u8] =
unsafe { slice::from_raw_parts(m.buf.addr.get() as _, m.buf.size.get()) };
pkt.extend_from_slice(data);
}
crate::Message::Scalar(_) | crate::Message::BlockingScalar(_) => (),
}
}
_ => (),
// Also send memory, if it's present.
if let Some(memory) = call.memory() {
use core::slice;
let data: &[u8] = unsafe { slice::from_raw_parts(memory.addr.get() as _, memory.size.get()) };
pkt.extend_from_slice(data);
}
xsc.write_all(&pkt).expect("Server shut down");
if let Err(e) = xsc.write_all(&pkt) {
eprintln!("Server shut down: {}", e);
std::process::exit(0);
}
}

View file

@ -369,6 +369,10 @@ impl MemoryRange {
addr != 0,
"tried to construct a memory range with a null pointer"
);
assert!(
size != 0,
"tried to construct a zero-length memory range"
);
Ok(MemoryRange {
addr: MemoryAddress::new(addr).ok_or(Error::BadAddress)?,
size: MemorySize::new(size).ok_or(Error::BadAddress)?,
@ -575,6 +579,20 @@ impl Result {
_ => Result::UnknownResult(src[0], src[1], src[2], src[3], src[4], src[5], src[6]),
}
}
/// If the Result has memory attached to it, return the memory
pub fn memory(&self) -> Option<MemoryRange> {
match self {
Result::Message(msg) => match &msg.body {
Message::Move(memory_message)
| Message::Borrow(memory_message)
| Message::MutableBorrow(memory_message) => Some(memory_message.buf),
_ => None,
},
_ => None,
}
}
}
impl From<Error> for Result {

View file

@ -580,8 +580,26 @@ impl SysCall {
sc.arg4,
],
},
SysCall::ReturnScalar1(sender, arg1) => [SysCallNumber::ReturnScalar1 as usize, *sender, *arg1, 0, 0, 0, 0, 0],
SysCall::ReturnScalar2(sender, arg1, arg2) => [SysCallNumber::ReturnScalar2 as usize, *sender, *arg1, *arg2, 0, 0, 0, 0],
SysCall::ReturnScalar1(sender, arg1) => [
SysCallNumber::ReturnScalar1 as usize,
*sender,
*arg1,
0,
0,
0,
0,
0,
],
SysCall::ReturnScalar2(sender, arg1, arg2) => [
SysCallNumber::ReturnScalar2 as usize,
*sender,
*arg1,
*arg2,
0,
0,
0,
0,
],
SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7) => [
SysCallNumber::Invalid as usize,
*a1,
@ -769,6 +787,66 @@ impl SysCall {
SysCallNumber::Invalid => SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7),
})
}
/// Returns `true` if the associated syscall is a message that has memory attached to it
pub fn has_memory(&self) -> bool {
match self {
SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => {
matches!(msg, Message::Move(_) | Message::Borrow(_) | Message::MutableBorrow(_))
}
SysCall::ReturnMemory(_, _) => true,
_ => false,
}
}
/// Returns `true` if the associated syscall is a message that is a Move
pub fn is_move(&self) -> bool {
match self {
SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => {
matches!(msg, Message::Move(_))
}
_ => false,
}
}
/// Returns `true` if the associated syscall is a message that is a Borrow
pub fn is_borrow(&self) -> bool {
match self {
SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => {
matches!(msg, Message::Borrow(_))
}
_ => false,
}
}
/// Returns `true` if the associated syscall is a message that is a MutableBorrow
pub fn is_mutableborrow(&self) -> bool {
match self {
SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => {
matches!(msg, Message::MutableBorrow(_))
}
_ => false,
}
}
/// Returns `true` if the associated syscall is returning memory
pub fn is_return_memory(&self) -> bool {
matches!(self, SysCall::ReturnMemory(_, _))
}
/// If the syscall has memory attached to it, return the memory
pub fn memory(&self) -> Option<MemoryRange> {
match self {
SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => match msg {
Message::Move(memory_message)
| Message::Borrow(memory_message)
| Message::MutableBorrow(memory_message) => Some(memory_message.buf),
_ => None,
},
SysCall::ReturnMemory(_, range) => Some(*range),
_ => None,
}
}
}
extern "Rust" {
@ -865,7 +943,11 @@ pub fn return_scalar(sender: MessageSender, val: usize) -> core::result::Result<
/// Map the given physical address to the given virtual address.
/// The `size` field must be page-aligned.
pub fn return_scalar2(sender: MessageSender, val1: usize, val2: usize) -> core::result::Result<(), Error> {
pub fn return_scalar2(
sender: MessageSender,
val1: usize,
val2: usize,
) -> core::result::Result<(), Error> {
let result = rsyscall(SysCall::ReturnScalar2(sender, val1, val2))?;
if let crate::Result::Ok = result {
Ok(())