kernel: get immutable borrows working

We can now pass memory immutably between processes

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-04-01 17:27:59 +08:00
parent d6f985c141
commit bedb499edb
5 changed files with 347 additions and 113 deletions

View file

@ -1,16 +1,30 @@
pub use crate::arch::ProcessContext;
use core::{mem, slice};
use xous::{CtxID, MemoryRange, MemorySize, PID, SID};
use xous::{CtxID, MemoryAddress, MemoryRange, MemorySize, PID, SID, Message};
pub struct SenderID {
pub sidx: usize,
pub tidx: usize,
}
impl SenderID {
pub fn from_usize(src: usize) -> Result<SenderID, xous::Error> {
Ok(SenderID {
sidx: src >> 16,
tidx: src & 0xffff,
})
}
}
/// Internal representation of a queued message for a server. This should be
/// exactly 8 words / 32 bytes, yielding 128 queued messages per server
#[repr(usize)]
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
enum QueuedMessage {
Empty,
ScalarMessage(
usize, /* sender */
usize, /* context */
usize, /* sender PID/CtxID */
usize, /* sender base address */
usize, /* id */
usize, /* arg1 */
usize, /* arg2 */
@ -18,8 +32,8 @@ enum QueuedMessage {
usize, /* arg4 */
),
MemoryMessageSend(
usize, /* sender */
usize, /* context */
usize, /* sender PID/CtxID */
usize, /* reserved */
usize, /* id */
usize, /* buf */
usize, /* buf_size */
@ -27,8 +41,8 @@ enum QueuedMessage {
usize, /* valid */
),
MemoryMessageROLend(
usize, /* sender */
usize, /* context */
usize, /* sender PID/CtxID */
usize, /* sender base address */
usize, /* id */
usize, /* buf */
usize, /* buf_size */
@ -36,14 +50,25 @@ enum QueuedMessage {
usize, /* valid */
),
MemoryMessageRWLend(
usize, /* sender */
usize, /* context */
usize, /* sender PID/CtxID */
usize, /* sender base address */
usize, /* id */
usize, /* buf */
usize, /* buf_size */
usize, /* offset */
usize, /* valid */
),
/// When a message is taken that needs to be returned -- such as an ROLend
/// or RWLend -- the slot is replaced with a WaitingResponse token and its
/// index is returned as the message sender. This is used to unblock the
/// sending process.
WaitingResponse(
usize, /* sender PID/CtxID */
usize, /* Client base address */
usize, /* Server base address */
usize, /* Range size */
),
}
/// A pointer to resolve a server ID to a particular process
@ -98,14 +123,60 @@ impl Server {
});
Ok(())
}
pub fn print_queue(&self) {
println!(" Q Queue Head: {}", self.queue_head);
println!(" Q Queue Tail: {}", self.queue_tail);
for (_idx, _entry) in self.queue.iter().enumerate() {
if _entry != &QueuedMessage::Empty {
println!(" Q entry[{}]: {:?}", _idx, _entry);
}
}
}
/// Convert a `QueuedMesage::WaitingResponse` into `QueuedMessage::Empty`
/// and return the pair. Advance the tail. Note that the `idx` could be
/// somewhere other than the tail, but as long as it points to a valid
/// message that's waiting a response, that's acceptable.
pub fn take_waiting_message(
&mut self,
idx: usize,
) -> Result<Option<(PID, CtxID, MemoryAddress, MemoryAddress, MemorySize)>, xous::Error> {
if idx > self.queue.len() {
return Err(xous::Error::BadAddress);
}
let (pid_ctx, server_addr, client_addr, len) = match self.queue[idx] {
QueuedMessage::WaitingResponse(pid_ctx, server_addr, client_addr, len) => {
(pid_ctx, server_addr, client_addr, len)
}
_ => return Err(xous::Error::BadAddress),
};
self.queue[idx] = QueuedMessage::Empty;
self.queue_tail += 1;
if self.queue_tail >= self.queue.len() {
self.queue_tail = 0;
}
let pid = ((pid_ctx >> 16) & 0xff) as PID;
let ctx = (pid_ctx & 0xffff) as CtxID;
let server_addr = match MemoryAddress::new(server_addr) {
Some(o) => o,
None => return Ok(None)
};
let client_addr = MemoryAddress::new(client_addr).expect("client memory address was 0, but server address was not");
let len = MemorySize::new(len).expect("memory length was 0, but address was not None");
Ok(Some((pid, ctx, server_addr, client_addr, len)))
}
/// Remove a message from the server's queue and replace it with
/// QueuedMessage::Empty. Advance the queue pointer while we're at it.
pub fn take_next_message(&mut self) -> Option<(xous::MessageEnvelope, CtxID)> {
/// QueuedMessage::WaitingResponse.
pub fn take_next_message(&mut self, server_idx: usize) -> Option<xous::MessageEnvelope> {
let result = match self.queue[self.queue_tail] {
QueuedMessage::Empty => return None,
QueuedMessage::WaitingResponse(_, _, _, _) => return None,
QueuedMessage::MemoryMessageROLend(
sender,
context,
pid_ctx,
client_addr,
id,
buf,
buf_size,
@ -113,7 +184,7 @@ impl Server {
valid,
) => (
xous::MessageEnvelope {
sender: sender,
sender: self.queue_tail | (server_idx << 16),
message: xous::Message::ImmutableBorrow(xous::MemoryMessage {
id,
buf: MemoryRange::new(buf, buf_size),
@ -121,11 +192,14 @@ impl Server {
valid: MemorySize::new(valid),
}),
},
context,
pid_ctx,
buf,
client_addr,
buf_size,
),
QueuedMessage::MemoryMessageRWLend(
sender,
context,
pid_ctx,
client_addr,
id,
buf,
buf_size,
@ -133,7 +207,7 @@ impl Server {
valid,
) => (
xous::MessageEnvelope {
sender: sender,
sender: self.queue_tail | (server_idx << 16),
message: xous::Message::MutableBorrow(xous::MemoryMessage {
id,
buf: MemoryRange::new(buf, buf_size),
@ -141,25 +215,37 @@ impl Server {
valid: MemorySize::new(valid),
}),
},
context,
pid_ctx,
buf,
client_addr,
buf_size,
),
QueuedMessage::MemoryMessageSend(sender, context, id, buf, buf_size, offset, valid) => {
(
xous::MessageEnvelope {
sender: sender,
message: xous::Message::Move(xous::MemoryMessage {
id,
buf: MemoryRange::new(buf, buf_size),
offset: MemorySize::new(offset),
valid: MemorySize::new(valid),
}),
},
context,
)
}
QueuedMessage::ScalarMessage(sender, context, id, arg1, arg2, arg3, arg4) => (
QueuedMessage::MemoryMessageSend(
pid_ctx,
_reserved,
id,
buf,
buf_size,
offset,
valid,
) => (
xous::MessageEnvelope {
sender: sender,
sender: self.queue_tail | (server_idx << 16),
message: xous::Message::Move(xous::MemoryMessage {
id,
buf: MemoryRange::new(buf, buf_size),
offset: MemorySize::new(offset),
valid: MemorySize::new(valid),
}),
},
pid_ctx,
0,
0,
0,
),
QueuedMessage::ScalarMessage(pid_ctx, _reserved, id, arg1, arg2, arg3, arg4) => (
xous::MessageEnvelope {
sender: self.queue_tail | (server_idx << 16),
message: xous::Message::Scalar(xous::ScalarMessage {
id,
arg1,
@ -168,31 +254,37 @@ impl Server {
arg4,
}),
},
context,
pid_ctx,
0,
0,
0,
),
};
self.queue[self.queue_tail] = QueuedMessage::Empty;
self.queue_tail += 1;
if self.queue_tail >= self.queue.len() {
self.queue_tail = 0;
}
Some(result)
self.queue[self.queue_tail] =
QueuedMessage::WaitingResponse(result.1, result.2, result.3, result.4);
Some(result.0)
}
/// Add the given message to this server's queue.
///
/// # Errors
///
/// * **ServerQueueFull**: The server queue cannot accept any more messages
pub fn queue_message(
&mut self,
context: usize,
envelope: xous::MessageEnvelope,
) -> core::result::Result<(), xous::Error> {
pid: PID,
context: CtxID,
message: xous::Message,
original_address: Option<MemoryAddress>,
) -> core::result::Result<usize, xous::Error> {
if self.queue[self.queue_head] != QueuedMessage::Empty {
return Err(xous::Error::ServerQueueFull);
}
self.queue[self.queue_head] = match envelope.message {
self.queue[self.queue_head] = match message {
xous::Message::Scalar(msg) => QueuedMessage::ScalarMessage(
envelope.sender,
context,
pid as usize | (context << 16),
original_address.map(|x| x.get()).unwrap_or(0),
msg.id,
msg.arg1,
msg.arg2,
@ -200,25 +292,72 @@ impl Server {
msg.arg4,
),
xous::Message::Move(msg) => QueuedMessage::MemoryMessageSend(
envelope.sender,
context,
pid as usize | (context << 16),
original_address.map(|x| x.get()).unwrap_or(0),
msg.id,
msg.buf.addr.get(),
msg.buf.size.get(),
msg.offset.map(|x| x.get()).unwrap_or(0) as usize,
msg.valid.map(|x| x.get()).unwrap_or(0) as usize,
),
xous::Message::MutableBorrow(msg) => QueuedMessage::MemoryMessageRWLend(
pid as usize | (context << 16),
original_address.map(|x| x.get()).unwrap_or(0),
msg.id,
msg.buf.addr.get(),
msg.buf.size.get(),
msg.offset.map(|x| x.get()).unwrap_or(0) as usize,
msg.valid.map(|x| x.get()).unwrap_or(0) as usize,
),
xous::Message::ImmutableBorrow(msg) => QueuedMessage::MemoryMessageROLend(
pid as usize | (context << 16),
original_address.map(|x| x.get()).unwrap_or(0),
msg.id,
msg.buf.addr.get(),
msg.buf.size.get(),
msg.offset.map(|x| x.get()).unwrap_or(0) as usize,
msg.valid.map(|x| x.get()).unwrap_or(0) as usize,
),
xous::Message::MutableBorrow(_msg) => unimplemented!(),
xous::Message::ImmutableBorrow(_msg) => unimplemented!(),
};
let idx = self.queue_head;
self.queue_head += 1;
if self.queue_head >= self.queue.len() {
self.queue_head = 0;
}
Ok(())
Ok(idx)
}
pub fn queue_address(
&mut self,
pid: PID,
context: CtxID,
message: &Message,
client_address: Option<MemoryAddress>,
) -> core::result::Result<usize, xous::Error> {
if self.queue[self.queue_head] != QueuedMessage::Empty {
return Err(xous::Error::ServerQueueFull);
}
let (server_address, len) = match message {
xous::Message::Scalar(_) | xous::Message::Move(_) => (0, 0),
xous::Message::MutableBorrow(msg) | xous::Message::ImmutableBorrow(msg) => {
(msg.buf.addr.get(), msg.buf.size.get())
}
};
self.queue[self.queue_head] = QueuedMessage::WaitingResponse(
pid as usize | (context << 16),
server_address,
client_address.map(|x| x.get()).unwrap_or(0),
len,
);
let idx = self.queue_head;
self.queue_head += 1;
if self.queue_head >= self.queue.len() {
self.queue_head = 0;
}
Ok(idx)
}
// assert!(
// mem::size_of::<QueuedMessage>() == 32,
// "QueuedMessage was supposed to be 32 bytes, but instead was {} bytes",

View file

@ -6,8 +6,8 @@ use crate::args::KernelArguments;
use crate::filled_array;
use crate::mem::{MemoryManagerHandle, PAGE_SIZE};
use crate::server::Server;
use core::{mem, slice};
use xous::{CtxID, MemoryFlags, MemoryType, MessageEnvelope, CID, PID, SID};
use core::slice;
use xous::{CtxID, MemoryAddress, MemoryFlags, MemoryType, Message, CID, PID, SID};
const MAX_PROCESS_COUNT: usize = 32;
const MAX_SERVER_COUNT: usize = 32;
@ -634,10 +634,10 @@ impl SystemServices {
// Restore the previous context, if one exists.
process.set_context_nr(new_context);
self.processes[self.pid as usize - 1].current_context = new_context as u8;
let ctx = process.current_context();
let _ctx = process.current_context();
println!(
"Switched to PID {}, context {}, with sepc: {:08x}",
new_pid, new_context, ctx.sepc
new_pid, new_context, _ctx.sepc
);
Ok(new_context)
@ -664,6 +664,7 @@ impl SystemServices {
&mut self,
src_virt: *mut usize,
dest_pid: PID,
dest_virt: *mut usize,
len: usize,
writable: bool,
_borrow: bool,
@ -704,7 +705,7 @@ impl SystemServices {
}
let result = mm.map_range(
phys as *mut usize,
0 as *mut usize,
dest_virt,
len,
dest_pid,
flags,
@ -775,7 +776,7 @@ impl SystemServices {
/// server table is full, return an error.
pub fn create_server(&mut self, name: usize) -> Result<SID, xous::Error> {
println!("Looking through server list for free server");
println!("Server entries are {} bytes long", mem::size_of::<Server>());
println!("Server entries are {} bytes long", core::mem::size_of::<Server>());
for entry in self.servers.iter_mut() {
if entry == &None {
@ -863,12 +864,16 @@ impl SystemServices {
}
}
/// Switch to the server's memory space and add the message to its server
/// queue
pub fn queue_server_message(
&mut self,
sidx: usize,
context: usize,
envelope: MessageEnvelope,
) -> Result<(), xous::Error> {
pid: PID,
context: CtxID,
message: Message,
original_address: Option<MemoryAddress>,
) -> Result<usize, xous::Error> {
let current_pid = self.current_pid();
let result = {
let server_pid = self
@ -882,7 +887,9 @@ impl SystemServices {
let server = self
.server_from_sidx(sidx)
.expect("couldn't re-discover server index");
server.queue_message(context, envelope)
let result = server.queue_message(pid, context, message, original_address);
server.print_queue();
result
};
let current_process = self
.get_process(current_pid)
@ -891,18 +898,51 @@ impl SystemServices {
result
}
/// Get a server based on a SID
pub fn server_mut(&mut self, sid: SID) -> Option<&mut Server> {
for server in self.servers.iter_mut() {
/// Switch to the server's address space and add a "remember this address"
/// entry to its server queue
pub fn remember_server_message(
&mut self,
sidx: usize,
pid: PID,
context: CtxID,
message: &Message,
client_address: Option<MemoryAddress>,
) -> Result<usize, xous::Error> {
let current_pid = self.current_pid();
let result = {
let server_pid = self
.server_from_sidx(sidx)
.ok_or(xous::Error::ServerNotFound)?
.pid;
{
let server_process = self.get_process(server_pid)?;
server_process.mapping.activate();
}
let server = self
.server_from_sidx(sidx)
.expect("couldn't re-discover server index");
let result = server.queue_address(pid, context, message, client_address);
server.print_queue();
result
};
let current_process = self
.get_process(current_pid)
.expect("couldn't restore previous process");
current_process.mapping.activate();
result
}
/// Get a server index based on a SID
pub fn server_sidx(&mut self, sid: SID) -> Option<usize> {
for (idx, server) in self.servers.iter_mut().enumerate() {
if let Some(active_server) = server {
if active_server.sid == sid {
return server.as_mut();
return Some(idx);
}
}
}
None
}
}
}}
/// How many people have checked out the handle object. This should be replaced
/// by an AtomicUsize when we get multicore support. For now, we can get away

View file

@ -2,6 +2,7 @@ use crate::arch;
use crate::arch::process::ProcessHandle;
use crate::irq::interrupt_claim;
use crate::mem::{MemoryManagerHandle, PAGE_SIZE};
use crate::server::SenderID;
use crate::services::SystemServicesHandle;
use core::mem;
use xous::*;
@ -264,7 +265,11 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
let mut ss = SystemServicesHandle::get();
let context_nr = ss.current_context_nr();
// See if there is a pending message. If so, return immediately.
let server = ss.server_mut(sid).ok_or(xous::Error::ServerNotFound)?;
let sidx = ss.server_sidx(sid).ok_or(xous::Error::ServerNotFound)?;
let server = ss
.server_from_sidx(sidx)
.ok_or(xous::Error::ServerNotFound)?;
server.print_queue();
// Ensure the server is for this PID
if server.pid != pid {
@ -272,12 +277,16 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
}
// If there is a pending message, return it immediately.
if let Some(msg) = server.take_next_message() {
return Ok(xous::Result::Message(msg.0));
if let Some(msg) = server.take_next_message(sidx) {
println!("PID {} had a message ready -- returning it", pid);
return Ok(xous::Result::Message(msg));
}
// There is no pending message, so return control to the parent process
// and mark ourselves as awaiting an event.
// There is no pending message, so return control to the parent
// process and mark ourselves as awaiting an event. When a message
// arrives, our return value will already be set to the
// MessageEnvelope of the incoming message.
println!("PID {} did not have any waiting messages -- parking context", pid);
server.park_context(context_nr);
let ppid = ss.get_process(pid).expect("Can't get current process").ppid;
@ -309,6 +318,51 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
ss.connect_to_server(sid)
.map(|x| xous::Result::ConnectionID(x))
}
SysCall::ReturnMemory(sender) => {
let mut ss = SystemServicesHandle::get();
let sender = SenderID::from_usize(sender)?;
let server = ss
.server_from_sidx(sender.sidx)
.ok_or(xous::Error::ServerNotFound)?;
if server.pid != pid {
return Err(xous::Error::ServerNotFound);
}
let result = server.take_waiting_message(sender.tidx)?;
let (client_pid, client_ctx, server_addr, client_addr, len) = match result {
Some(s) => s,
None => {
println!("Tried to return memory to client, but address was bad. Assuming this is not memory that needs to be returned.");
return Ok(xous::Result::Ok);
}
};
println!(
"Returning {} bytes from {:08x} in PID {} to {:08x} in PID {} in context {}",
len,
server_addr.get(),
pid,
client_addr.get(),
client_pid,
client_ctx
);
// Return the memory to the other process
// XXX This should keep the same previous write permissions as it used to have!
ss.send_memory(
server_addr.get() as *mut usize,
client_pid,
client_addr.get() as *mut usize,
len.get(),
true,
false,
)?;
// Unblock the client context to allow it to continue.
println!("Unblocking PID {} CTX {}", client_pid, client_ctx);
ss.ready_context(client_pid, client_ctx)?;
ss.set_context_result(client_pid, client_ctx, xous::Result::Ok)?;
Ok(xous::Result::Ok)
}
SysCall::SendMessage(cid, message) => {
let mut ss = SystemServicesHandle::get();
let context_nr = ss.current_context_nr();
@ -319,19 +373,29 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
.expect("server couldn't be located")
.pid;
// Remember the address the message came from, in case we need to
// return it after the borrow is through.
let client_address = match &message {
Message::Scalar(_) => None,
Message::Move(msg)
| Message::MutableBorrow(msg)
| Message::ImmutableBorrow(msg) => Some(msg.buf.addr),
};
// Translate memory messages from the client process to the server
// process. Additionally, determine whether the call is blocking.
// If so, switch to the server context right away.
// process. Additionally, determine whether the call is blocking. If
// so, switch to the server context right away.
let (message, blocking) = match message {
Message::Scalar(_) => (message, false),
Message::Move(msg) => {
let new_virt = ss.send_memory(
msg.buf.as_mut_ptr(),
server_pid,
msg.buf.len(),
true,
false,
)?;
msg.buf.as_mut_ptr(),
server_pid,
0 as *mut usize,
msg.buf.len(),
true,
false,
)?;
(
Message::Move(MemoryMessage {
id: msg.id,
@ -345,12 +409,13 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
Message::MutableBorrow(_) => unimplemented!(),
Message::ImmutableBorrow(msg) => {
let new_virt = ss.send_memory(
msg.buf.as_mut_ptr(),
server_pid,
msg.buf.len(),
false,
true,
)?;
msg.buf.as_mut_ptr(),
server_pid,
0 as *mut usize,
msg.buf.len(),
false,
true,
)?;
(
Message::ImmutableBorrow(MemoryMessage {
id: msg.id,
@ -361,12 +426,6 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
true,
)
}
,
};
let envelope = MessageEnvelope {
sender: (((pid as usize) << 16) & 0xffff0000) | ((context_nr << 0) & 0xffff),
message,
};
// If the server has an available context to receive the message,
@ -380,6 +439,10 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
"There are contexts available to handle this message. Marking PID {} as Ready",
server_pid
);
let sender =
ss.remember_server_message(sidx, pid, context_nr, &message, client_address)?;
let envelope = MessageEnvelope { sender, message };
ss.ready_context(server_pid, ctx_number)?;
if blocking {
println!("Activating Server context and switching away from Client");
@ -399,10 +462,7 @@ pub fn handle(call: SysCall) -> core::result::Result<xous::Result, xous::Error>
// Add this message to the queue. If the queue is full, this
// returns an error.
// let server = ss
// .server_from_sidx(sidx)
// .ok_or(xous::Error::ServerNotFound)?;
ss.queue_server_message(sidx, context_nr, envelope)?;
ss.queue_server_message(sidx, pid, context_nr, message, client_address)?;
// Park this context if it's blocking. This is roughly
// equivalent to a "Yield".

View file

@ -109,15 +109,10 @@ pub struct MessageEnvelope {
/// (in the case of a Borrow). Ignore Scalar messages.
impl Drop for MessageEnvelope {
fn drop(&mut self) {
match &self.message {
Message::MutableBorrow(msg) | Message::ImmutableBorrow(msg) => {
crate::syscall::return_memory(self.sender, msg.buf).expect("couldn't return memory");
}
Message::Move(msg) => {
crate::syscall::unmap_memory(msg.buf.addr, msg.buf.size)
crate::syscall::return_memory(self.sender).expect("couldn't return memory");
if let Message::Move(msg) = &self.message {
crate::syscall::unmap_memory(msg.buf.addr, msg.buf.size)
.expect("couldn't free memory message");
}
Message::Scalar(_) => (),
}
}
}

View file

@ -283,7 +283,7 @@ pub enum SysCall {
SendMessage(CID, Message),
/// Return a Borrowed memory region to a sender
ReturnMemory(MessageSender, MemoryRange),
ReturnMemory(MessageSender),
/// Spawn a new thread
SpawnThread(
@ -499,8 +499,8 @@ impl SysCall {
sc.arg4,
],
},
SysCall::ReturnMemory(a1, a2) => [SysCallNumber::ReturnMemory as usize,
*a1, a2.addr.get(), a2.addr.get(), 0, 0, 0, 0],
SysCall::ReturnMemory(a1) => [SysCallNumber::ReturnMemory as usize,
*a1, 0, 0, 0, 0, 0, 0],
SysCall::SpawnThread(a1, a2, a3) => [
SysCallNumber::SpawnThread as usize,
*a1 as usize,
@ -606,7 +606,7 @@ impl SysCall {
),
_ => SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7),
},
Some(SysCallNumber::ReturnMemory) => SysCall::ReturnMemory(a1, MemoryRange::new(a2, a3)),
Some(SysCallNumber::ReturnMemory) => SysCall::ReturnMemory(a1),
Some(SysCallNumber::SpawnThread) => {
SysCall::SpawnThread(a1 as *mut usize, a2 as *mut usize, a3 as *mut usize)
}
@ -711,8 +711,8 @@ pub fn unmap_memory(virt: MemoryAddress, size: MemorySize) -> core::result::Resu
/// Map the given physical address to the given virtual address.
/// The `size` field must be page-aligned.
pub fn return_memory(sender: MessageSender, range: MemoryRange) -> core::result::Result<(), Error> {
let result = rsyscall(SysCall::ReturnMemory(sender, range))?;
pub fn return_memory(sender: MessageSender) -> core::result::Result<(), Error> {
let result = rsyscall(SysCall::ReturnMemory(sender))?;
if let Result::Ok = result {
Ok(())
} else if let Result::Error(e) = result {