initial commit

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-12-29 17:53:46 +08:00
commit 87b48c3665
31 changed files with 4218 additions and 0 deletions

18
.gitattributes vendored Normal file
View File

@ -0,0 +1,18 @@
*.py text eol=lf
*.dfu binary
*.png binary
*.jpg binary
*.bin binary
*.elf binary
*.h text eol=lf
*.c text eol=lf
*.s text eol=lf
*.S text eol=lf
README.* text eol=lf
LICENSE text eol=lf
Makefile text eol=lf
*.mk text eol=lf
*.sh text eol=lf
*.ps1 text eol=crlf
.gitignore text eol=lf
.gitattributes text eol=lf

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

133
Cargo.lock generated Normal file
View File

@ -0,0 +1,133 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "hex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "ipc-test"
version = "0.1.0"
dependencies = [
"xous",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"rand_chacha",
"rand_core",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
]
[[package]]
name = "syn"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "xous"
version = "0.1.0"
dependencies = [
"bitflags",
"hex",
"lazy_static",
"xous-macros",
]
[[package]]
name = "xous-macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn",
]

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "ipc-test"
version = "0.1.0"
authors = ["Sean Cross <sean@xobs.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
xous = { path = "xous-rs" }

98
macros/Cargo.lock generated Normal file
View File

@ -0,0 +1,98 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "proc-macro2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"rand_chacha",
"rand_core",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
]
[[package]]
name = "syn"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "xous-macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn",
]

29
macros/Cargo.toml Normal file
View File

@ -0,0 +1,29 @@
[package]
authors = [
"Sean Cross <sean@xobs.io>"
]
categories = ["embedded", "no-std"]
description = "Attributes re-exported in `xous`"
documentation = "https://docs.rs/xous"
keywords = ["xous", "runtime", "startup"]
license = "MIT OR Apache-2.0"
name = "xous-macros"
repository = "https://github.com/betrusted/xous-core"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
proc-macro2 = "1.0"
[dependencies.syn]
version = "1.0"
features = ["extra-traits", "full"]
[dependencies.rand]
version = "0.7.3"
default-features = false
features = ["small_rng"]

137
macros/src/lib.rs Normal file
View File

@ -0,0 +1,137 @@
// #![deny(warnings)]
extern crate proc_macro;
extern crate rand;
#[macro_use]
extern crate quote;
extern crate core;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
use proc_macro2::Span;
use rand::Rng;
use rand::SeedableRng;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use syn::{parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
use proc_macro::TokenStream;
/// Attribute to declare the entry point of the program
///
/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
/// private modules between the item and the root of the crate); if the item is in the root of the
/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
///
/// The specified function will be called by the reset handler *after* RAM has been initialized.
/// If present, the FPU will also be enabled before the function is called.
///
/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
///
/// # Properties
///
/// The entry point will be called by the reset handler. The program can't reference to the entry
/// point, much less invoke it.
///
/// # Examples
///
/// - Simple entry point
///
/// ``` no_run
/// # #![no_main]
/// # use riscv_rt_macros::entry;
/// #[entry]
/// fn main() -> ! {
/// loop {
/// /* .. */
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn xous_main(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.sig.constness.is_none()
&& f.sig.asyncness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.is_empty()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => false,
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[xous_main]` function must have signature `[unsafe] fn() -> !`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let unsafety = f.sig.unsafety;
let hash = random_ident();
let stmts = f.block.stmts;
let r = quote!(
#[export_name = "xous_entry"]
#(#attrs)*
pub #unsafety fn #hash() -> ! {
xous::init();
#(#stmts)*
}
xous::maybe_main!();
);
r.into()
}
// Creates a random identifier
fn random_ident() -> Ident {
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
let mut seed: [u8; 16] = [0; 16];
for (i, v) in seed.iter_mut().take(8).enumerate() {
*v = ((secs >> (i * 8)) & 0xFF) as u8
}
for (i, v) in seed.iter_mut().skip(8).enumerate() {
*v = ((count >> (i * 8)) & 0xFF) as u8
}
let mut rng = rand::rngs::SmallRng::from_seed(seed);
Ident::new(
&(0..16)
.map(|i| {
if i == 0 || rng.gen() {
(b'a' + rng.gen::<u8>() % 25) as char
} else {
(b'0' + rng.gen::<u8>() % 10) as char
}
})
.collect::<String>(),
Span::call_site(),
)
}

47
src/main.rs Normal file
View File

@ -0,0 +1,47 @@
#[repr(C)]
#[derive(Default)]
struct SendableThing {
one: usize,
two: u32,
three: u8,
four: u16,
}
// #[repr(C)]
// struct UnSendableThing {
// badness: *const u8,
// }
#[xous::xous_main]
fn main() -> ! {
// let mut unsendable = xous::ipc::Sendable::new(UnSendableThing {
// badness: 4 as *const u8,
// });
// Allocate an uninitialized SendableThing. Note we must explicitly call out what it is
// we're constructing, since it's ambiguous. This is the Turbofish operator (i.e. ::<T>)
let _st1_uninit = xous::ipc::UninitializedSendable::<SendableThing>::uninit().unwrap();
// {{{something here involving uninitialized values}}}
let _st1 = unsafe { _st1_uninit.assume_init() };
// Pass a default value to the constructor
let mut _st2 = xous::ipc::Sendable::new(SendableThing {
one: 1,
two: 10,
three: 3,
four: 4,
}).unwrap();
// Note that you can mostly ignore the `SendableThing<T>` wrapper and
// directly manipulate fields.
_st2.two = 2;
_st2.four += 1;
// Since this implements `Default`, we can just call the `default()` constructor.
// This also uses Turbofish.
let _st3 = xous::ipc::Sendable::<SendableThing>::default();
// It's also possible to simply be explicit about what type we want.
let _st4: xous::ipc::Sendable<SendableThing> = xous::ipc::Sendable::default();
loop {}
}

28
xous-rs/Cargo.toml Normal file
View File

@ -0,0 +1,28 @@
[package]
name = "xous"
version = "0.1.0"
authors = ["Sean Cross <sean@xobs.io>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "System call interface for Xous"
[dependencies]
bitflags = "1"
xous-macros = { path = "../macros", version = "0.1.0" }
log = { version = "0.4", optional = true }
[features]
# If this is set, then the "Drop" feature of MemoryMessage structs
# will not be implemented. This should only be set by the kernel.
forget-memory-messages = []
# `logging` causes this program to connect to a log server on startup
# so you can run log commands such as `info!()`.
logging = ["log"]
default = []
[target.'cfg(any(windows,unix))'.dependencies]
lazy_static = "1.4"
hex = "0.4"
# debug-here = "*"

18
xous-rs/assemble.ps1 Normal file
View File

@ -0,0 +1,18 @@
# remove existing blobs because otherwise this will append object files to the old blobs
New-Item -Type Directory -Name bin -Force | Out-Null
Remove-Item -Force bin/*.a | Out-Null
$crate = "xous"
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32imac src/asm.S -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32imac-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32imc-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32i src/asm.S -DSKIP_MULTICORE -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=lp64 -march=rv64imac src/asm.S -o bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv64imac-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-ar crs bin/riscv64gc-unknown-none-elf.a bin/$crate.o
Remove-Item bin/$crate.o

22
xous-rs/assemble.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -euxo pipefail
crate=xous-kernel
# remove existing blobs because otherwise this will append object files to the old blobs
mkdir -p bin
rm -f bin/*.a
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32imac src/asm.S -o bin/$crate.o
ar crs bin/riscv32imac-unknown-none-elf.a bin/$crate.o
ar crs bin/riscv32imc-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32i src/asm.S -DSKIP_MULTICORE -o bin/$crate.o
ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
riscv64-unknown-elf-gcc -ggdb3 -c -mabi=lp64 -march=rv64imac src/asm.S -o bin/$crate.o
ar crs bin/riscv64imac-unknown-none-elf.a bin/$crate.o
ar crs bin/riscv64gc-unknown-none-elf.a bin/$crate.o
rm bin/$crate.o

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

35
xous-rs/build.rs Normal file
View File

@ -0,0 +1,35 @@
// NOTE: Adapted from cortex-m/build.rs
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let target = env::var("TARGET").unwrap();
let name = env::var("CARGO_PKG_NAME").unwrap();
let target_os = target.split('-').nth(2).unwrap_or("none");
// If we're not running on a desktop-class operating system, emit the "baremetal"
// config setting. This will enable software to do tasks such as
// managing memory.
if target_os == "none" {
println!("Target {} is bare metal", target);
println!("cargo:rustc-cfg=baremetal");
} else {
println!("Target {} is NOT bare metal", target);
}
if target.starts_with("riscv") {
fs::copy(
format!("bin/{}.a", target),
out_dir.join(format!("lib{}.a", name)),
).unwrap();
println!("cargo:rustc-link-lib=static={}", name);
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed=bin/{}.a", target);
}
println!("cargo:rerun-if-changed=build.rs");
}

683
xous-rs/src/arch/hosted.rs Normal file
View File

@ -0,0 +1,683 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryInto;
use std::io::{Read, Write};
use std::mem::size_of;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream, ToSocketAddrs};
use std::sync::{Arc, Mutex};
use std::thread_local;
use crate::{Result, PID, TID};
mod mem;
pub use mem::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ProcessKey([u8; 16]);
impl ProcessKey {
pub fn new(key: [u8; 16]) -> ProcessKey {
ProcessKey(key)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ThreadInit {}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ProcessInit {
pub key: ProcessKey,
}
pub struct ProcessArgsAsThread<F: FnOnce()> {
main: F,
name: String,
}
impl<F> ProcessArgsAsThread<F>
where
F: FnOnce(),
{
pub fn new(name: &str, main: F) -> ProcessArgsAsThread<F> {
ProcessArgsAsThread {
main,
name: name.to_owned(),
}
}
}
pub struct ProcessHandleAsThread(std::thread::JoinHandle<()>);
/// If no connection exists, create a new connection to the server. This means
/// our parent PID will be PID1. Otherwise, reuse the same connection.
pub fn create_process_pre_as_thread<F>(
_args: &ProcessArgsAsThread<F>,
) -> core::result::Result<ProcessInit, crate::Error>
where
F: FnOnce(),
{
ensure_connection()?;
// Ensure there is a connection, because after this function returns
// we'll make a syscall with CreateProcess(). This should only need
// to happen for PID1.
Ok(ProcessInit {
key: PROCESS_KEY
.with(|pk| *pk.borrow())
.unwrap_or_else(default_process_key),
})
}
pub fn create_process_post_as_thread<F>(
args: ProcessArgsAsThread<F>,
init: ProcessInit,
pid: PID,
) -> core::result::Result<ProcessHandleAsThread, crate::Error>
where
F: FnOnce() + Send + 'static,
{
let server_address = xous_address();
let f = args.main;
let thread_main = std::thread::Builder::new()
.name(args.name)
.spawn(move || {
set_xous_address(server_address);
THREAD_ID.with(|tid| *tid.borrow_mut() = 1);
PROCESS_ID.with(|p| *p.borrow_mut() = pid);
XOUS_SERVER_CONNECTION.with(|xsc| {
let mut xsc = xsc.borrow_mut();
match xous_connect_impl(server_address, &init.key) {
Ok(a) => {
*xsc = Some(a);
Ok(())
}
Err(_) => Err(crate::Error::InternalError),
}
})?;
crate::create_thread(f)
})
.map_err(|_| crate::Error::InternalError)?
.join()
.unwrap()
.unwrap();
Ok(ProcessHandleAsThread(thread_main.0))
}
pub fn wait_process_as_thread(joiner: ProcessHandleAsThread) -> crate::SysCallResult {
joiner.0.join().map(|_| Result::Ok).map_err(|_x| {
// panic!("wait error: {:?}", x);
crate::Error::InternalError
})
}
pub struct ProcessArgs {
command: String,
name: String,
}
impl ProcessArgs {
pub fn new(name: &str, command: String) -> ProcessArgs {
ProcessArgs {
command,
name: name.to_owned(),
}
}
}
#[derive(Debug)]
pub struct ProcessHandle(std::process::Child);
/// If no connection exists, create a new connection to the server. This means
/// our parent PID will be PID1. Otherwise, reuse the same connection.
pub fn create_process_pre(_args: &ProcessArgs) -> core::result::Result<ProcessInit, crate::Error> {
ensure_connection()?;
// Ensure there is a connection, because after this function returns
// we'll make a syscall with CreateProcess(). This should only need
// to happen for PID1.
Ok(ProcessInit {
key: PROCESS_KEY
.with(|pk| *pk.borrow())
.unwrap_or_else(default_process_key),
})
}
pub fn create_process_post(
args: ProcessArgs,
init: ProcessInit,
pid: PID,
) -> core::result::Result<ProcessHandle, crate::Error> {
use std::process::Command;
let server_env = format!("{}", xous_address());
let pid_env = format!("{}", pid);
let process_name_env = args.name.to_string();
let process_key_env = hex::encode(&init.key.0);
let (shell, args) = if cfg!(windows) {
("cmd", ["/C", &args.command])
} else if cfg!(unix) {
("sh", ["-c", &args.command])
} else {
panic!("unrecognized platform -- don't know how to shell out");
};
// println!("Launching process...");
Command::new(shell)
.args(&args)
.env("XOUS_SERVER", server_env)
.env("XOUS_PID", pid_env)
.env("XOUS_PROCESS_NAME", process_name_env)
.env("XOUS_PROCESS_KEY", process_key_env)
.spawn()
.map(ProcessHandle)
.map_err(|_| {
// eprintln!("couldn't start command: {}", e);
crate::Error::InternalError
})
}
pub fn wait_process(mut joiner: ProcessHandle) -> crate::SysCallResult {
joiner
.0
.wait()
.or(Err(crate::Error::InternalError))
.and_then(|e| {
if e.success() {
Ok(crate::Result::Ok)
} else {
Err(crate::Error::UnknownError)
}
})
}
pub struct WaitHandle<T>(std::thread::JoinHandle<T>);
#[derive(Clone)]
struct ServerConnection {
send: Arc<Mutex<TcpStream>>,
recv: Arc<Mutex<TcpStream>>,
mailbox: Arc<Mutex<HashMap<TID, Result>>>,
}
pub fn thread_to_args(call: usize, _init: &ThreadInit) -> [usize; 8] {
[call, 0, 0, 0, 0, 0, 0, 0]
}
pub fn process_to_args(call: usize, init: &ProcessInit) -> [usize; 8] {
[
call,
u32::from_le_bytes(init.key.0[0..4].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[4..8].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[8..12].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[12..16].try_into().unwrap()) as _,
0,
0,
0,
]
}
pub fn args_to_thread(
_a1: usize,
_a2: usize,
_a3: usize,
_a4: usize,
_a5: usize,
_a6: usize,
_a7: usize,
) -> core::result::Result<ThreadInit, crate::Error> {
Ok(ThreadInit {})
}
pub fn args_to_process(
a1: usize,
a2: usize,
a3: usize,
a4: usize,
_a5: usize,
_a6: usize,
_a7: usize,
) -> core::result::Result<ProcessInit, crate::Error> {
let mut v = vec![];
v.extend_from_slice(&(a1 as u32).to_le_bytes());
v.extend_from_slice(&(a2 as u32).to_le_bytes());
v.extend_from_slice(&(a3 as u32).to_le_bytes());
v.extend_from_slice(&(a4 as u32).to_le_bytes());
let mut key = [0u8; 16];
key.copy_from_slice(&v);
Ok(ProcessInit {
key: ProcessKey(key),
})
}
thread_local!(static NETWORK_CONNECT_ADDRESS: RefCell<Option<SocketAddr>> = RefCell::new(None));
thread_local!(static XOUS_SERVER_CONNECTION: RefCell<Option<ServerConnection>> = RefCell::new(None));
thread_local!(static THREAD_ID: RefCell<TID> = RefCell::new(1));
thread_local!(static PROCESS_ID: RefCell<PID> = RefCell::new(PID::new(1).unwrap()));
thread_local!(static PROCESS_KEY: RefCell<Option<ProcessKey>> = RefCell::new(None));
thread_local!(static CALL_FOR_THREAD: RefCell<Arc<Mutex<HashMap<TID, crate::SysCall>>>> = RefCell::new(Arc::new(Mutex::new(HashMap::new()))));
fn default_xous_address() -> SocketAddr {
std::env::var("XOUS_SERVER")
.map(|s| {
s.to_socket_addrs()
.expect("invalid server address")
.next()
.expect("unable to resolve server address")
})
.unwrap_or_else(|_| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0))
}
fn default_process_key() -> ProcessKey {
std::env::var("XOUS_PROCESS_KEY")
.map(|s| {
let mut base = ProcessKey([0u8; 16]);
hex::decode_to_slice(s, &mut base.0).unwrap();
base
})
.unwrap_or(ProcessKey([0u8; 16]))
}
pub fn set_process_key(new_key: &[u8; 16]) {
PROCESS_KEY.with(|pk| *pk.borrow_mut() = Some(ProcessKey(*new_key)));
}
/// Set the network address for this particular thread.
pub fn set_xous_address(new_address: SocketAddr) {
NETWORK_CONNECT_ADDRESS.with(|nca| {
let mut address = nca.borrow_mut();
*address = Some(new_address);
XOUS_SERVER_CONNECTION.with(|xsc| *xsc.borrow_mut() = None);
});
}
/// Get the network address for this particular thread.
fn xous_address() -> SocketAddr {
NETWORK_CONNECT_ADDRESS
.with(|nca| *nca.borrow())
.unwrap_or_else(default_xous_address)
}
pub fn create_thread_simple_pre<T, U>(
_f: &fn(T) -> U,
_arg: &T,
) -> core::result::Result<ThreadInit, crate::Error>
where
T: Send + 'static,
U: Send + 'static,
{
Ok(ThreadInit {})
}
pub fn create_thread_simple_post<T, U>(
f: fn(T) -> U,
arg: T,
thread_id: TID,
) -> core::result::Result<WaitHandle<U>, crate::Error>
where
T: Send + 'static,
U: Send + 'static,
{
create_thread_post(move || f(arg), thread_id)
}
pub fn create_thread_pre<F, T>(_f: &F) -> core::result::Result<ThreadInit, crate::Error>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
Ok(ThreadInit {})
}
pub fn create_thread_post<F, U>(
f: F,
thread_id: TID,
) -> core::result::Result<WaitHandle<U>, crate::Error>
where
F: FnOnce() -> U,
F: Send + 'static,
U: Send + 'static,
{
let server_address = xous_address();
let server_connection =
XOUS_SERVER_CONNECTION.with(|xsc| xsc.borrow().as_ref().unwrap().clone());
let process_id = PROCESS_ID.with(|pid| *pid.borrow());
let call_for_thread = CALL_FOR_THREAD.with(|cft| cft.borrow().clone());
Ok(std::thread::Builder::new()
.spawn(move || {
set_xous_address(server_address);
THREAD_ID.with(|tid| *tid.borrow_mut() = thread_id);
PROCESS_ID.with(|pid| *pid.borrow_mut() = process_id);
XOUS_SERVER_CONNECTION.with(|xsc| *xsc.borrow_mut() = Some(server_connection));
CALL_FOR_THREAD.with(|cft| *cft.borrow_mut() = call_for_thread);
f()
})
.map(WaitHandle)
.map_err(|_| crate::Error::InternalError)?)
}
pub fn wait_thread<T>(joiner: WaitHandle<T>) -> crate::SysCallResult {
joiner
.0
.join()
.map(|_| Result::Ok)
.map_err(|_| crate::Error::InternalError)
}
pub fn ensure_connection() -> core::result::Result<(), crate::Error> {
XOUS_SERVER_CONNECTION.with(|xsc| {
let mut xsc = xsc.borrow_mut();
if xsc.is_none() {
NETWORK_CONNECT_ADDRESS.with(|nca| {
let addr = nca.borrow().unwrap_or_else(default_xous_address);
let pid1_key = PROCESS_KEY
.with(|pk| *pk.borrow())
.unwrap_or_else(default_process_key);
match xous_connect_impl(addr, &pid1_key) {
Ok(a) => {
*xsc = Some(a);
Ok(())
}
Err(_) => Err(crate::Error::InternalError),
}
})
} else {
Ok(())
}
})
}
fn xous_connect_impl(
addr: SocketAddr,
key: &ProcessKey,
) -> core::result::Result<ServerConnection, ()> {
// eprintln!("Opening connection to Xous server @ {} with key {:?}...", addr, key);
assert_ne!(&key.0, &[0u8; 16]);
match TcpStream::connect(addr) {
Ok(mut conn) => {
conn.write_all(&key.0).unwrap(); // Send key to authenticate us as PID 1
conn.flush().unwrap();
Ok(ServerConnection {
send: Arc::new(Mutex::new(conn.try_clone().unwrap())),
recv: Arc::new(Mutex::new(conn)),
mailbox: Arc::new(Mutex::new(HashMap::new())),
})
}
Err(_e) => {
// eprintln!("Unable to connect to Xous server: {}", _e);
// eprintln!(
// "Ensure Xous is running, or specify this process as an argument to the kernel"
// );
Err(())
}
}
}
#[allow(clippy::too_many_arguments)]
#[no_mangle]
pub fn _xous_syscall(
nr: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
ret: &mut Result,
) {
XOUS_SERVER_CONNECTION.with(|xsc| {
THREAD_ID.with(|tid| {
let call = crate::SysCall::from_args(nr, a1, a2, a3, a4, a5, a6, a7).unwrap();
{
CALL_FOR_THREAD.with(|cft| {
let cft_rc = cft.borrow();
let mut cft_mtx = cft_rc.lock().unwrap();
let tid = *tid.borrow();
assert!(cft_mtx.get(&tid).is_none());
cft_mtx.insert(tid, call)
});
}
let call = crate::SysCall::from_args(nr, a1, a2, a3, a4, a5, a6, a7).unwrap();
let mut xsc_borrowed = xsc.borrow_mut();
let xsc_asmut = xsc_borrowed.as_mut().expect("not connected to server (did you forget to create a thread with xous::create_thread()?)");
loop {
_xous_syscall_to(
nr,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
&call,
&mut xsc_asmut.send.lock().unwrap(),
);
_xous_syscall_result(ret, *tid.borrow(), xsc_asmut);
if *ret != Result::WouldBlock {
return;
}
std::thread::sleep(std::time::Duration::from_millis(50));
}
})
});
}
fn _xous_syscall_result(ret: &mut Result, thread_id: TID, server_connection: &ServerConnection) {
// Check to see if this thread id has an entry in the mailbox already.
// This will block until the hashmap is free.
{
let mut mailbox = server_connection.mailbox.lock().unwrap();
if let Some(entry) = mailbox.get(&thread_id) {
if &Result::BlockedProcess != entry {
*ret = mailbox.remove(&thread_id).unwrap();
return;
}
}
}
// Receive the packet back
loop {
// Now that we have the Stream mutex, temporarily take the Mailbox mutex to see if
// this thread ID is there. If it is, there's no need to read via the network.
// Note that the mailbox mutex is released if it isn't found.
{
let mut mailbox = server_connection.mailbox.lock().unwrap();
if let Some(entry) = mailbox.get(&thread_id) {
if &Result::BlockedProcess != entry {
*ret = mailbox.remove(&thread_id).unwrap();
return;
}
}
}
let mut stream = match server_connection.recv.try_lock() {
Ok(lk) => lk,
Err(std::sync::TryLockError::WouldBlock) => {
std::thread::sleep(std::time::Duration::from_millis(10));
continue;
}
Err(e) => panic!("Receive error: {}", e),
};
// One more check, in case something came in while we waited for the receiver above.
{
let mut mailbox = server_connection.mailbox.lock().unwrap();
if let Some(entry) = mailbox.get(&thread_id) {
if &Result::BlockedProcess != entry {
*ret = mailbox.remove(&thread_id).unwrap();
return;
}
}
}
// 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];
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>());
// Read the Thread ID, which comes across first, followed by the 8 words of
// the message data.
let msg_thread_id =
usize::from_le_bytes(raw_bytes_chunks.next().unwrap().try_into().unwrap());
for (pkt_word, word) in pkt.iter_mut().zip(raw_bytes_chunks) {
*pkt_word = usize::from_le_bytes(word.try_into().unwrap());
}
let mut response = Result::from_args(pkt);
// If we got a `WouldBlock`, then we need to retry the whole call
// again. Return and retry.
if response == Result::WouldBlock {
// If the incoming message was for this thread, return it directly.
if msg_thread_id == thread_id {
*ret = response;
return;
}
// Otherwise, add it to the mailbox and try again.
let mut mailbox = server_connection.mailbox.lock().unwrap();
mailbox.insert(msg_thread_id, response);
continue;
}
if response == Result::BlockedProcess {
// println!(" Waiting again");
continue;
}
// Determine if this thread will have a memory packet following it.
let call = CALL_FOR_THREAD.with(|cft| {
let cft_borrowed = cft.borrow();
let mut cft_mtx = cft_borrowed.lock().unwrap();
cft_mtx
.remove(&msg_thread_id)
.expect("thread didn't declare whether it has data")
});
// 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);
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();
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.as_mut_ptr(), mem.len()) };
// If it's a Borrow, verify the contents haven't changed.
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 we're returning memory to the Server, then reconstitute the buffer we just passed,
// and Drop it so it can be freed.
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
// this thread ID is there. If it is, there's no need to read via the network.
// Note that the mailbox mutex is released if it isn't found.
{
// If the incoming message was for this thread, return it directly.
if msg_thread_id == thread_id {
*ret = response;
return;
}
// Otherwise, add it to the mailbox and try again.
let mut mailbox = server_connection.mailbox.lock().unwrap();
mailbox.insert(msg_thread_id, response);
}
}
}
#[allow(clippy::too_many_arguments)]
#[no_mangle]
fn _xous_syscall_to(
nr: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
call: &crate::SysCall,
xsc: &mut TcpStream,
) {
// println!(
// "Making Syscall: {:?}",
// crate::SysCall::from_args(nr, a1, a2, a3, a4, a5, a6, a7).unwrap()
// );
// Send the packet to the server
let mut pkt = vec![];
THREAD_ID.with(|tid| pkt.extend_from_slice(&tid.borrow().to_le_bytes()));
for word in &[nr, a1, a2, a3, a4, a5, a6, a7] {
pkt.extend_from_slice(&word.to_le_bytes());
}
// 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.as_ptr(), memory.len()) };
pkt.extend_from_slice(data);
}
if let Err(e) = xsc.write_all(&pkt) {
eprintln!("Server shut down: {}", e);
std::process::exit(0);
}
xsc.flush().unwrap();
}

View File

@ -0,0 +1,41 @@
use crate::{Error, MemoryAddress, MemoryFlags, MemoryRange};
extern crate alloc;
use alloc::alloc::{alloc, dealloc, Layout};
pub fn map_memory_pre(
_phys: &Option<MemoryAddress>,
_virt: &Option<MemoryAddress>,
_size: usize,
_flags: MemoryFlags,
) -> core::result::Result<(), Error> {
Ok(())
}
pub fn map_memory_post(
_phys: Option<MemoryAddress>,
_virt: Option<MemoryAddress>,
_size: usize,
_flags: MemoryFlags,
mut range: MemoryRange,
) -> core::result::Result<MemoryRange, Error> {
let layout = Layout::from_size_align(range.len(), 4096).unwrap();
let new_mem = MemoryAddress::new(unsafe { alloc(layout) } as usize).ok_or(Error::BadAddress)?;
range.addr = new_mem;
Ok(range)
}
pub fn unmap_memory_pre(
_range: &MemoryRange
) -> core::result::Result<(), Error> {
Ok(())
}
pub fn unmap_memory_post(
range: MemoryRange
) -> core::result::Result<(), Error> {
let layout = Layout::from_size_align(range.len(), 4096).unwrap();
let ptr = range.as_mut_ptr();
unsafe { dealloc(ptr, layout) };
Ok(())
}

9
xous-rs/src/arch/mod.rs Normal file
View File

@ -0,0 +1,9 @@
#[cfg(target_os = "none")]
pub mod native;
#[cfg(target_os = "none")]
pub use native::*;
#[cfg(not(target_os = "none"))]
pub mod hosted;
#[cfg(not(target_os = "none"))]
pub use hosted::*;

218
xous-rs/src/arch/native.rs Normal file
View File

@ -0,0 +1,218 @@
use crate::{MemoryAddress, MemoryRange, PID, TID};
use core::convert::TryInto;
mod mem;
pub use mem::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ProcessArgs {
name: [u8; 16],
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ThreadInit {
pub call: fn(data: usize) -> usize,
pub stack: MemoryRange,
pub arg: Option<MemoryAddress>,
pub name: [u8; 12],
}
impl ThreadInit {
pub fn new(
call: fn(data: usize) -> usize,
stack: MemoryRange,
arg: Option<MemoryAddress>,
name: [u8; 12],
) -> Self {
ThreadInit {
call,
stack,
arg,
name,
}
}
}
impl Default for ThreadInit {
fn default() -> Self {
ThreadInit {
call: unsafe { core::mem::transmute::<usize, fn(data: usize) -> usize>(4) },
stack: MemoryRange::new(4, 4).unwrap(),
arg: None,
name: [0; 12],
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ProcessKey([u8; 8]);
impl ProcessKey {
pub fn new(key: [u8; 8]) -> ProcessKey {
ProcessKey(key)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ProcessInit {
pub key: ProcessKey,
}
pub struct WaitHandle<T>(core::marker::PhantomData<T>);
pub struct ProcessHandle(());
pub fn thread_to_args(call: usize, init: &ThreadInit) -> [usize; 8] {
[
call as usize,
init.call as usize,
init.stack.as_ptr() as _,
init.stack.len(),
init.arg.map(|x| x.get()).unwrap_or_default(),
0,
0,
0,
]
}
/// This code is executed inside the kernel. It takes the list of args
/// that were passed via registers and converts them into a `ThreadInit`
/// struct with enough information to start the new thread.
pub fn args_to_thread(
a1: usize,
a2: usize,
a3: usize,
a4: usize,
_a5: usize,
_a6: usize,
_a7: usize,
) -> core::result::Result<ThreadInit, crate::Error> {
let call = unsafe { core::mem::transmute(a1) };
let stack = MemoryRange::new(a2, a3).map_err(|_| crate::Error::InvalidSyscall)?;
let arg = MemoryAddress::new(a4);
Ok(ThreadInit {
call,
stack,
arg,
name: [0; 12],
})
}
pub fn create_thread_pre<F, T>(_f: &F) -> core::result::Result<ThreadInit, crate::Error>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
todo!()
// Ok(ThreadInit {})
}
pub fn create_thread_post<F, T>(
_f: F,
_thread_id: TID,
) -> core::result::Result<WaitHandle<T>, crate::Error>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
todo!()
// let server_address = xous_address();
// let server_connection =
// XOUS_SERVER_CONNECTION.with(|xsc| xsc.borrow().as_ref().unwrap().clone());
// let process_id = PROCESS_ID.with(|pid| *pid.borrow());
// Ok(std::thread::Builder::new()
// .spawn(move || {
// set_xous_address(server_address);
// THREAD_ID.with(|tid| *tid.borrow_mut() = thread_id);
// PROCESS_ID.with(|pid| *pid.borrow_mut() = process_id);
// XOUS_SERVER_CONNECTION.with(|xsc| *xsc.borrow_mut() = Some(server_connection));
// f()
// })
// .map(WaitHandle)
// .map_err(|_| crate::Error::InternalError)?)
}
pub fn wait_thread<T>(_joiner: WaitHandle<T>) -> crate::SysCallResult {
todo!()
// joiner
// .0
// .join()
// .map(|_| Result::Ok)
// .map_err(|_| crate::Error::InternalError)
}
pub fn process_to_args(call: usize, init: &ProcessInit) -> [usize; 8] {
[
call,
u32::from_le_bytes(init.key.0[0..4].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[4..8].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[8..12].try_into().unwrap()) as _,
u32::from_le_bytes(init.key.0[12..16].try_into().unwrap()) as _,
0,
0,
0,
]
}
pub fn args_to_process(
_a1: usize,
_a2: usize,
_a3: usize,
_a4: usize,
_a5: usize,
_a6: usize,
_a7: usize,
) -> core::result::Result<ProcessInit, crate::Error> {
todo!()
}
pub fn create_thread_simple_pre<T, U>(
f: &fn(T) -> U,
arg: &T,
) -> core::result::Result<ThreadInit, crate::Error>
where
T: Send + 'static,
U: Send + 'static,
{
let stack = crate::map_memory(
None,
None,
131_072,
crate::MemoryFlags::R | crate::MemoryFlags::W | crate::MemoryFlags::RESERVE,
)?;
let start = unsafe { core::mem::transmute(*f) };
let arg = unsafe { core::mem::transmute(arg) };
Ok(ThreadInit::new(start, stack, Some(arg), [0; 12]))
}
pub fn create_thread_simple_post<T, U>(
_f: fn(T) -> U,
_arg: T,
_thread_id: TID,
) -> core::result::Result<WaitHandle<U>, crate::Error>
where
T: Send + 'static,
U: Send + 'static,
{
Ok(WaitHandle(core::marker::PhantomData))
}
/// If no connection exists, create a new connection to the server. This means
/// our parent PID will be PID1. Otherwise, reuse the same connection.
pub fn create_process_pre(_args: &ProcessArgs) -> core::result::Result<ProcessInit, crate::Error> {
todo!()
}
pub fn create_process_post(
_args: ProcessArgs,
_init: ProcessInit,
_pid: PID,
) -> core::result::Result<ProcessHandle, crate::Error> {
todo!()
}
pub fn wait_process(_joiner: ProcessHandle) -> crate::SysCallResult {
loop {
crate::wait_event();
}
}

View File

@ -0,0 +1,32 @@
use crate::{Error, MemoryAddress, MemoryFlags, MemoryRange};
pub fn map_memory_pre(
_phys: &Option<MemoryAddress>,
_virt: &Option<MemoryAddress>,
_size: usize,
_flags: MemoryFlags,
) -> core::result::Result<(), Error> {
Ok(())
}
pub fn map_memory_post(
_phys: Option<MemoryAddress>,
_virt: Option<MemoryAddress>,
_size: usize,
_flags: MemoryFlags,
range: MemoryRange,
) -> core::result::Result<MemoryRange, Error> {
Ok(range)
}
pub fn unmap_memory_pre(
_range: &MemoryRange
) -> core::result::Result<(), Error> {
Ok(())
}
pub fn unmap_memory_post(
_range: MemoryRange
) -> core::result::Result<(), Error> {
Ok(())
}

20
xous-rs/src/asm.S Normal file
View File

@ -0,0 +1,20 @@
#define STORE sw
#define LOAD lw
#define LOG_REGBYTES 2
#define REGBYTES (1 << LOG_REGBYTES)
.global _xous_syscall
.global _xous_syscall_rust
_xous_syscall_rust:
_xous_syscall:
ecall
lw t0, 0(sp)
sw a0, 0*REGBYTES(t0)
sw a1, 1*REGBYTES(t0)
sw a2, 2*REGBYTES(t0)
sw a3, 3*REGBYTES(t0)
sw a4, 4*REGBYTES(t0)
sw a5, 5*REGBYTES(t0)
sw a6, 6*REGBYTES(t0)
sw a7, 7*REGBYTES(t0)
ret

98
xous-rs/src/carton.rs Normal file
View File

@ -0,0 +1,98 @@
//! A Carton is an object that wraps another object for shipping across the kernel
//! boundary. Structs that are stored in Cartons can be sent as messages.
use crate::{Error, MemoryMessage, MemoryRange, MemorySize, Message, CID};
#[derive(Debug)]
pub struct Carton<'a> {
range: MemoryRange,
valid: MemoryRange,
slice: &'a [u8],
should_drop: bool,
}
impl<'a> Carton<'a> {
pub fn from_bytes(bytes: &[u8]) -> Self {
let src_mem = bytes.as_ptr();
// Ensure our byte size is a multiple of 4096
let remainder = bytes.len() & 4095;
let size = bytes.len() + (4096 - remainder);
let new_mem = crate::map_memory(
None,
None,
size,
crate::MemoryFlags::R | crate::MemoryFlags::W,
)
.unwrap();
// NOTE: Remaining bytes are not zeroed. We assume the kernel has done this for us.
unsafe {
core::ptr::copy(src_mem, new_mem.as_mut_ptr(), bytes.len());
};
let mut valid = new_mem;
valid.size = MemorySize::new(bytes.len()).unwrap();
Carton {
range: new_mem,
slice: unsafe { core::slice::from_raw_parts_mut(new_mem.as_mut_ptr(), bytes.len()) },
valid,
should_drop: true,
}
}
pub fn into_message(mut self, id: usize) -> MemoryMessage {
// Leak the memory buffer, since it will be taken care of
// when the MemoryMessage is dropped.
self.should_drop = false;
MemoryMessage {
id,
buf: self.valid,
offset: None,
valid: None,
}
}
/// Perform an immutable lend of this Carton to the specified server.
/// This function will block until the server returns.
pub fn lend(&self, connection: CID, id: usize) -> Result<crate::Result, Error> {
let msg = MemoryMessage {
id,
buf: self.valid,
offset: None,
valid: None,
};
crate::send_message(connection, Message::Borrow(msg))
}
/// Perform a mutable lend of this Carton to the server.
pub fn lend_mut(&mut self, connection: CID, id: usize) -> Result<crate::Result, Error> {
let msg = MemoryMessage {
id,
buf: self.valid,
offset: None,
valid: None,
};
crate::send_message(connection, Message::MutableBorrow(msg))
}
}
impl<'a> AsRef<MemoryRange> for Carton<'a> {
fn as_ref(&self) -> &MemoryRange {
&self.valid
}
}
impl<'a> AsRef<[u8]> for Carton<'a> {
fn as_ref(&self) -> &[u8] {
&self.slice
}
}
impl<'a> Drop for Carton<'a> {
fn drop(&mut self) {
if self.should_drop {
crate::unmap_memory(self.range).unwrap();
}
}
}

649
xous-rs/src/definitions.rs Normal file
View File

@ -0,0 +1,649 @@
use core::convert::TryInto;
use core::num::{NonZeroU8, NonZeroUsize};
pub type MemoryAddress = NonZeroUsize;
pub type MemorySize = NonZeroUsize;
pub type StackPointer = usize;
pub type MessageId = usize;
pub type PID = NonZeroU8;
pub type Connection = usize;
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone)]
pub struct MessageSender {
data: usize,
}
impl MessageSender {
pub fn to_usize(&self) -> usize {
self.data
}
pub fn from_usize(data: usize) -> Self {
MessageSender { data }
}
pub fn pid(&self) -> Option<PID> {
let pid_u8 = ((self.data >> 24) & 0xff) as u8;
PID::new(pid_u8)
}
}
impl core::default::Default for MessageSender {
fn default() -> Self {
MessageSender { data: 0 }
}
}
impl core::fmt::Display for MessageSender {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "data: {}", self.data)
}
}
/// Server ID
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct SID((u32, u32, u32, u32));
impl SID {
pub fn from_bytes(b: &[u8]) -> Option<SID> {
if b.len() > 16 {
None
} else {
let mut sid = (0, 0, 0, 0);
let mut byte_iter = b.chunks_exact(4);
if let Some(val) = byte_iter.next() {
sid.0 = u32::from_le_bytes(val.try_into().ok()?);
}
if let Some(val) = byte_iter.next() {
sid.1 = u32::from_le_bytes(val.try_into().ok()?);
}
if let Some(val) = byte_iter.next() {
sid.2 = u32::from_le_bytes(val.try_into().ok()?);
}
if let Some(val) = byte_iter.next() {
sid.3 = u32::from_le_bytes(val.try_into().ok()?);
}
Some(SID(sid))
}
}
pub fn from_u32(a0: u32, a1: u32, a2: u32, a3: u32) -> SID {
SID((a0, a1, a2, a3))
}
pub fn to_u32(&self) -> (u32, u32, u32, u32) {
((self.0).0, (self.0).1, (self.0).2, (self.0).3)
}
}
impl core::str::FromStr for SID {
type Err = ();
fn from_str(s: &str) -> core::result::Result<SID, ()> {
Self::from_bytes(s.as_bytes()).ok_or(())
}
}
/// Connection ID
pub type CID = usize;
/// Context ID
pub type TID = usize;
/// Equivalent to a RISC-V Hart ID
pub type CpuID = usize;
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct MemoryRange {
pub addr: MemoryAddress,
pub size: MemorySize,
}
bitflags! {
/// Flags to be passed to the MapMemory struct.
/// Note that it is an error to have memory be
/// writable and not readable.
pub struct MemoryFlags: usize {
/// Free this memory
const FREE = 0b0000_0000;
/// Immediately allocate this memory. Otherwise it will
/// be demand-paged. This is implicitly set when `phys`
/// is not 0.
const RESERVE = 0b0000_0001;
/// Allow the CPU to read from this page.
const R = 0b0000_0010;
/// Allow the CPU to write to this page.
const W = 0b0000_0100;
/// Allow the CPU to execute from this page.
const X = 0b0000_1000;
}
}
pub fn pid_from_usize(src: usize) -> core::result::Result<PID, Error> {
if src > u8::MAX as _ {
return Err(Error::InvalidPID);
}
Ok(PID::new(src as u8).ok_or(Error::InvalidPID)?)
}
#[repr(usize)]
#[derive(Debug, PartialEq)]
pub enum Error {
NoError = 0,
BadAlignment = 1,
BadAddress = 2,
OutOfMemory = 3,
MemoryInUse = 4,
InterruptNotFound = 5,
InterruptInUse = 6,
InvalidString = 7,
ServerExists = 8,
ServerNotFound = 9,
ProcessNotFound = 10,
ProcessNotChild = 11,
ProcessTerminated = 12,
Timeout = 13,
InternalError = 14,
ServerQueueFull = 15,
ThreadNotAvailable = 16,
UnhandledSyscall = 17,
InvalidSyscall = 18,
ShareViolation = 19,
InvalidThread = 20,
InvalidPID = 21,
UnknownError = 22,
}
impl Error {
pub fn from_usize(arg: usize) -> Self {
use crate::Error::*;
match arg {
0 => NoError,
1 => BadAlignment,
2 => BadAddress,
3 => OutOfMemory,
4 => MemoryInUse,
5 => InterruptNotFound,
6 => InterruptInUse,
7 => InvalidString,
8 => ServerExists,
9 => ServerNotFound,
10 => ProcessNotFound,
11 => ProcessNotChild,
12 => ProcessTerminated,
13 => Timeout,
14 => InternalError,
15 => ServerQueueFull,
16 => ThreadNotAvailable,
17 => UnhandledSyscall,
18 => InvalidSyscall,
19 => ShareViolation,
20 => InvalidThread,
21 => InvalidPID,
_ => UnknownError,
}
}
pub fn to_usize(&self) -> usize {
use crate::Error::*;
match *self {
NoError => 0,
BadAlignment => 1,
BadAddress => 2,
OutOfMemory => 3,
MemoryInUse => 4,
InterruptNotFound => 5,
InterruptInUse => 6,
InvalidString => 7,
ServerExists => 8,
ServerNotFound => 9,
ProcessNotFound => 10,
ProcessNotChild => 11,
ProcessTerminated => 12,
Timeout => 13,
InternalError => 14,
ServerQueueFull => 15,
ThreadNotAvailable => 16,
UnhandledSyscall => 17,
InvalidSyscall => 18,
ShareViolation => 19,
InvalidThread => 20,
InvalidPID => 21,
UnknownError => usize::MAX,
}
}
}
#[repr(C)]
pub struct Context {
stack: StackPointer,
pid: PID,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
/// A struct describing memory that is passed between processes.
/// The `buf` value will get translated as necessary.
pub struct MemoryMessage {
/// A user-assignable message ID.
pub id: MessageId,
/// The offset of the buffer. This address will get transformed when the
/// message is moved between processes.
pub buf: MemoryRange,
/// The offset within the buffer where the interesting stuff starts.
pub offset: Option<MemoryAddress>,
/// How many bytes in the buffer are valid
pub valid: Option<MemorySize>,
}
impl MemoryMessage {
pub fn from_usize(
id: usize,
addr: usize,
size: usize,
offset: usize,
valid: usize,
) -> Option<MemoryMessage> {
let addr = match MemoryAddress::new(addr) {
None => return None,
Some(s) => s,
};
let size = match MemorySize::new(size) {
None => return None,
Some(s) => s,
};
let buf = MemoryRange { addr, size };
let offset = MemoryAddress::new(offset);
let valid = MemorySize::new(valid);
Some(MemoryMessage {
id,
buf,
offset,
valid,
})
}
pub fn to_usize(&self) -> [usize; 5] {
[
self.id,
self.buf.addr.get(),
self.buf.size.get(),
self.offset.map(|e| e.get()).unwrap_or(0),
self.valid.map(|e| e.get()).unwrap_or(0),
]
}
}
#[repr(C)]
#[derive(Debug, PartialEq, Clone, Copy)]
/// A simple scalar message. This is similar to a `move` message.
pub struct ScalarMessage {
pub id: MessageId,
pub arg1: usize,
pub arg2: usize,
pub arg3: usize,
pub arg4: usize,
}
impl ScalarMessage {
pub fn from_usize(
id: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
) -> ScalarMessage {
ScalarMessage {
id,
arg1,
arg2,
arg3,
arg4,
}
}
pub fn to_usize(&self) -> [usize; 5] {
[self.id, self.arg1, self.arg2, self.arg3, self.arg4]
}
}
#[repr(usize)]
#[derive(Debug, PartialEq)]
pub enum Message {
MutableBorrow(MemoryMessage),
Borrow(MemoryMessage),
Move(MemoryMessage),
Scalar(ScalarMessage),
BlockingScalar(ScalarMessage),
}
impl Message {
/// Determine whether the specified Message will block
pub fn is_blocking(&self) -> bool {
match *self {
Message::MutableBorrow(_) | Message::Borrow(_) | Message::BlockingScalar(_) => true,
Message::Move(_) | Message::Scalar(_) => false,
}
}
/// Determine whether the specified message has data attached
pub fn has_memory(&self) -> bool {
match *self {
Message::MutableBorrow(_) | Message::Borrow(_) | Message::Move(_) => true,
Message::BlockingScalar(_) | Message::Scalar(_) => false,
}
}
pub fn memory(&self) -> Option<&MemoryRange> {
match self {
Message::MutableBorrow(mem) | Message::Borrow(mem) | Message::Move(mem) => {
Some(&mem.buf)
}
Message::BlockingScalar(_) | Message::Scalar(_) => None,
}
}
pub fn message_type(&self) -> usize {
match *self {
Message::MutableBorrow(_) => 1,
Message::Borrow(_) => 2,
Message::Move(_) => 3,
Message::Scalar(_) => 4,
Message::BlockingScalar(_) => 5,
}
}
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub struct MessageEnvelope {
pub sender: MessageSender,
pub body: Message,
}
impl MessageEnvelope {
pub fn to_usize(&self) -> [usize; 7] {
let ret = match &self.body {
Message::MutableBorrow(m) => (0, m.to_usize()),
Message::Borrow(m) => (1, m.to_usize()),
Message::Move(m) => (2, m.to_usize()),
Message::Scalar(m) => (3, m.to_usize()),
Message::BlockingScalar(m) => (4, m.to_usize()),
};
[
self.sender.to_usize(),
ret.0,
ret.1[0],
ret.1[1],
ret.1[2],
ret.1[3],
ret.1[4],
]
}
}
#[cfg(not(feature = "forget-memory-messages"))]
/// When a MessageEnvelope goes out of scope, return the memory. It must either
/// go to the kernel (in the case of a Move), or back to the borrowed process
/// (in the case of a Borrow). Ignore Scalar messages.
impl Drop for MessageEnvelope {
fn drop(&mut self) {
match &self.body {
Message::Borrow(x) | Message::MutableBorrow(x) => {
crate::syscall::return_memory(self.sender, x.buf).expect("couldn't return memory")
}
Message::Move(msg) => {
crate::syscall::unmap_memory(msg.buf).expect("couldn't free memory message")
}
_ => (),
}
}
}
impl MemoryRange {
pub fn new(addr: usize, size: usize) -> core::result::Result<MemoryRange, Error> {
assert!(
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)?,
})
}
pub fn from_parts(addr: MemoryAddress, size: MemorySize) -> MemoryRange {
MemoryRange { addr, size }
}
pub fn len(&self) -> usize {
self.size.get()
}
pub fn is_empty(&self) -> bool {
self.size.get() > 0
}
pub fn as_ptr(&self) -> *const u8 {
self.addr.get() as *const u8
}
pub fn as_mut_ptr(&self) -> *mut u8 {
self.addr.get() as *mut u8
}
}
/// Which memory region the operation should affect.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MemoryType {
/// The address where addresses go when no `virt` is specified.
Default = 1,
/// Addresses will begin here when `IncreaseHeap` is called.
Heap = 2,
/// When messages are passed to a process, they will go here.
Messages = 3,
/// Unlike other memory types, this defines the "end" of the region.
Stack = 4,
}
impl From<usize> for MemoryType {
fn from(arg: usize) -> Self {
match arg {
2 => MemoryType::Heap,
3 => MemoryType::Messages,
4 => MemoryType::Stack,
_ => MemoryType::Default,
}
}
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum Result {
Ok,
Error(Error),
MemoryAddress(MemoryAddress),
MemoryRange(MemoryRange),
ReadyThreads(
usize, /* count */
usize,
/* pid0 */ usize, /* context0 */
usize,
/* pid1 */ usize, /* context1 */
usize,
/* pid2 */ usize, /* context2 */
),
ResumeProcess,
ServerID(SID),
ConnectionID(CID),
NewServerID(SID, CID),
Message(MessageEnvelope),
ThreadID(TID),
ProcessID(PID),
/// The requested system call is unimplemented
Unimplemented,
/// The process is blocked and should perform the read() again. This is only
/// ever seen in `Hosted` mode, because when running natively the kernel
/// simply never schedules the process.
BlockedProcess,
/// A scalar with one value
Scalar1(usize),
/// A scalar with two values
Scalar2(usize, usize),
/// The syscall should be attempted again. This is returned when calling
/// functions such as `try_connect()` and `try_send()` that may block.
WouldBlock,
/// The message was successful but no value was returned.
None,
UnknownResult(usize, usize, usize, usize, usize, usize, usize),
}
impl Result {
pub fn to_args(&self) -> [usize; 8] {
match self {
Result::Ok => [0, 0, 0, 0, 0, 0, 0, 0],
Result::Error(e) => [1, e.to_usize(), 0, 0, 0, 0, 0, 0],
Result::MemoryAddress(s) => [2, s.get(), 0, 0, 0, 0, 0, 0],
Result::MemoryRange(r) => [3, r.addr.get(), r.size.get(), 0, 0, 0, 0, 0],
Result::ReadyThreads(count, pid0, ctx0, pid1, ctx1, pid2, ctx2) => {
[4, *count, *pid0, *ctx0, *pid1, *ctx1, *pid2, *ctx2]
}
Result::ResumeProcess => [5, 0, 0, 0, 0, 0, 0, 0],
Result::ServerID(sid) => {
let s = sid.to_u32();
[6, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0]
}
Result::ConnectionID(cid) => [7, *cid, 0, 0, 0, 0, 0, 0],
Result::Message(me) => {
let me_enc = me.to_usize();
[
8, me_enc[0], me_enc[1], me_enc[2], me_enc[3], me_enc[4], me_enc[5], me_enc[6],
]
}
Result::ThreadID(ctx) => [9, *ctx as usize, 0, 0, 0, 0, 0, 0],
Result::ProcessID(pid) => [10, pid.get() as _, 0, 0, 0, 0, 0, 0],
Result::Unimplemented => [11, 0, 0, 0, 0, 0, 0, 0],
Result::BlockedProcess => [12, 0, 0, 0, 0, 0, 0, 0],
Result::Scalar1(a) => [13, *a, 0, 0, 0, 0, 0, 0],
Result::Scalar2(a, b) => [14, *a, *b, 0, 0, 0, 0, 0],
Result::NewServerID(sid, cid) => {
let s = sid.to_u32();
[15, s.0 as _, s.1 as _, s.2 as _, s.3 as _, *cid, 0, 0]
}
Result::WouldBlock => [16, 0, 0, 0, 0, 0, 0, 0],
Result::None => [17, 0, 0, 0, 0, 0, 0, 0],
Result::UnknownResult(arg1, arg2, arg3, arg4, arg5, arg6, arg7) => {
[usize::MAX, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7]
}
}
}
pub fn from_args(src: [usize; 8]) -> Self {
match src[0] {
0 => Result::Ok,
1 => Result::Error(Error::from_usize(src[1])),
2 => match MemoryAddress::new(src[1]) {
None => Result::Error(Error::InternalError),
Some(s) => Result::MemoryAddress(s),
},
3 => {
let addr = match MemoryAddress::new(src[1]) {
None => return Result::Error(Error::InternalError),
Some(s) => s,
};
let size = match MemorySize::new(src[2]) {
None => return Result::Error(Error::InternalError),
Some(s) => s,
};
Result::MemoryRange(MemoryRange { addr, size })
}
4 => Result::ReadyThreads(src[1], src[2], src[3], src[4], src[5], src[6], src[7]),
5 => Result::ResumeProcess,
6 => Result::ServerID(SID::from_u32(
src[1] as _,
src[2] as _,
src[3] as _,
src[4] as _,
)),
7 => Result::ConnectionID(src[1] as CID),
8 => {
let sender = src[1];
let message = match src[2] {
0 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) {
None => return Result::Error(Error::InternalError),
Some(s) => Message::MutableBorrow(s),
},
1 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) {
None => return Result::Error(Error::InternalError),
Some(s) => Message::Borrow(s),
},
2 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) {
None => return Result::Error(Error::InternalError),
Some(s) => Message::Move(s),
},
3 => Message::Scalar(ScalarMessage::from_usize(
src[3], src[4], src[5], src[6], src[7],
)),
4 => Message::BlockingScalar(ScalarMessage::from_usize(
src[3], src[4], src[5], src[6], src[7],
)),
_ => return Result::Error(Error::InternalError),
};
Result::Message(MessageEnvelope {
sender: MessageSender::from_usize(sender),
body: message,
})
}
9 => Result::ThreadID(src[1] as TID),
10 => Result::ProcessID(PID::new(src[1] as _).unwrap()),
11 => Result::Unimplemented,
12 => Result::BlockedProcess,
13 => Result::Scalar1(src[1]),
14 => Result::Scalar2(src[1], src[2]),
15 => Result::NewServerID(
SID::from_u32(src[1] as _, src[2] as _, src[3] as _, src[4] as _),
src[5] as _,
),
16 => Result::WouldBlock,
17 => Result::None,
_ => 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 {
fn from(e: Error) -> Self {
Result::Error(e)
}
}
pub type SysCallRequest = core::result::Result<crate::syscall::SysCall, Error>;
pub type SysCallResult = core::result::Result<Result, Error>;

269
xous-rs/src/ipc.rs Normal file
View File

@ -0,0 +1,269 @@
// use core::mem::MaybeUninit;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::{PhantomData, Unpin};
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
#[derive(PartialEq, Debug)]
enum MemoryState {
/// This struct is available for lending and sending
Available,
/// This memory has been mutably lent out and will be returned
MutablyLent,
/// This memory was moved, and should be forgotten
Moved,
}
// #[repr(C)]
pub struct UninitializedSendable<T> {
contents: NonNull<T>,
}
pub struct Sendable<T> {
contents: *mut T,
total_size: usize,
memory_state: MemoryState,
// NOTE: this marker has no consequences for variance, but is necessary
// for dropck to understand that we logically own a `T`.
//
// For details, see:
// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
_marker: PhantomData<T>,
}
impl<T> UninitializedSendable<T> {
fn type_size() -> usize {
let type_size = core::mem::size_of::<T>();
let remainder = type_size & 4095;
type_size + (4096 - remainder)
}
pub fn new(val: T) -> Result<UninitializedSendable<T>, crate::Error>
where
T: Unpin,
{
let uninitialized = Self::uninit()?;
let type_size = core::mem::size_of::<T>();
unsafe {
let src_slice = core::slice::from_raw_parts(&val as *const _ as *const u8, type_size);
let dest_slice = core::slice::from_raw_parts_mut(
uninitialized.contents.as_ptr() as *mut u8,
type_size,
);
for (src, dest) in src_slice.iter().zip(dest_slice) {
*dest = *src;
}
}
Ok(uninitialized)
}
pub fn uninit() -> Result<UninitializedSendable<T>, crate::Error> {
let padded_size = Self::type_size();
// Ensure this object takes up exactly a multiple of a page. This
// ensures it can be sent to another process.
let new_mem = crate::map_memory(
None,
None,
padded_size,
crate::MemoryFlags::R | crate::MemoryFlags::W,
)?;
let contents = unsafe { NonNull::new_unchecked(new_mem.as_mut_ptr() as *mut T) };
Ok(UninitializedSendable { contents })
}
pub unsafe fn assume_init(self) -> Sendable<T> {
Sendable {
contents: self.contents.as_ptr(),
total_size: Self::type_size(),
memory_state: MemoryState::Available,
_marker: PhantomData,
}
}
}
impl<T: Default + Unpin + Send> Default for Sendable<T> {
/// Creates a `Box<T>`, with the `Default` value for T.
fn default() -> Sendable<T> {
Sendable::new(Default::default()).unwrap()
}
}
// impl<T> Default for Sendable<[T]> {
// fn default() -> Sendable<[T]> {
// Sendable::<[T; 0]>::new([])
// }
// }
/// `Unique` pointers are `Send` if `T` is `Send` because the data they
/// reference is unaliased. Note that this aliasing invariant is
/// unenforced by the type system; the abstraction using the
/// `Unique` must enforce it.
unsafe impl<T: Send> Send for Sendable<T> {}
/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they
/// reference is unaliased. Note that this aliasing invariant is
/// unenforced by the type system; the abstraction using the
/// `Unique` must enforce it.
unsafe impl<T: Sync> Sync for Sendable<T> {}
impl<T: Send> Sendable<T> {
pub fn new(val: T) -> Result<Sendable<T>, crate::Error>
where
T: Unpin,
{
Ok(unsafe { UninitializedSendable::new(val)?.assume_init() })
}
/// Perform an immutable lend of this Carton to the specified server.
/// This function will block until the server returns.
pub fn lend(&self, connection: crate::CID, id: usize) -> Result<crate::Result, crate::Error> {
let buf = crate::MemoryRange::new(self.contents as usize, self.total_size)?;
let msg = crate::MemoryMessage {
id,
buf,
offset: None,
valid: crate::MemorySize::new(core::mem::size_of::<T>()),
};
crate::send_message(connection, crate::Message::Borrow(msg))
}
/// Perform a mutable lend of this Carton to the server.
pub fn lend_mut(
&mut self,
connection: crate::CID,
id: usize,
) -> Result<crate::Result, crate::Error> {
let buf = crate::MemoryRange::new(self.contents as usize, self.total_size)?;
let msg = crate::MemoryMessage {
id,
buf,
offset: None,
valid: crate::MemorySize::new(core::mem::size_of::<T>()),
};
self.memory_state = MemoryState::MutablyLent;
let result = crate::send_message(connection, crate::Message::MutableBorrow(msg));
self.memory_state = MemoryState::Available;
result
}
/// Perform a mutable lend of this Carton to the server.
pub fn send(
mut self,
connection: crate::CID,
id: usize,
) -> Result<crate::Result, crate::Error> {
let buf = crate::MemoryRange::new(self.contents as usize, self.total_size)?;
let msg = crate::MemoryMessage {
id,
buf,
offset: None,
valid: crate::MemorySize::new(core::mem::size_of::<T>()),
};
let result = crate::send_message(connection, crate::Message::Move(msg))?;
// Mark this state as Moved, which prevents it from being Dropped.
self.memory_state = MemoryState::Moved;
Ok(result)
}
}
impl<T> Deref for Sendable<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.contents }
}
}
impl<T> DerefMut for Sendable<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.contents }
}
}
// Display formatting
impl<T: fmt::Display> fmt::Display for Sendable<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: fmt::Debug> fmt::Debug for Sendable<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> fmt::Pointer for Sendable<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// It's not possible to extract the inner Uniq directly from the Box,
// instead we cast it to a *const which aliases the Unique
let ptr: *const T = &**self;
fmt::Pointer::fmt(&ptr, f)
}
}
impl<T: PartialEq> PartialEq for Sendable<T> {
#[inline]
fn eq(&self, other: &Sendable<T>) -> bool {
PartialEq::eq(&**self, &**other)
}
#[inline]
fn ne(&self, other: &Sendable<T>) -> bool {
PartialEq::ne(&**self, &**other)
}
}
impl<T: PartialOrd> PartialOrd for Sendable<T> {
#[inline]
fn partial_cmp(&self, other: &Sendable<T>) -> Option<Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
#[inline]
fn lt(&self, other: &Sendable<T>) -> bool {
PartialOrd::lt(&**self, &**other)
}
#[inline]
fn le(&self, other: &Sendable<T>) -> bool {
PartialOrd::le(&**self, &**other)
}
#[inline]
fn ge(&self, other: &Sendable<T>) -> bool {
PartialOrd::ge(&**self, &**other)
}
#[inline]
fn gt(&self, other: &Sendable<T>) -> bool {
PartialOrd::gt(&**self, &**other)
}
}
impl<T: Ord> Ord for Sendable<T> {
#[inline]
fn cmp(&self, other: &Sendable<T>) -> Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: Eq> Eq for Sendable<T> {}
impl<T: Hash> Hash for Sendable<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T> Drop for Sendable<T> {
fn drop(&mut self) {
if self.memory_state != MemoryState::Available {
panic!("invalid memory state: {:?}", self.memory_state);
}
let range = crate::MemoryRange::new(self.contents as usize, self.total_size).unwrap();
crate::unmap_memory(range).unwrap();
}
}

163
xous-rs/src/lib.rs Normal file
View File

@ -0,0 +1,163 @@
#![cfg_attr(target_os = "none", no_std)]
#[macro_use]
extern crate bitflags;
extern crate xous_macros as macros;
pub use macros::xous_main;
pub mod arch;
pub mod carton;
pub mod definitions;
pub mod ipc;
mod messages;
pub mod string;
pub mod syscall;
pub use arch::{ProcessArgs, ProcessInit, ProcessKey, ThreadInit};
pub use definitions::*;
pub use messages::*;
pub use string::*;
pub use syscall::*;
#[cfg(not(target_os = "none"))]
pub use arch::ProcessArgsAsThread;
#[cfg(target_os = "none")]
pub fn init() {}
#[cfg(not(target_os = "none"))]
pub fn init() {
use std::panic;
panic::set_hook(Box::new(|arg| {
println!("PANIC!");
println!("Details: {:?}", arg);
// debug_here::debug_here!();
}));
}
/// Convert a four-letter string into a 32-bit int.
#[macro_export]
macro_rules! make_name {
($fcc:expr) => {{
let mut c: [u8; 4] = Default::default();
c.copy_from_slice($fcc.as_bytes());
u32::from_le_bytes(c) as usize
}};
}
#[cfg(not(target_os = "none"))]
#[macro_export]
macro_rules! maybe_main {
() => {
extern "Rust" {
fn xous_entry() -> !;
}
fn main() {
xous::arch::ensure_connection().unwrap();
unsafe { xous_entry() };
}
};
}
#[cfg(target_os = "none")]
#[macro_export]
macro_rules! maybe_main {
() => {
use core::panic::PanicInfo;
#[panic_handler]
fn handle_panic(arg: &PanicInfo) -> ! {
use core::fmt::Write;
use xous::{
terminate_process, try_connect, try_send_message, wait_event, Message,
ScalarMessage, CID, SID,
};
// Try to connect to the log server. If this fails, we won't be able to print
// anything anyway.
// We use `try_connect()` here because we want this to work even during an interrupt handler.
// If we've already connected to the log server, then the kernel will reuse the
// connection number.
if let Ok(conn) = try_connect(SID::from_bytes(b"xous-log-server ").unwrap()) {
struct PanicWriter {
conn: CID,
}
impl PanicWriter {
// Group `usize` bytes into a `usize` and return it, beginning
// from `offset` * sizeof(usize) bytes from the start. For example,
// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
// return a usize with 5678 packed into it.
fn group_or_null(data: &[u8], offset: usize) -> usize {
let start = offset * core::mem::size_of::<usize>();
let mut out_array = [0u8; core::mem::size_of::<usize>()];
for i in 0..core::mem::size_of::<usize>() {
out_array[i] = if i + start < data.len() {
data[start + i]
} else {
0
};
}
usize::from_le_bytes(out_array)
}
}
impl core::fmt::Write for PanicWriter {
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
for c in s.as_bytes().chunks(core::mem::size_of::<usize>() * 4) {
// Text is grouped into 4x `usize` words. The id is 1100 plus
// the number of characters in this message.
let mut panic_msg = ScalarMessage {
id: 1100 + c.len(),
arg1: Self::group_or_null(&c, 0),
arg2: Self::group_or_null(&c, 1),
arg3: Self::group_or_null(&c, 2),
arg4: Self::group_or_null(&c, 3),
};
try_send_message(self.conn, Message::Scalar(panic_msg)).ok();
}
Ok(())
}
}
let mut pw = PanicWriter { conn };
// Send the "We're panicking" message (1000).
let panic_start_msg = ScalarMessage {
id: 1000,
arg1: 0,
arg2: 0,
arg3: 0,
arg4: 0,
};
try_send_message(conn, Message::Scalar(panic_start_msg)).ok();
// Send the contents of the panic.
writeln!(&mut pw, "{}", arg).ok();
// Send the "We're done panicking now it's time to quit" message (1200)
let panic_start_msg = ScalarMessage {
id: 1200,
arg1: 0,
arg2: 0,
arg3: 0,
arg4: 0,
};
try_send_message(conn, Message::Scalar(panic_start_msg)).ok();
}
wait_event();
terminate_process();
loop {}
}
extern "Rust" {
fn xous_entry() -> !;
}
#[export_name = "_start"]
pub extern "C" fn _start() {
unsafe { xous_entry() };
}
};
}

View File

108
xous-rs/src/string.rs Normal file
View File

@ -0,0 +1,108 @@
use crate::{
map_memory, send_message, Error, MemoryFlags, MemoryMessage, MemoryRange, MemorySize, Message,
Result, CID,
};
pub struct String<'a> {
raw_slice: &'a mut [u8],
s: &'a str,
len: usize,
}
impl<'a> String<'a> {
pub fn new(max: usize) -> String<'a> {
let mem = map_memory(None, None, max, MemoryFlags::R | MemoryFlags::W).unwrap();
let p = mem.as_mut_ptr();
for i in 0..max {
unsafe { p.add(i).write_volatile(0) };
}
String {
raw_slice: unsafe { core::slice::from_raw_parts_mut(mem.as_mut_ptr(), max) },
s: unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(mem.as_ptr(), 0))
},
len: 0,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Convert a `MemoryMessage` into a `String`
pub fn from_message(message: &'a mut MemoryMessage) -> core::result::Result<String<'a>, core::str::Utf8Error> {
let raw_slice = unsafe { core::slice::from_raw_parts_mut(message.buf.as_mut_ptr(), message.buf.len()) };
let starting_length = message.valid.map(|x| x.get()).unwrap_or(0);
Ok(String {
raw_slice,
s: core::str::from_utf8(unsafe { core::slice::from_raw_parts(message.buf.as_ptr(), starting_length) })?,
len: 0,
})
}
/// Perform an immutable lend of this String to the specified server.
/// This function will block until the server returns.
pub fn lend(&self, connection: CID, id: crate::MessageId) -> core::result::Result<Result, Error> {
let memory_range =
MemoryRange::new(self.raw_slice.as_ptr() as _, self.raw_slice.len()).unwrap();
let msg = MemoryMessage {
id,
buf: memory_range,
offset: None,
valid: MemorySize::new(self.len).map(Some).unwrap_or(None),
};
send_message(connection, Message::Borrow(msg))
}
/// Move this string from the client into the server.
pub fn send(self, connection: CID, id: crate::MessageId) -> core::result::Result<Result, Error> {
let memory_range =
MemoryRange::new(self.raw_slice.as_ptr() as _, self.raw_slice.len()).unwrap();
let msg = MemoryMessage {
id,
buf: memory_range,
offset: None,
valid: MemorySize::new(self.len).map(Some).unwrap_or(None),
};
send_message(connection, Message::Move(msg))
}
/// Clear the contents of this String and set the length to 0
pub fn clear(&mut self) {
self.len = 0;
self.s = unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(
self.raw_slice.as_ptr(),
self.len,
))
};
}
}
impl<'a> core::fmt::Display for String<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.s)
}
}
impl<'a> core::fmt::Write for String<'a> {
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
for c in s.bytes() {
if self.len < self.raw_slice.len() {
self.raw_slice[self.len] = c;
self.len += 1;
}
}
self.s = unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(
self.raw_slice.as_ptr(),
self.len,
))
};
Ok(())
}
}

1352
xous-rs/src/syscall.rs Normal file

File diff suppressed because it is too large Load Diff