initial commit
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
commit
87b48c3665
18
.gitattributes
vendored
Normal file
18
.gitattributes
vendored
Normal 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
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
133
Cargo.lock
generated
Normal file
133
Cargo.lock
generated
Normal 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
10
Cargo.toml
Normal 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
98
macros/Cargo.lock
generated
Normal 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
29
macros/Cargo.toml
Normal 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
137
macros/src/lib.rs
Normal 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
47
src/main.rs
Normal 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
28
xous-rs/Cargo.toml
Normal 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
18
xous-rs/assemble.ps1
Normal 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
22
xous-rs/assemble.sh
Normal 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
|
BIN
xous-rs/bin/riscv32i-unknown-none-elf.a
Normal file
BIN
xous-rs/bin/riscv32i-unknown-none-elf.a
Normal file
Binary file not shown.
BIN
xous-rs/bin/riscv32imac-unknown-none-elf.a
Normal file
BIN
xous-rs/bin/riscv32imac-unknown-none-elf.a
Normal file
Binary file not shown.
BIN
xous-rs/bin/riscv32imac-unknown-xous-elf.a
Normal file
BIN
xous-rs/bin/riscv32imac-unknown-xous-elf.a
Normal file
Binary file not shown.
BIN
xous-rs/bin/riscv32imc-unknown-none-elf.a
Normal file
BIN
xous-rs/bin/riscv32imc-unknown-none-elf.a
Normal file
Binary file not shown.
BIN
xous-rs/bin/riscv64gc-unknown-none-elf.a
Normal file
BIN
xous-rs/bin/riscv64gc-unknown-none-elf.a
Normal file
Binary file not shown.
BIN
xous-rs/bin/riscv64imac-unknown-none-elf.a
Normal file
BIN
xous-rs/bin/riscv64imac-unknown-none-elf.a
Normal file
Binary file not shown.
35
xous-rs/build.rs
Normal file
35
xous-rs/build.rs
Normal 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
683
xous-rs/src/arch/hosted.rs
Normal 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();
|
||||
}
|
41
xous-rs/src/arch/hosted/mem.rs
Normal file
41
xous-rs/src/arch/hosted/mem.rs
Normal 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
9
xous-rs/src/arch/mod.rs
Normal 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
218
xous-rs/src/arch/native.rs
Normal 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();
|
||||
}
|
||||
}
|
32
xous-rs/src/arch/native/mem.rs
Normal file
32
xous-rs/src/arch/native/mem.rs
Normal 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
20
xous-rs/src/asm.S
Normal 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
98
xous-rs/src/carton.rs
Normal 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
649
xous-rs/src/definitions.rs
Normal 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
269
xous-rs/src/ipc.rs
Normal 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
163
xous-rs/src/lib.rs
Normal 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() };
|
||||
}
|
||||
};
|
||||
}
|
0
xous-rs/src/messages/mod.rs
Normal file
0
xous-rs/src/messages/mod.rs
Normal file
108
xous-rs/src/string.rs
Normal file
108
xous-rs/src/string.rs
Normal 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
1352
xous-rs/src/syscall.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user