Skip to content

Commit

Permalink
runtime: implement prototype background RPCs.
Browse files Browse the repository at this point in the history
whitequark committed Oct 29, 2016
1 parent c656a53 commit 2ac85cd
Showing 18 changed files with 252 additions and 87 deletions.
2 changes: 1 addition & 1 deletion artiq/gateware/amp/kernel_cpu.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@

class KernelCPU(Module):
def __init__(self, platform,
exec_address=0x40400000,
exec_address=0x40800000,
main_mem_origin=0x40000000,
l2_size=8192):
self._reset = CSRStorage(reset=1)
2 changes: 1 addition & 1 deletion artiq/runtime.rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ path = "src/lib.rs"
std_artiq = { path = "libstd_artiq" }
lwip = { path = "liblwip", default-features = false }
fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] }
log = { version = "0.3", default-features = false, features = ["max_level_debug"] }
log = { version = "0.3", default-features = false, features = [] }
log_buffer = { version = "1.0" }
byteorder = { version = "0.5", default-features = false }

22 changes: 22 additions & 0 deletions artiq/runtime.rs/libksupport/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions artiq/runtime.rs/libksupport/Cargo.toml
Original file line number Diff line number Diff line change
@@ -8,6 +8,10 @@ name = "ksupport"
path = "lib.rs"
crate-type = ["staticlib"]

[dependencies]
std_artiq = { path = "../libstd_artiq" }
byteorder = { version = "0.5", default-features = false }

[profile.dev]
panic = 'unwind'
opt-level = 2
1 change: 1 addition & 0 deletions artiq/runtime.rs/libksupport/api.rs
Original file line number Diff line number Diff line change
@@ -91,6 +91,7 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(watchdog_clear = ::watchdog_clear),

api!(send_rpc = ::send_rpc),
api!(send_async_rpc = ::send_async_rpc),
api!(recv_rpc = ::recv_rpc),

api!(cache_get = ::cache_get),
1 change: 0 additions & 1 deletion artiq/runtime.rs/libksupport/dyld.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use core::{ptr, slice, str};
use core::slice::SliceExt;
use libc::{c_void, c_char, c_int, size_t};

#[allow(non_camel_case_types)]
58 changes: 54 additions & 4 deletions artiq/runtime.rs/libksupport/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
#![feature(lang_items, needs_panic_runtime, asm, libc, core_slice_ext)]
#![feature(lang_items, needs_panic_runtime, asm, libc, stmt_expr_attributes)]

#![no_std]
#![needs_panic_runtime]

#[macro_use]
extern crate std_artiq as std;
extern crate libc;
extern crate byteorder;

#[path = "../src/board.rs"]
mod board;
#[path = "../src/mailbox.rs"]
mod mailbox;
#[path = "../src/rpc_queue.rs"]
mod rpc_queue;

#[path = "../src/proto.rs"]
mod proto;
#[path = "../src/kernel_proto.rs"]
mod kernel_proto;
#[path = "../src/rpc_proto.rs"]
mod rpc_proto;

mod dyld;
mod api;

use core::{mem, ptr, slice, str};
use std::io::Cursor;
use libc::{c_char, size_t};
use kernel_proto::*;
use dyld::Library;

#[no_mangle]
pub extern "C" fn malloc(_size: usize) -> *mut libc::c_void {
unimplemented!()
}

#[no_mangle]
pub extern "C" fn realloc(_ptr: *mut libc::c_void, _size: usize) -> *mut libc::c_void {
unimplemented!()
}

#[no_mangle]
pub extern "C" fn free(_ptr: *mut libc::c_void) {
unimplemented!()
}

fn send(request: &Message) {
unsafe { mailbox::send(request as *const _ as usize) }
while !mailbox::acknowledged() {}
@@ -81,13 +107,37 @@ extern fn send_rpc(service: u32, tag: *const u8, data: *const *const ()) {
let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) };

send(&RpcSend {
service: service as u32,
batch: service == 0,
async: false,
service: service,
tag: tag,
data: data
})
}

extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) {
extern { fn strlen(s: *const c_char) -> size_t; }
let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) };

while rpc_queue::full() {}
rpc_queue::enqueue(|mut slice| {
let length = {
let mut writer = Cursor::new(&mut slice[4..]);
try!(rpc_proto::send_args(&mut writer, service, tag, data));
writer.position()
};
proto::write_u32(&mut slice, length as u32)
}).unwrap_or_else(|err| {
assert!(err.kind() == std::io::ErrorKind::UnexpectedEof);

send(&RpcSend {
async: true,
service: service,
tag: tag,
data: data
})
})
}

extern fn recv_rpc(slot: *mut ()) -> usize {
send(&RpcRecvRequest(slot));
recv!(&RpcRecvReply(ref result) => {
@@ -206,7 +256,7 @@ unsafe fn attribute_writeback(typeinfo: *const ()) {
attributes = attributes.offset(1);

if !(*attribute).tag.is_null() {
send_rpc(0, (*attribute).tag, [
send_async_rpc(0, (*attribute).tag, [
&object as *const _ as *const (),
&(*attribute).name as *const _ as *const (),
(object as usize + (*attribute).offset) as *const ()
51 changes: 1 addition & 50 deletions artiq/runtime.rs/libstd_artiq/lib.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ extern crate rustc_unicode;
extern crate alloc_artiq;
extern crate alloc;
#[macro_use]
#[macro_reexport(vec)]
#[macro_reexport(vec, format)]
extern crate collections;
extern crate libc;

@@ -31,52 +31,3 @@ pub mod prelude {

pub mod error;
pub mod io;

use core::fmt::Write;

#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*)));
}

#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

extern {
fn putchar(c: libc::c_int) -> libc::c_int;
fn readchar() -> libc::c_char;
}

pub struct Console;

impl core::fmt::Write for Console {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
for c in s.bytes() { unsafe { putchar(c as i32); } }
Ok(())
}
}

pub fn print_fmt(args: self::core::fmt::Arguments) {
let _ = Console.write_fmt(args);
}

#[lang = "panic_fmt"]
extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! {
let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args);
let _ = write!(Console, "waiting for debugger...\n");
unsafe {
let _ = readchar();
loop { asm!("l.trap 0") }
}
}

// Allow linking with crates that are built as -Cpanic=unwind even when the root crate
// is built with -Cpanic=abort.
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
loop {}
}
6 changes: 3 additions & 3 deletions artiq/runtime.rs/src/kernel_proto.rs
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@
use core::marker::PhantomData;
use core::fmt;

pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000;
pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40440000;
pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800080;
pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40840000;
pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
pub const KSUPPORT_HEADER_SIZE: usize = 0x80;

@@ -42,8 +42,8 @@ pub enum Message<'a> {
WatchdogClear { id: usize },

RpcSend {
async: bool,
service: u32,
batch: bool,
tag: &'a [u8],
data: *const *const ()
},
54 changes: 52 additions & 2 deletions artiq/runtime.rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm)]
#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm,
lang_items)]

#[macro_use]
extern crate std_artiq as std;
@@ -11,13 +12,54 @@ extern crate byteorder;
extern crate fringe;
extern crate lwip;

use core::fmt::Write;
use logger::BufferLogger;

extern {
fn putchar(c: libc::c_int) -> libc::c_int;
fn readchar() -> libc::c_char;
}

#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*)));
}

#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

pub struct Console;

impl core::fmt::Write for Console {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
for c in s.bytes() { unsafe { putchar(c as i32); } }
Ok(())
}
}

pub fn print_fmt(args: self::core::fmt::Arguments) {
let _ = Console.write_fmt(args);
}

#[lang = "panic_fmt"]
extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! {
let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args);
let _ = write!(Console, "waiting for debugger...\n");
unsafe {
let _ = readchar();
loop { asm!("l.trap 0") }
}
}

mod board;
mod config;
mod clock;
mod rtio_crg;
mod mailbox;
mod rpc_queue;

mod urc;
mod sched;
@@ -29,9 +71,9 @@ mod kernel_proto;
mod session_proto;
mod moninj_proto;
mod analyzer_proto;
mod rpc_proto;

mod kernel;
mod rpc;
mod session;
mod moninj;
#[cfg(has_rtio_analyzer)]
@@ -44,6 +86,14 @@ extern {

include!(concat!(env!("OUT_DIR"), "/git_info.rs"));

// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort.
// This is never called.
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
loop {}
}

#[no_mangle]
pub unsafe extern fn rust_main() {
static mut LOG_BUFFER: [u8; 4096] = [0; 4096];
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::slice;
use std::io::{self, Read, Write, BufWriter};
#![allow(dead_code)]

use core::slice;
use std::io::{self, Read, Write};
use proto::*;
use self::tag::{Tag, TagIterator, split_tag};

@@ -74,6 +76,7 @@ unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (),
pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (),
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
let mut it = TagIterator::new(tag_bytes);
#[cfg(not(ksupport))]
trace!("recv ...->{}", it);

let tag = it.next().expect("truncated tag");
@@ -98,7 +101,6 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
})
}

let writer = &mut BufWriter::new(writer);
try!(write_u8(writer, tag.as_u8()));
match tag {
Tag::None => Ok(()),
@@ -161,14 +163,16 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
}
}

pub fn send_args(writer: &mut Write, tag_bytes: &[u8],
pub fn send_args(writer: &mut Write, service: u32, tag_bytes: &[u8],
data: *const *const ()) -> io::Result<()> {
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);

let mut args_it = TagIterator::new(arg_tags_bytes);
let return_it = TagIterator::new(return_tag_bytes);
trace!("send ({})->{}", args_it, return_it);
#[cfg(not(ksupport))]
trace!("send<{}>({})->{}", service, args_it, return_it);

try!(write_u32(writer, service));
for index in 0.. {
if let Some(arg_tag) = args_it.next() {
let mut data = unsafe { *data.offset(index) };
61 changes: 61 additions & 0 deletions artiq/runtime.rs/src/rpc_queue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#![allow(dead_code)]

use core::ptr::{read_volatile, write_volatile};
use core::slice;
use board;

const SEND_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 4) as *mut usize;
const RECV_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 8) as *mut usize;

const QUEUE_BEGIN: usize = 0x40400000;
const QUEUE_END: usize = 0x40800000;
const QUEUE_CHUNK: usize = 0x1000;

pub unsafe fn init() {
write_volatile(SEND_MAILBOX, QUEUE_BEGIN);
write_volatile(RECV_MAILBOX, QUEUE_END);
}

fn next(mut addr: usize) -> usize {
debug_assert!(addr % QUEUE_CHUNK == 0);
debug_assert!(addr >= QUEUE_BEGIN && addr < QUEUE_END);

addr += QUEUE_CHUNK;
if addr == QUEUE_END { addr = QUEUE_BEGIN }
addr
}

pub fn empty() -> bool {
unsafe { read_volatile(SEND_MAILBOX) == read_volatile(RECV_MAILBOX) }
}

pub fn full() -> bool {
unsafe { next(read_volatile(SEND_MAILBOX)) == read_volatile(RECV_MAILBOX) }
}

pub fn enqueue<T, E, F>(f: F) -> Result<T, E>
where F: FnOnce(&mut [u8]) -> Result<T, E> {
debug_assert!(!full());

unsafe {
let slice = slice::from_raw_parts_mut(read_volatile(SEND_MAILBOX) as *mut u8, QUEUE_CHUNK);
f(slice).and_then(|x| {
write_volatile(SEND_MAILBOX, next(read_volatile(SEND_MAILBOX)));
Ok(x)
})
}
}

pub fn dequeue<T, E, F>(f: F) -> Result<T, E>
where F: FnOnce(&mut [u8]) -> Result<T, E> {
debug_assert!(!empty());

unsafe {
board::flush_cpu_dcache();
let slice = slice::from_raw_parts_mut(read_volatile(RECV_MAILBOX) as *mut u8, QUEUE_CHUNK);
f(slice).and_then(|x| {
write_volatile(RECV_MAILBOX, next(read_volatile(RECV_MAILBOX)));
Ok(x)
})
}
}
41 changes: 30 additions & 11 deletions artiq/runtime.rs/src/session.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::prelude::v1::*;
use std::{mem, str};
use std::cell::RefCell;
use std::fmt::Write;
use std::io::{self, Read};
use {config, rtio_crg, clock, mailbox, kernel};
use std::io::{self, Read, Write, BufWriter};
use {config, rtio_crg, clock, mailbox, rpc_queue, kernel};
use logger::BufferLogger;
use cache::Cache;
use urc::Urc;
use sched::{ThreadHandle, Waiter, Spawner};
use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY};
use byteorder::{ByteOrder, NetworkEndian};

use rpc;
use rpc_proto as rpc;
use session_proto as host;
use kernel_proto as kern;

@@ -132,7 +132,8 @@ fn kern_recv_notrace<R, F>(waiter: Waiter, f: F) -> io::Result<R>
where F: FnOnce(&kern::Message) -> io::Result<R> {
try!(waiter.until(|| mailbox::receive() != 0));
if !kernel::validate(mailbox::receive()) {
return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid kernel CPU pointer"))
let message = format!("invalid kernel CPU pointer 0x{:x}", mailbox::receive());
return Err(io::Error::new(io::ErrorKind::InvalidData, message))
}

f(unsafe { mem::transmute::<usize, &kern::Message>(mailbox::receive()) })
@@ -352,6 +353,7 @@ fn process_kern_message(waiter: Waiter,
kern_recv_dotrace(request);
match request {
&kern::Log(args) => {
use std::fmt::Write;
try!(session.log_buffer.write_fmt(args)
.map_err(|_| io_error("cannot append to session log buffer")));
session.flush_log_buffer();
@@ -383,15 +385,13 @@ fn process_kern_message(waiter: Waiter,
kern_acknowledge()
}

&kern::RpcSend { service, batch, tag, data } => {
&kern::RpcSend { async, service, tag, data } => {
match stream {
None => unexpected!("unexpected RPC in flash kernel"),
Some(ref mut stream) => {
try!(host_write(stream, host::Reply::RpcRequest {
service: service
}));
try!(rpc::send_args(stream, tag, data));
if !batch {
try!(host_write(stream, host::Reply::RpcRequest));
try!(rpc::send_args(&mut BufWriter::new(stream), service, tag, data));
if !async {
session.kernel_state = KernelState::RpcWait
}
kern_acknowledge()
@@ -465,12 +465,27 @@ fn process_kern_message(waiter: Waiter,
})
}

fn process_kern_queued_rpc(stream: &mut TcpStream,
session: &mut Session) -> io::Result<()> {
rpc_queue::dequeue(|slice| {
trace!("comm<-kern (async RPC)");
let length = NetworkEndian::read_u32(slice) as usize;
try!(host_write(stream, host::Reply::RpcRequest));
try!(stream.write(&slice[4..][..length]));
Ok(())
})
}

fn host_kernel_worker(waiter: Waiter,
stream: &mut TcpStream,
congress: &mut Congress) -> io::Result<()> {
let mut session = Session::new(congress);

loop {
if !rpc_queue::empty() {
try!(process_kern_queued_rpc(stream, &mut session))
}

if stream.readable() {
try!(process_host_message(waiter, stream, &mut session));
}
@@ -509,6 +524,10 @@ fn flash_kernel_worker(waiter: Waiter,
try!(kern_run(&mut session));

loop {
if !rpc_queue::empty() {
return Err(io_error("unexpected background RPC in flash kernel"))
}

if mailbox::receive() != 0 {
if try!(process_kern_message(waiter, None, &mut session)) {
return Ok(())
5 changes: 2 additions & 3 deletions artiq/runtime.rs/src/session_proto.rs
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ pub enum Reply<'a> {
backtrace: &'a [usize]
},

RpcRequest { service: u32 },
RpcRequest,

FlashRead(&'a [u8]),
FlashOk,
@@ -170,9 +170,8 @@ impl<'a> Reply<'a> {
}
},

Reply::RpcRequest { service } => {
Reply::RpcRequest => {
try!(write_u8(writer, 10));
try!(write_u32(writer, service));
},

Reply::FlashRead(ref bytes) => {
3 changes: 3 additions & 0 deletions artiq/runtime/Makefile
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@ OBJECTS := flash_storage.o main.o
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o

RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b
export CORE_IO_COMMIT

CFLAGS += \
-I$(LIBALLOC_DIRECTORY) \
@@ -54,6 +56,7 @@ $(RUSTOUT_DIRECTORY)/libksupport.a:
--manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \
--target=or1k-unknown-none -- \
$(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \
--cfg ksupport \
-C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \
-L../libcompiler-rt

6 changes: 5 additions & 1 deletion artiq/runtime/ksupport.ld
Original file line number Diff line number Diff line change
@@ -6,10 +6,14 @@ INCLUDE generated/regions.ld

/* First 4M of main memory are reserved for runtime
* code/data/heap, then comes kernel memory.
* Next 4M of main memory are reserved for
* the background RPC queue.
* First 256K of kernel memory are for support code.
* Support code is loaded at ORIGIN-0x80 so that ELF headers
* are also loaded.
*/
MEMORY {
ksupport (RWX) : ORIGIN = 0x40400000, LENGTH = 0x40000
ksupport (RWX) : ORIGIN = 0x40800080, LENGTH = 0x40000
}

/* Kernel stack is at the end of main RAM. */
4 changes: 2 additions & 2 deletions artiq/runtime/ksupport_glue.c
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@

void send_to_log(const char *ptr, size_t length);

#define KERNELCPU_EXEC_ADDRESS 0x40400000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40440000
#define KERNELCPU_EXEC_ADDRESS 0x40800080
#define KERNELCPU_PAYLOAD_ADDRESS 0x40840000
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
#define KSUPPORT_HEADER_SIZE 0x80

4 changes: 1 addition & 3 deletions artiq/runtime/runtime.ld
Original file line number Diff line number Diff line change
@@ -74,9 +74,7 @@ SECTIONS
.heap :
{
_fheap = .;
. = ORIGIN(runtime) + LENGTH(runtime)
/* Leave room for ksupport headers. */
- 0x1000;
. = ORIGIN(runtime) + LENGTH(runtime);
_eheap = .;
} > runtime

0 comments on commit 2ac85cd

Please sign in to comment.