Rust: implement basic communication with kernel CPU.

This commit is contained in:
whitequark 2016-10-01 04:20:27 +00:00
parent 1cbb187136
commit b3b1ea71c5
12 changed files with 619 additions and 114 deletions

View File

@ -8,7 +8,7 @@ build = "build.rs"
walkdir = "0.1" walkdir = "0.1"
[lib] [lib]
name = "artiq_rust" name = "runtime"
crate-type = ["staticlib"] crate-type = ["staticlib"]
path = "src/lib.rs" path = "src/lib.rs"

View File

@ -122,25 +122,25 @@ extern {
pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err; pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err;
pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb; pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb;
pub fn tcp_accept(pcb: *mut tcp_pcb, pub fn tcp_accept(pcb: *mut tcp_pcb,
accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb, accept: Option<extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb,
err: err) -> err); err: err) -> err>);
pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16, pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16,
connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err; connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err;
pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err; pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err;
pub fn tcp_sent(pcb: *mut tcp_pcb, pub fn tcp_sent(pcb: *mut tcp_pcb,
sent: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err); sent: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err>);
pub fn tcp_recv(pcb: *mut tcp_pcb, pub fn tcp_recv(pcb: *mut tcp_pcb,
recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf, recv: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf,
err: err) -> err); err: err) -> err>);
pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16); pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16);
pub fn tcp_poll(pcb: *mut tcp_pcb, pub fn tcp_poll(pcb: *mut tcp_pcb,
poll: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb), poll: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb)>,
interval: u8); interval: u8);
pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err; pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err;
pub fn tcp_close(pcb: *mut tcp_pcb) -> err; pub fn tcp_close(pcb: *mut tcp_pcb) -> err;
pub fn tcp_abort(pcb: *mut tcp_pcb); pub fn tcp_abort(pcb: *mut tcp_pcb);
pub fn tcp_err(pcb: *mut tcp_pcb, pub fn tcp_err(pcb: *mut tcp_pcb,
err: extern fn(arg: *mut c_void, err: err)); err: Option<extern fn(arg: *mut c_void, err: err)>);
// nonstandard // nonstandard
pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16;
@ -154,7 +154,7 @@ extern {
pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err; pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err;
pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err; pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err;
pub fn udp_recv(pcb: *mut udp_pcb, pub fn udp_recv(pcb: *mut udp_pcb,
recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf, recv: Option<extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf,
addr: *mut ip_addr, port: u16), addr: *mut ip_addr, port: u16)>,
recv_arg: *mut c_void); recv_arg: *mut c_void);
} }

View File

@ -279,12 +279,12 @@ impl UdpSocket {
recv_buffer: LinkedList::new() recv_buffer: LinkedList::new()
})); }));
let arg = &mut *state as *mut RefCell<UdpSocketState> as *mut _; let arg = &mut *state as *mut RefCell<UdpSocketState> as *mut _;
lwip_sys::udp_recv(raw, recv, arg); lwip_sys::udp_recv(raw, Some(recv), arg);
Ok(UdpSocket { raw: raw, state: state }) Ok(UdpSocket { raw: raw, state: state })
} }
} }
pub fn state(&self) -> *const RefCell<UdpSocketState> { pub fn state(&self) -> &RefCell<UdpSocketState> {
&*self.state &*self.state
} }
@ -376,12 +376,12 @@ impl TcpListener {
})); }));
let arg = &mut *state as *mut RefCell<TcpListenerState> as *mut _; let arg = &mut *state as *mut RefCell<TcpListenerState> as *mut _;
lwip_sys::tcp_arg(raw2, arg); lwip_sys::tcp_arg(raw2, arg);
lwip_sys::tcp_accept(raw2, accept); lwip_sys::tcp_accept(raw2, Some(accept));
Ok(TcpListener { raw: raw2, state: state }) Ok(TcpListener { raw: raw2, state: state })
} }
} }
pub fn state(&self) -> *const RefCell<TcpListenerState> { pub fn state(&self) -> &RefCell<TcpListenerState> {
&*self.state &*self.state
} }
@ -467,14 +467,14 @@ impl TcpStream {
})); }));
let arg = &mut *state as *mut RefCell<TcpStreamState> as *mut _; let arg = &mut *state as *mut RefCell<TcpStreamState> as *mut _;
lwip_sys::tcp_arg(raw, arg); lwip_sys::tcp_arg(raw, arg);
lwip_sys::tcp_recv(raw, recv); lwip_sys::tcp_recv(raw, Some(recv));
lwip_sys::tcp_sent(raw, sent); lwip_sys::tcp_sent(raw, Some(sent));
lwip_sys::tcp_err(raw, err); lwip_sys::tcp_err(raw, Some(err));
TcpStream { raw: raw, state: state } TcpStream { raw: raw, state: state }
} }
} }
pub fn state(&self) -> *const RefCell<TcpStreamState> { pub fn state(&self) -> &RefCell<TcpStreamState> {
&*self.state &*self.state
} }
@ -536,6 +536,11 @@ impl TcpStream {
impl Drop for TcpStream { impl Drop for TcpStream {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// lwip *will* try to call back after tcp_close
lwip_sys::tcp_recv(self.raw, None);
lwip_sys::tcp_sent(self.raw, None);
lwip_sys::tcp_err(self.raw, None);
// tcp_close can fail here, but in drop() we don't care // tcp_close can fail here, but in drop() we don't care
let _ = lwip_sys::tcp_close(self.raw); let _ = lwip_sys::tcp_close(self.raw);
} }

View File

@ -0,0 +1,37 @@
use core::ptr;
use board::csr::kernel_cpu;
use mailbox;
const KERNELCPU_EXEC_ADDRESS: usize = 0x42000000;
const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x42020000;
const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024);
const KSUPPORT_HEADER_SIZE: usize = 0x80;
pub unsafe fn start() {
if kernel_cpu::reset_read() == 0 {
panic!("attempted to start kernel CPU when it is already running")
}
stop();
extern {
static _binary_ksupport_elf_start: ();
static _binary_ksupport_elf_end: ();
}
let ksupport_start = &_binary_ksupport_elf_start as *const _ as usize;
let ksupport_end = &_binary_ksupport_elf_end as *const _ as usize;
ptr::copy_nonoverlapping(ksupport_start as *const u8,
(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8,
ksupport_end - ksupport_start);
kernel_cpu::reset_write(0);
}
pub fn stop() {
unsafe { kernel_cpu::reset_write(1) }
mailbox::acknowledge();
}
pub fn validate(ptr: usize) -> bool {
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
}

View File

@ -0,0 +1,323 @@
use std::io;
use mailbox;
use kernel;
#[derive(Debug)]
pub struct Exception<'a> {
pub name: &'a str,
pub file: &'a str,
pub line: u32,
pub column: u32,
pub function: &'a str,
pub message: &'a str,
pub param: [u64; 3],
}
pub use self::c::BacktraceItem;
#[derive(Debug)]
pub enum Message<'a> {
LoadRequest(&'a [u8]),
LoadReply { error: Option<&'a str> },
NowInitRequest,
NowInitReply(u64),
NowSave(u64),
RunFinished,
RunException {
exception: Exception<'a>,
backtrace: &'a [BacktraceItem]
},
WatchdogSetRequest { ms: u64 },
WatchdogSetReply { id: usize },
WatchdogClear { id: usize },
RpcSend {
service: u32,
tag: &'a [u8],
data: *const *const ()
},
RpcRecvRequest {
slot: *mut ()
},
RpcRecvReply {
alloc_size: usize,
exception: Option<Exception<'a>>
},
CacheGetRequest { key: &'a str },
CacheGetReply { value: &'static [u32] },
CachePutRequest { key: &'a str, value: &'static [u32] },
CachePutReply { succeeded: bool },
Log(&'a str)
}
pub use self::Message::*;
impl<'a> Message<'a> {
fn into_lower<R, F: FnOnce(*const ()) -> R>(self, f: F) -> R {
match self {
Message::LoadRequest(library) => {
let msg = c::LoadRequest {
ty: c::Type::LoadRequest,
library: library.as_ptr() as *const _
};
f(&msg as *const _ as *const _)
}
Message::NowInitReply(now) => {
let msg = c::NowInitReply {
ty: c::Type::NowInitReply,
now: now
};
f(&msg as *const _ as *const _)
}
other => panic!("Message::into_lower: {:?} unimplemented", other)
}
}
unsafe fn from_lower(ptr: *const ()) -> Self {
let msg = ptr as *const c::Message;
match (*msg).ty {
c::Type::LoadReply => {
let msg = ptr as *const c::LoadReply;
let error = if (*msg).error.is_null() {
None
} else {
Some(c::from_c_str((*msg).error))
};
Message::LoadReply { error: error }
}
c::Type::NowInitRequest => Message::NowInitRequest,
c::Type::NowSave => {
let msg = ptr as *const c::NowSave;
Message::NowSave((*msg).now)
}
c::Type::RunFinished => Message::RunFinished,
c::Type::Log => {
let msg = ptr as *const c::Log;
Message::Log(c::from_c_str_len((*msg).buf, (*msg).len))
}
ref other => panic!("Message::from_lower: {:?} unimplemented", other)
}
}
pub fn send_and_wait(self, waiter: ::sched::Waiter) -> io::Result<()> {
self.into_lower(|ptr| {
unsafe { mailbox::send(ptr as usize) }
waiter.until(mailbox::acknowledged)
})
}
pub fn wait_and_receive<R, F>(waiter: ::sched::Waiter, f: F) -> io::Result<R>
where F: FnOnce(Message<'a>) -> 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 msg = unsafe { Self::from_lower(mailbox::receive() as *const ()) };
Ok(try!(f(msg)))
}
pub fn acknowledge() {
unsafe { mailbox::acknowledge() }
}
}
// Low-level representation, compatible with the C code in ksupport
mod c {
use libc::{c_void, c_int, c_char, size_t};
#[repr(u32)]
#[derive(Debug)]
pub enum Type {
LoadRequest,
LoadReply,
NowInitRequest,
NowInitReply,
NowSave,
RunFinished,
RunException,
WatchdogSetRequest,
WatchdogSetReply,
WatchdogClear,
RpcSend,
RpcRecvRequest,
RpcRecvReply,
RpcBatch,
CacheGetRequest,
CacheGetReply,
CachePutRequest,
CachePutReply,
Log,
}
#[repr(C)]
#[derive(Debug)]
pub struct Message {
pub ty: Type
}
// kernel messages
#[repr(C)]
#[derive(Debug)]
pub struct LoadRequest {
pub ty: Type,
pub library: *const c_void,
}
#[repr(C)]
#[derive(Debug)]
pub struct LoadReply {
pub ty: Type,
pub error: *const c_char
}
#[repr(C)]
#[derive(Debug)]
pub struct NowInitReply {
pub ty: Type,
pub now: u64
}
#[repr(C)]
#[derive(Debug)]
pub struct NowSave {
pub ty: Type,
pub now: u64
}
#[repr(C)]
#[derive(Debug)]
pub struct RunException {
pub ty: Type,
pub exception: *const Exception,
pub backtrace: *const BacktraceItem,
pub backtrace_size: size_t
}
#[repr(C)]
#[derive(Debug)]
pub struct WatchdogSetRequest {
pub ty: Type,
pub ms: c_int
}
#[repr(C)]
#[derive(Debug)]
pub struct WatchdogSetReply {
pub ty: Type,
pub id: c_int
}
#[repr(C)]
#[derive(Debug)]
pub struct WatchdogClear {
pub ty: Type,
pub id: c_int
}
#[repr(C)]
#[derive(Debug)]
pub struct RpcSend {
pub ty: Type,
pub service: c_int,
pub tag: *const c_char,
pub data: *const *const c_void
}
#[repr(C)]
#[derive(Debug)]
pub struct RpcRecvRequest {
pub ty: Type,
pub slot: *mut c_void
}
#[repr(C)]
#[derive(Debug)]
pub struct RpcRecvReply {
pub ty: Type,
pub alloc_size: c_int,
pub exception: *const Exception
}
#[repr(C)]
#[derive(Debug)]
pub struct CacheGetRequest {
pub ty: Type,
pub key: *const c_char
}
#[repr(C)]
#[derive(Debug)]
pub struct CacheGetReply {
pub ty: Type,
pub length: size_t,
pub elements: *const u32
}
#[repr(C)]
#[derive(Debug)]
pub struct CachePutRequest {
pub ty: Type,
pub key: *const c_char,
pub length: size_t,
pub elements: *const u32
}
#[repr(C)]
#[derive(Debug)]
pub struct CachePutReply {
pub ty: Type,
pub succeeded: c_int
}
#[repr(C)]
#[derive(Debug)]
pub struct Log {
pub ty: Type,
pub buf: *const c_char,
pub len: size_t
}
// Supplementary structures
#[repr(C)]
#[derive(Debug)]
pub struct Exception {
pub name: *const c_char, // or typeinfo
pub file: *const c_char,
pub line: u32,
pub column: u32,
pub function: *const c_char,
pub message: *const c_char,
pub param: [u64; 3],
}
#[repr(C)]
#[derive(Debug)]
pub struct BacktraceItem {
pub function: usize,
pub offset: usize
}
pub unsafe fn from_c_str_len<'a>(ptr: *const c_char, len: size_t) -> &'a str {
use core::{str, slice};
str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, len))
}
pub unsafe fn from_c_str<'a>(ptr: *const c_char) -> &'a str {
extern { fn strlen(cs: *const c_char) -> size_t; }
from_c_str_len(ptr, strlen(ptr))
}
}

View File

@ -20,7 +20,10 @@ mod mailbox;
mod logger; mod logger;
mod kernel_proto;
mod session_proto; mod session_proto;
mod kernel;
mod session; mod session;
extern { extern {
@ -42,9 +45,10 @@ pub unsafe extern fn rust_main() {
network_init(); network_init();
let mut scheduler = sched::Scheduler::new(); let mut scheduler = sched::Scheduler::new();
scheduler.spawn(4096, move |waiter| { scheduler.spawn(8192, move |waiter| {
session::handler(waiter, logger) session::handler(waiter, logger)
}); });
loop { loop {
lwip_service(); lwip_service();
scheduler.run() scheduler.run()

View File

@ -48,9 +48,9 @@ impl Log for BufferLogger {
if self.enabled(record.metadata()) { if self.enabled(record.metadata()) {
use core::fmt::Write; use core::fmt::Write;
writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}", writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}",
record.level(), record.location().module_path(), record.args()).unwrap(); record.level(), record.target(), record.args()).unwrap();
println!("{:>5}({}): {}", println!("{:>5}({}): {}",
record.level(), record.location().module_path(), record.args()); record.level(), record.target(), record.args());
} }
} }
} }

View File

@ -1,14 +1,12 @@
use core::ptr::{read_volatile, write_volatile}; use core::ptr::{read_volatile, write_volatile};
use board; use board;
const MAILBOX: *mut u32 = board::mem::MAILBOX_BASE as *mut u32; const MAILBOX: *mut usize = board::mem::MAILBOX_BASE as *mut usize;
static mut last: u32 = 0; static mut last: usize = 0;
pub fn send(data: u32) { pub unsafe fn send(data: usize) {
unsafe { last = data;
last = data; write_volatile(MAILBOX, data)
write_volatile(MAILBOX, data)
}
} }
pub fn acknowledged() -> bool { pub fn acknowledged() -> bool {
@ -18,12 +16,7 @@ pub fn acknowledged() -> bool {
} }
} }
pub fn send_and_wait(data: u32) { pub fn receive() -> usize {
send(data);
while !acknowledged() {}
}
pub fn receive() -> u32 {
unsafe { unsafe {
let data = read_volatile(MAILBOX); let data = read_volatile(MAILBOX);
if data == last { if data == last {
@ -37,15 +30,6 @@ pub fn receive() -> u32 {
} }
} }
pub fn wait_and_receive() -> u32 {
loop {
let data = receive();
if data != 0 {
return data
}
}
}
pub fn acknowledge() { pub fn acknowledge() {
unsafe { write_volatile(MAILBOX, 0) } unsafe { write_volatile(MAILBOX, 0) }
} }

View File

@ -104,8 +104,8 @@ impl Scheduler {
} }
} }
#[derive(Debug)]
enum WaitEvent { enum WaitEvent {
Completion(*const (Fn() -> bool + 'static)),
UdpReadable(*const RefCell<lwip::UdpSocketState>), UdpReadable(*const RefCell<lwip::UdpSocketState>),
TcpAcceptable(*const RefCell<lwip::TcpListenerState>), TcpAcceptable(*const RefCell<lwip::TcpListenerState>),
TcpWriteable(*const RefCell<lwip::TcpStreamState>), TcpWriteable(*const RefCell<lwip::TcpStreamState>),
@ -115,6 +115,8 @@ enum WaitEvent {
impl WaitEvent { impl WaitEvent {
fn completed(&self) -> bool { fn completed(&self) -> bool {
match *self { match *self {
WaitEvent::Completion(f) =>
unsafe { (*f)() },
WaitEvent::UdpReadable(state) => WaitEvent::UdpReadable(state) =>
unsafe { (*state).borrow().readable() }, unsafe { (*state).borrow().readable() },
WaitEvent::TcpAcceptable(state) => WaitEvent::TcpAcceptable(state) =>
@ -127,12 +129,27 @@ impl WaitEvent {
} }
} }
// *const DST doesn't have impl Debug
impl ::core::fmt::Debug for WaitEvent {
fn fmt(&self, f: &mut ::core::fmt::Formatter) ->
::core::result::Result<(), ::core::fmt::Error> {
write!(f, "WaitEvent...")
}
}
unsafe impl Send for WaitEvent {} unsafe impl Send for WaitEvent {}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Waiter<'a>(&'a Yielder<WaitResult, WaitRequest, OwnedStack>); pub struct Waiter<'a>(&'a Yielder<WaitResult, WaitRequest, OwnedStack>);
impl<'a> Waiter<'a> { impl<'a> Waiter<'a> {
pub fn relinquish(&self) {
self.0.suspend(WaitRequest {
timeout: None,
event: None
});
}
pub fn sleep(&self, duration: Duration) -> Result<()> { pub fn sleep(&self, duration: Duration) -> Result<()> {
let request = WaitRequest { let request = WaitRequest {
timeout: Some(Instant::now() + duration), timeout: Some(Instant::now() + duration),
@ -154,6 +171,13 @@ impl<'a> Waiter<'a> {
} }
} }
pub fn until<F: Fn() -> bool + 'static>(&self, f: F) -> Result<()> {
self.suspend(WaitRequest {
timeout: None,
event: Some(WaitEvent::Completion(&f as *const _))
})
}
pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> { pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> {
self.suspend(WaitRequest { self.suspend(WaitRequest {
timeout: None, timeout: None,
@ -239,6 +263,10 @@ impl<'a> UdpSocket<'a> {
(&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]);
Ok(len) Ok(len)
} }
pub fn readable(&self) -> bool {
self.lower.state().borrow().readable()
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -265,6 +293,10 @@ impl<'a> TcpListener<'a> {
buffer: None buffer: None
}, addr)) }, addr))
} }
pub fn acceptable(&self) -> bool {
self.lower.state().borrow().acceptable()
}
} }
pub use self::lwip::Shutdown; pub use self::lwip::Shutdown;
@ -280,6 +312,14 @@ impl<'a> TcpStream<'a> {
pub fn shutdown(&self, how: Shutdown) -> Result<()> { pub fn shutdown(&self, how: Shutdown) -> Result<()> {
Ok(try!(self.lower.shutdown(how))) Ok(try!(self.lower.shutdown(how)))
} }
pub fn readable(&self) -> bool {
self.buffer.is_some() || self.lower.state().borrow().readable()
}
pub fn writeable(&self) -> bool {
self.lower.state().borrow().writeable()
}
} }
impl<'a> Read for TcpStream<'a> { impl<'a> Read for TcpStream<'a> {

View File

@ -1,12 +1,23 @@
use std::prelude::v1::*; use std::prelude::v1::*;
use std::str; use std::str;
use std::io::{self, Read, ErrorKind}; use std::io::{self, Read};
use {config, rtio_crg}; use {config, rtio_crg, clock, mailbox, kernel};
use logger::BufferLogger; use logger::BufferLogger;
use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY}; use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY};
use session_proto::*;
#[derive(Debug, Clone, Copy)] use session_proto as host;
use kernel_proto as kern;
macro_rules! unexpected {
($($arg:tt)*) => {
{
error!($($arg)*);
return Err(io::Error::new(io::ErrorKind::InvalidData, "protocol error"))
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum KernelState { enum KernelState {
Absent, Absent,
Loaded, Loaded,
@ -17,19 +28,16 @@ enum KernelState {
#[derive(Debug)] #[derive(Debug)]
pub struct Session { pub struct Session {
kernel_state: KernelState, kernel_state: KernelState,
} watchdog_set: clock::WatchdogSet,
now: u64
extern {
fn kloader_stop();
fn watchdog_init();
fn kloader_start_idle_kernel();
} }
impl Session { impl Session {
pub fn new() -> Session { pub fn new() -> Session {
unsafe { kloader_stop(); }
Session { Session {
kernel_state: KernelState::Absent kernel_state: KernelState::Absent,
watchdog_set: clock::WatchdogSet::new(),
now: 0
} }
} }
@ -41,16 +49,6 @@ impl Session {
} }
} }
impl Drop for Session {
fn drop(&mut self) {
unsafe {
kloader_stop();
watchdog_init();
kloader_start_idle_kernel();
}
}
}
fn check_magic(stream: &mut TcpStream) -> io::Result<()> { fn check_magic(stream: &mut TcpStream) -> io::Result<()> {
const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; const MAGIC: &'static [u8] = b"ARTIQ coredev\n";
@ -63,89 +61,203 @@ fn check_magic(stream: &mut TcpStream) -> io::Result<()> {
} }
} }
fn handle_request(stream: &mut TcpStream, fn host_read(stream: &mut TcpStream) -> io::Result<host::Request> {
logger: &BufferLogger, let request = try!(host::Request::read_from(stream));
session: &mut Session) -> io::Result<()> { match &request {
fn read_request(stream: &mut TcpStream) -> io::Result<Request> { &host::Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"),
let request = try!(Request::read_from(stream)); _ => trace!("comm<-host {:?}", request)
match &request {
&Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"),
_ => trace!("comm<-host {:?}", request)
}
Ok(request)
} }
Ok(request)
}
fn write_reply(stream: &mut TcpStream, reply: Reply) -> io::Result<()> { fn host_write(stream: &mut TcpStream, reply: host::Reply) -> io::Result<()> {
trace!("comm->host {:?}", reply); trace!("comm->host {:?}", reply);
reply.write_to(stream) reply.write_to(stream)
}
fn kern_send<'a>(waiter: Waiter, request: kern::Message<'a>) -> io::Result<()> {
match &request {
&kern::LoadRequest(_) => trace!("comm->kern LoadRequest(...)"),
_ => trace!("comm->kern {:?}", request)
} }
request.send_and_wait(waiter)
}
match try!(read_request(stream)) { fn kern_recv<R, F>(waiter: Waiter, f: F) -> io::Result<R>
Request::Ident => where F: FnOnce(kern::Message) -> io::Result<R> {
write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))), kern::Message::wait_and_receive(waiter, |reply| {
trace!("comm<-kern {:?}", reply);
f(reply)
})
}
fn kern_acknowledge() -> io::Result<()> {
kern::Message::acknowledge();
Ok(())
}
fn comm_handle(waiter: Waiter,
stream: &mut TcpStream,
logger: &BufferLogger,
session: &mut Session) -> io::Result<()> {
match try!(host_read(stream)) {
host::Request::Ident =>
host_write(stream, host::Reply::Ident(::board::ident(&mut [0; 64]))),
// artiq_corelog // artiq_corelog
Request::Log => { host::Request::Log => {
// Logging the packet with the log is inadvisable // Logging the packet with the log is inadvisable
trace!("comm->host Log(...)"); trace!("comm->host Log(...)");
logger.extract(move |log| { logger.extract(move |log| {
Reply::Log(log).write_to(stream) host::Reply::Log(log).write_to(stream)
}) })
} }
Request::LogClear => { host::Request::LogClear => {
logger.clear(); logger.clear();
write_reply(stream, Reply::Log("")) host_write(stream, host::Reply::Log(""))
} }
// artiq_coreconfig // artiq_coreconfig
Request::FlashRead { ref key } => { host::Request::FlashRead { ref key } => {
let value = config::read_to_end(key); let value = config::read_to_end(key);
write_reply(stream, Reply::FlashRead(&value)) host_write(stream, host::Reply::FlashRead(&value))
} }
Request::FlashWrite { ref key, ref value } => { host::Request::FlashWrite { ref key, ref value } => {
match config::write(key, value) { match config::write(key, value) {
Ok(_) => write_reply(stream, Reply::FlashOk), Ok(_) => host_write(stream, host::Reply::FlashOk),
Err(_) => write_reply(stream, Reply::FlashError) Err(_) => host_write(stream, host::Reply::FlashError)
} }
} }
Request::FlashRemove { ref key } => { host::Request::FlashRemove { ref key } => {
config::remove(key); config::remove(key);
write_reply(stream, Reply::FlashOk) host_write(stream, host::Reply::FlashOk)
} }
Request::FlashErase => { host::Request::FlashErase => {
config::erase(); config::erase();
write_reply(stream, Reply::FlashOk) host_write(stream, host::Reply::FlashOk)
} }
// artiq_run/artiq_master // artiq_run/artiq_master
Request::SwitchClock(clk) => { host::Request::SwitchClock(clk) => {
if session.running() { if session.running() {
error!("attempted to switch RTIO clock while kernel was running"); error!("attempted to switch RTIO clock while a kernel was running");
write_reply(stream, Reply::ClockSwitchFailed) return host_write(stream, host::Reply::ClockSwitchFailed)
}
if rtio_crg::switch_clock(clk) {
host_write(stream, host::Reply::ClockSwitchCompleted)
} else { } else {
if rtio_crg::switch_clock(clk) { host_write(stream, host::Reply::ClockSwitchFailed)
write_reply(stream, Reply::ClockSwitchCompleted)
} else {
write_reply(stream, Reply::ClockSwitchFailed)
}
} }
} }
_ => unreachable!() host::Request::LoadLibrary(library) => {
if session.running() {
error!("attempted to load a new kernel while a kernel was running");
return host_write(stream, host::Reply::LoadFailed)
}
unsafe { kernel::start() }
try!(kern_send(waiter, kern::LoadRequest(&library)));
kern_recv(waiter, |reply| {
match reply {
kern::LoadReply { error: None } => {
session.kernel_state = KernelState::Loaded;
host_write(stream, host::Reply::LoadCompleted)
}
kern::LoadReply { error: Some(cause) } => {
error!("cannot load kernel: {}", cause);
host_write(stream, host::Reply::LoadFailed)
}
other => unexpected!("unexpected reply from kernel CPU: {:?}", other)
}
})
}
host::Request::RunKernel => {
if session.kernel_state != KernelState::Loaded {
error!("attempted to run a kernel while not in Loaded state");
return host_write(stream, host::Reply::KernelStartupFailed)
}
session.kernel_state = KernelState::Running;
kern_acknowledge()
}
request => unexpected!("unexpected {:?}", request)
} }
} }
fn handle_requests(stream: &mut TcpStream, fn kern_handle(waiter: Waiter,
logger: &BufferLogger) -> io::Result<()> { stream: &mut TcpStream,
session: &mut Session) -> io::Result<()> {
kern::Message::wait_and_receive(waiter, |request| {
match (&request, session.kernel_state) {
(&kern::LoadReply { .. }, KernelState::Loaded) |
(&kern::RpcRecvRequest { .. }, KernelState::RpcWait) => {
// We're standing by; ignore the message.
return Ok(())
}
(_, KernelState::Running) => (),
_ => {
unexpected!("unexpected request {:?} from kernel CPU in {:?} state",
request, session.kernel_state)
}
}
trace!("comm<-kern {:?}", request);
match request {
kern::Log(log) => {
info!(target: "kernel", "{}", log);
kern_acknowledge()
}
kern::NowInitRequest =>
kern_send(waiter, kern::NowInitReply(session.now)),
kern::NowSave(now) => {
session.now = now;
kern_acknowledge()
}
request => unexpected!("unexpected {:?}", request)
}
})
}
fn handle(waiter: Waiter,
stream: &mut TcpStream,
logger: &BufferLogger) -> io::Result<()> {
try!(check_magic(stream)); try!(check_magic(stream));
let mut session = Session::new(); let mut session = Session::new();
loop { loop {
try!(handle_request(stream, logger, &mut session)) if stream.readable() {
try!(comm_handle(waiter, stream, logger, &mut session))
}
if mailbox::receive() != 0 {
try!(kern_handle(waiter, stream, &mut session))
}
if session.kernel_state == KernelState::Running {
if session.watchdog_set.expired() {
try!(host_write(stream, host::Reply::WatchdogExpired));
return Err(io::Error::new(io::ErrorKind::Other, "watchdog expired"))
}
if !rtio_crg::check() {
try!(host_write(stream, host::Reply::ClockFailure));
return Err(io::Error::new(io::ErrorKind::Other, "RTIO clock failure"))
}
}
waiter.relinquish()
} }
} }
@ -159,13 +271,13 @@ pub fn handler(waiter: Waiter,
let (mut stream, addr) = listener.accept().unwrap(); let (mut stream, addr) = listener.accept().unwrap();
info!("new connection from {:?}", addr); info!("new connection from {:?}", addr);
match handle_requests(&mut stream, logger) { match handle(waiter, &mut stream, logger) {
Ok(()) => (), Ok(()) => (),
Err(err) => { Err(err) => {
if err.kind() == ErrorKind::UnexpectedEof { if err.kind() == io::ErrorKind::UnexpectedEof {
info!("connection closed"); info!("connection closed");
} else { } else {
error!("cannot handle network request: {:?}", err); error!("session aborted: {:?}", err);
} }
} }
} }

View File

@ -27,7 +27,7 @@ all: runtime.bin runtime.fbi
%.fbi: %.bin %.fbi: %.bin
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< @echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $<
runtime.elf: $(OBJECTS) libartiq_rust.a runtime.elf: $(OBJECTS) libruntime.a
$(LD) $(LDFLAGS) \ $(LD) $(LDFLAGS) \
--gc-sections \ --gc-sections \
-T $(RUNTIME_DIRECTORY)/runtime.ld \ -T $(RUNTIME_DIRECTORY)/runtime.ld \
@ -40,7 +40,7 @@ runtime.elf: $(OBJECTS) libartiq_rust.a
-L../liballoc \ -L../liballoc \
-L../liblwip \ -L../liblwip \
-Lcargo/or1k-unknown-none/debug/ \ -Lcargo/or1k-unknown-none/debug/ \
-lartiq_rust -lbase -lm -lcompiler-rt -lalloc -llwip -lruntime -lbase -lm -lcompiler-rt -lalloc -llwip
@chmod -x $@ @chmod -x $@
ksupport.elf: $(OBJECTS_KSUPPORT) ksupport.elf: $(OBJECTS_KSUPPORT)
@ -60,7 +60,7 @@ ksupport.elf: $(OBJECTS_KSUPPORT)
ksupport_data.o: ksupport.elf ksupport_data.o: ksupport.elf
$(LD) -r -b binary -o $@ $< $(LD) -r -b binary -o $@ $<
libartiq_rust.a: libruntime.a:
CARGO_TARGET_DIR="./cargo" \ CARGO_TARGET_DIR="./cargo" \
cargo rustc --verbose \ cargo rustc --verbose \
--manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \ --manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \

View File

@ -167,7 +167,7 @@ void kloader_service_essential_kmsg(void)
case MESSAGE_TYPE_LOG: { case MESSAGE_TYPE_LOG: {
struct msg_log *msg = (struct msg_log *)umsg; struct msg_log *msg = (struct msg_log *)umsg;
core_log_va(msg->fmt, msg->args); core_log("%s", msg->buf);
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
} }