From fd2bebb81f17bfce9621db906ef166433bfccdad Mon Sep 17 00:00:00 2001
From: Matt Harvey <mattharvey@google.com>
Date: Tue, 2 Feb 2021 14:12:08 -0800
Subject: [PATCH] Replaces processmanager with a one-app CAmkES assembly for
 the entire Kata OS

This also adds a skeleton for the DebugConsole CLI taking IO from a UART
via some Rust wrapper functions, also defined in this change
(kata-uart-client).

Change-Id: I56856c14992010483da58c45f6550c0a4c9987b0
GitOrigin-RevId: e1b2d65ed3a7f627a9f7377caa407151fc943864
---
 CMakeLists.txt                                |  27 +---
 README.md                                     |  11 ++
 apps/system/CMakeLists.txt                    |  37 +++++
 apps/system/README                            |  17 +++
 .../system/components/DebugConsole/Cargo.lock |  49 +++++++
 .../system/components/DebugConsole/Cargo.toml |   9 ++
 .../DebugConsole/DebugConsole.camkes          |   6 +
 .../kata-debug-console/Cargo.toml             |  16 +++
 .../kata-debug-console/src/run.rs             |  27 ++++
 .../DebugConsole/kata-io/Cargo.toml           |   5 +
 .../DebugConsole/kata-io/src/lib.rs           |  35 +++++
 .../DebugConsole/kata-line-reader/Cargo.toml  |   8 ++
 .../DebugConsole/kata-line-reader/src/lib.rs  |  78 +++++++++++
 .../DebugConsole/kata-shell/Cargo.toml        |   9 ++
 .../DebugConsole/kata-shell/src/lib.rs        | 127 ++++++++++++++++++
 .../DebugConsole/kata-uart-client/Cargo.toml  |   9 ++
 .../DebugConsole/kata-uart-client/src/lib.rs  |  37 +++++
 .../components/UartDriver/UartDriver.camkes   |  21 +++
 .../system/components/UartDriver/src/driver.c |  94 +++++++++++++
 apps/system/interfaces/uart.idl4              |  16 +++
 apps/system/rust.cmake                        |  53 ++++++++
 apps/system/system.camkes                     |  54 ++++++++
 easy-settings.cmake                           |   5 +
 rust.cmake                                    |  60 +++++++++
 settings.cmake                                |   1 +
 25 files changed, 785 insertions(+), 26 deletions(-)
 mode change 100644 => 120000 CMakeLists.txt
 create mode 100644 README.md
 create mode 100644 apps/system/CMakeLists.txt
 create mode 100644 apps/system/README
 create mode 100644 apps/system/components/DebugConsole/Cargo.lock
 create mode 100644 apps/system/components/DebugConsole/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/DebugConsole.camkes
 create mode 100644 apps/system/components/DebugConsole/kata-debug-console/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/kata-debug-console/src/run.rs
 create mode 100644 apps/system/components/DebugConsole/kata-io/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/kata-io/src/lib.rs
 create mode 100644 apps/system/components/DebugConsole/kata-line-reader/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/kata-line-reader/src/lib.rs
 create mode 100644 apps/system/components/DebugConsole/kata-shell/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/kata-shell/src/lib.rs
 create mode 100644 apps/system/components/DebugConsole/kata-uart-client/Cargo.toml
 create mode 100644 apps/system/components/DebugConsole/kata-uart-client/src/lib.rs
 create mode 100644 apps/system/components/UartDriver/UartDriver.camkes
 create mode 100644 apps/system/components/UartDriver/src/driver.c
 create mode 100644 apps/system/interfaces/uart.idl4
 create mode 100644 apps/system/rust.cmake
 create mode 100644 apps/system/system.camkes
 create mode 100644 easy-settings.cmake
 create mode 100644 rust.cmake
 create mode 120000 settings.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index c58cc51..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-cmake_minimum_required(VERSION 3.7.2)
-project(ProcessManager C ASM)
-
-set(project_dir "${CMAKE_CURRENT_LIST_DIR}/../../")
-file(GLOB project_modules ${project_dir}/projects/*)
-list(
-    APPEND
-        CMAKE_MODULE_PATH
-        ${project_dir}/kernel
-        ${project_dir}/tools/seL4/cmake-tool/helpers/
-        ${project_dir}/tools/seL4/elfloader-tool/
-        ${project_modules}
-)
-set(POLLY_DIR ${project_dir}/tools/polly CACHE INTERNAL "")
-
-add_executable(ProcessManager src/main.c)
-
-target_link_libraries(ProcessManager
-  sel4runtime sel4
-  muslc utils sel4muslcsys sel4platsupport sel4utils sel4debug)
-
-include(rootserver)
-DeclareRootserver(ProcessManager)
-
-include(simulation)
-GenerateSimulateScript()
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 120000
index 0000000..fba0c5b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1 @@
+../camkes/CMakeLists.txt
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5729571
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# Kata OS
+
+This is a CAmkES project that assembles the entire Kata OS. It exists outside
+the seL4 source trees, since it contains code not intended to go to upstream
+seL4.
+
+This uses the [standard CAmkES build system](https://docs.sel4.systems/projects/camkes/manual.html#running-a-simple-example)
+by symlinking CMakeLists.txt. It also symlinks settings.cmake, and so retains
+the notion of "apps," which enables the build system to switch which assembly
+it builds using the CAMKES\_APP CMake cache value. Kata OS just has one app,
+*system*.
diff --git a/apps/system/CMakeLists.txt b/apps/system/CMakeLists.txt
new file mode 100644
index 0000000..423db69
--- /dev/null
+++ b/apps/system/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# Copyright 2018, Data61
+# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+# ABN 41 687 119 230.
+#
+# This software may be distributed and modified according to the terms of
+# the BSD 2-Clause license. Note that NO WARRANTY is provided.
+# See "LICENSE_BSD2.txt" for details.
+#
+# @TAG(DATA61_BSD)
+#
+
+cmake_minimum_required(VERSION 3.7.2)
+
+project(system)
+
+include(${CMAKE_CURRENT_LIST_DIR}/rust.cmake)
+
+RustAddLibrary(
+  kata_debug_console
+  SOURCE_DIR
+  ${CMAKE_CURRENT_LIST_DIR}/components/DebugConsole
+  TARGET
+  "riscv32imc-unknown-none-elf"
+  LIB_FILENAME
+  libkata_debug_console.a
+)
+
+DeclareCAmkESComponent(DebugConsole
+  LIBS kata_debug_console
+)
+
+DeclareCAmkESComponent(UartDriver
+  SOURCES components/UartDriver/src/driver.c
+)
+
+DeclareCAmkESRootserver(system.camkes)
diff --git a/apps/system/README b/apps/system/README
new file mode 100644
index 0000000..d832c7a
--- /dev/null
+++ b/apps/system/README
@@ -0,0 +1,17 @@
+<!--
+     Copyright 2017, Data61
+     Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+     ABN 41 687 119 230.
+
+     This software may be distributed and modified according to the terms of
+     the BSD 2-Clause license. Note that NO WARRANTY is provided.
+     See "LICENSE_BSD2.txt" for details.
+
+     @TAG(DATA61_BSD)
+-->
+#
+
+UART driver for KZM.
+
+This is an example of accessing hardware device memory. The device memory
+address may vary.
diff --git a/apps/system/components/DebugConsole/Cargo.lock b/apps/system/components/DebugConsole/Cargo.lock
new file mode 100644
index 0000000..a631062
--- /dev/null
+++ b/apps/system/components/DebugConsole/Cargo.lock
@@ -0,0 +1,49 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "cty"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3"
+
+[[package]]
+name = "kata-debug-console"
+version = "0.1.0"
+dependencies = [
+ "kata-shell",
+ "kata-uart-client",
+ "panic-halt",
+]
+
+[[package]]
+name = "kata-io"
+version = "0.1.0"
+
+[[package]]
+name = "kata-line-reader"
+version = "0.1.0"
+dependencies = [
+ "kata-io",
+]
+
+[[package]]
+name = "kata-shell"
+version = "0.1.0"
+dependencies = [
+ "kata-io",
+ "kata-line-reader",
+]
+
+[[package]]
+name = "kata-uart-client"
+version = "0.1.0"
+dependencies = [
+ "cty",
+ "kata-io",
+]
+
+[[package]]
+name = "panic-halt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
diff --git a/apps/system/components/DebugConsole/Cargo.toml b/apps/system/components/DebugConsole/Cargo.toml
new file mode 100644
index 0000000..525f272
--- /dev/null
+++ b/apps/system/components/DebugConsole/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+members = [
+    "kata-debug-console",
+    "kata-io",
+    "kata-line-reader",
+    "kata-shell",
+    "kata-uart-client",
+]
diff --git a/apps/system/components/DebugConsole/DebugConsole.camkes b/apps/system/components/DebugConsole/DebugConsole.camkes
new file mode 100644
index 0000000..81aa874
--- /dev/null
+++ b/apps/system/components/DebugConsole/DebugConsole.camkes
@@ -0,0 +1,6 @@
+component DebugConsole {
+  control;
+  uses uart_inf uart;
+  dataport Buf tx_dataport;
+  dataport Buf rx_dataport;
+}
diff --git a/apps/system/components/DebugConsole/kata-debug-console/Cargo.toml b/apps/system/components/DebugConsole/kata-debug-console/Cargo.toml
new file mode 100644
index 0000000..bfd510a
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-debug-console/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "kata-debug-console"
+version = "0.1.0"
+authors = ["Matt Harvey <mattharvey@google.com>"]
+edition = "2018"
+description = "Kata OS DebugConsole"
+
+[dependencies]
+panic-halt = "0.2.0"
+kata-shell = { path = "../kata-shell" }
+kata-uart-client = { path = "../kata-uart-client" }
+
+[lib]
+name = "kata_debug_console"
+path = "src/run.rs"
+crate-type = ["staticlib"]
diff --git a/apps/system/components/DebugConsole/kata-debug-console/src/run.rs b/apps/system/components/DebugConsole/kata-debug-console/src/run.rs
new file mode 100644
index 0000000..4892039
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-debug-console/src/run.rs
@@ -0,0 +1,27 @@
+//! Kata OS command line interface
+
+// This brief bootstrap of Rust-in-Kata prototypes a minimal modular design
+// for the DebugConsole CLI use case.
+//
+// * kata_io Read/Write interface (or move to std::, but that requires alloc)
+// * kata_uart_client implementation of the kata_io interface
+// * kata_line_reader
+// * kata_shell
+// * kata_debug_console main entry point fn run()
+
+// std:: requires at least an allocator, which Kata does not have yet. For now
+// the CLI will be implemented with only core::.
+#![no_std]
+
+extern crate panic_halt;
+
+use kata_shell;
+use kata_uart_client;
+
+/// Entry point for DebugConsole. Runs the shell with UART IO.
+#[no_mangle]
+pub extern "C" fn run() -> ! {
+    let mut tx = kata_uart_client::Tx {};
+    let mut rx = kata_uart_client::Rx {};
+    kata_shell::repl(&mut tx, &mut rx);
+}
diff --git a/apps/system/components/DebugConsole/kata-io/Cargo.toml b/apps/system/components/DebugConsole/kata-io/Cargo.toml
new file mode 100644
index 0000000..eaccbe5
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-io/Cargo.toml
@@ -0,0 +1,5 @@
+[package]
+name = "kata-io"
+version = "0.1.0"
+authors = ["Matt Harvey <mattharvey@google.com>"]
+edition = "2018"
diff --git a/apps/system/components/DebugConsole/kata-io/src/lib.rs b/apps/system/components/DebugConsole/kata-io/src/lib.rs
new file mode 100644
index 0000000..5eb7877
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-io/src/lib.rs
@@ -0,0 +1,35 @@
+#![no_std]
+
+pub struct Error;
+
+/// Interface for the CLI to consume bytes.
+pub trait Read {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error>;
+}
+
+/// Interface for the CLI to emit bytes.
+pub trait Write {
+    fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
+}
+
+/// Adapter for writing core::fmt formatted strings.
+impl core::fmt::Write for dyn Write + '_ {
+    /// Writes the bytes of a &str to the underlying writer.
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        match self.write(s.as_bytes()) {
+            Ok(_) => Ok(()),
+            Err(_) => Err(core::fmt::Error),
+        }
+    }
+}
+
+impl dyn Read + '_ {
+    pub fn get_u8(&mut self) -> Result<u8, Error> {
+        let mut buf: [u8; 1] = [0u8];
+        let n_read = self.read(&mut buf)?;
+        match n_read {
+            1usize => Ok(buf[0]),
+            _ => Err(Error),
+        }
+    }
+}
diff --git a/apps/system/components/DebugConsole/kata-line-reader/Cargo.toml b/apps/system/components/DebugConsole/kata-line-reader/Cargo.toml
new file mode 100644
index 0000000..feb53f4
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-line-reader/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "kata-line-reader"
+version = "0.1.0"
+authors = ["Matt Harvey <mattharvey@google.com>"]
+edition = "2018"
+
+[dependencies]
+kata-io = { path = "../kata-io" }
diff --git a/apps/system/components/DebugConsole/kata-line-reader/src/lib.rs b/apps/system/components/DebugConsole/kata-line-reader/src/lib.rs
new file mode 100644
index 0000000..ccecae5
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-line-reader/src/lib.rs
@@ -0,0 +1,78 @@
+#![no_std]
+
+use core::fmt;
+
+use kata_io as io;
+
+const LINE_MAX: usize = 128;
+
+pub enum LineReadError {
+    IO(io::Error),
+    Overflow,
+    Encoding(core::str::Utf8Error),
+}
+
+impl From<io::Error> for LineReadError {
+    fn from(err: io::Error) -> LineReadError {
+        LineReadError::IO(err)
+    }
+}
+
+impl From<core::str::Utf8Error> for LineReadError {
+    fn from(err: core::str::Utf8Error) -> LineReadError {
+        LineReadError::Encoding(err)
+    }
+}
+
+impl fmt::Display for LineReadError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            LineReadError::IO(_) => write!(f, "IO error"),
+            LineReadError::Overflow => write!(f, "line too long"),
+            LineReadError::Encoding(_) => write!(f, "bad character encoding"),
+        }
+    }
+}
+
+pub struct LineReader {
+    // Owned by LineReader to facilitate static allocation.
+    buf: [u8; LINE_MAX],
+}
+
+impl LineReader {
+    pub fn new() -> LineReader {
+        LineReader {
+            buf: [0u8; LINE_MAX],
+        }
+    }
+
+    pub fn read_line(
+        &mut self,
+        output: &mut dyn io::Write,
+        input: &mut dyn io::Read,
+    ) -> Result<&str, LineReadError> {
+        const DEL: u8 = 127u8;
+        const BACKSPACE: u8 = 8u8;
+        let mut len = 0;
+        while len < self.buf.len() {
+            let mut c = input.get_u8()?;
+            while c == DEL || c == BACKSPACE {
+                if len > 0 {
+                    output.write(&[BACKSPACE, b' ', BACKSPACE])?;
+                    len -= 1;
+                }
+                c = input.get_u8()?;
+            }
+            if c == b'\r' || c == b'\n' {
+                if len > 0 {
+                    output.write(&[b'\n'])?;
+                }
+                return Ok(core::str::from_utf8(&self.buf[0..len])?);
+            }
+            self.buf[len] = c;
+            len += 1;
+            output.write(&[c])?;
+        }
+        Err(LineReadError::Overflow)
+    }
+}
diff --git a/apps/system/components/DebugConsole/kata-shell/Cargo.toml b/apps/system/components/DebugConsole/kata-shell/Cargo.toml
new file mode 100644
index 0000000..ff7cbb4
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-shell/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "kata-shell"
+version = "0.1.0"
+authors = ["Matt Harvey <mattharvey@google.com>"]
+edition = "2018"
+
+[dependencies]
+kata-io = { path = "../kata-io" }
+kata-line-reader = { path = "../kata-line-reader" }
diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs
new file mode 100644
index 0000000..1f694a4
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs
@@ -0,0 +1,127 @@
+#![no_std]
+
+use core::fmt;
+use core::fmt::Write;
+
+use kata_io as io;
+use kata_line_reader::LineReader;
+
+/// Error type indicating why a command line is not runnable.
+enum CommandError {
+    UnknownCommand,
+    BadArgs,
+    Formatter(fmt::Error),
+}
+
+impl fmt::Display for CommandError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            CommandError::UnknownCommand => write!(f, "unknown command"),
+            CommandError::BadArgs => write!(f, "invalid arguments"),
+            CommandError::Formatter(e) => write!(f, "{}", e),
+        }
+    }
+}
+
+impl From<core::num::ParseIntError> for CommandError {
+    fn from(_err: core::num::ParseIntError) -> CommandError {
+        CommandError::BadArgs
+    }
+}
+
+impl From<core::num::ParseFloatError> for CommandError {
+    fn from(_err: core::num::ParseFloatError) -> CommandError {
+        CommandError::BadArgs
+    }
+}
+
+impl From<fmt::Error> for CommandError {
+    fn from(err: fmt::Error) -> CommandError {
+        CommandError::Formatter(err)
+    }
+}
+
+/// Read-eval-print loop for the DebugConsole command line interface.
+pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! {
+    let mut line_reader = LineReader::new();
+    loop {
+        // The PROMPT is the Hiragana representation of the word "kata."
+        const PROMPT: &str = "かた ";
+        let _ = output.write_str(PROMPT);
+        match line_reader.read_line(output, input) {
+            Ok(cmdline) => dispatch_command(cmdline, output),
+            Err(e) => {
+                let _ = write!(output, "\n{}\n", e);
+            }
+        }
+    }
+}
+
+/// Runs a command line.
+///
+/// The line is split on whitespace. The first token is the command; the
+/// remaining tokens are the arguments.
+fn dispatch_command(cmdline: &str, output: &mut dyn io::Write) {
+    let mut args = cmdline.split_ascii_whitespace();
+    match args.nth(0) {
+        Some(command) => {
+            // Statically binds command names to implementations fns, which are
+            // defined below.
+            //
+            // Since even the binding is static, it is fine for each command
+            // implementation to use its own preferred signature.
+            let result = match command {
+                "echo" => echo_command(cmdline, output),
+                "add" => add_command(&mut args, output),
+                "clear" => clear_command(output),
+                _ => Err(CommandError::UnknownCommand),
+            };
+            if let Err(e) = result {
+                let _ = write!(output, "{}\n", e);
+            };
+        }
+        None => {
+            let _ = output.write_str("\n");
+        }
+    };
+}
+
+/// Implements an "echo" command which writes its arguments to output.
+fn echo_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
+    const COMMAND_LENGTH: usize = 5; // "echo "
+    if cmdline.len() < COMMAND_LENGTH {
+        Ok(())
+    } else {
+        Ok(write!(
+            output,
+            "{}\n",
+            &cmdline[COMMAND_LENGTH..cmdline.len()]
+        )?)
+    }
+}
+
+/// Implements a binary float addition command.
+///
+/// This is a toy to demonstrate that the CLI can operate on some very basic
+/// dynamic input and that the Rust runtime provides floating point arithmetic
+/// on integer-only hardware. It is also a prototype example of "command taking
+/// arguments." It should be removed once actually useful system control
+/// commands are implemented and done cribbing from it.
+fn add_command(
+    args: &mut dyn Iterator<Item = &str>,
+    output: &mut dyn io::Write,
+) -> Result<(), CommandError> {
+    if let Some(x_str) = args.nth(0) {
+        if let Some(y_str) = args.nth(0) {
+            let x = x_str.parse::<f32>()?;
+            let y = y_str.parse::<f32>()?;
+            return Ok(write!(output, "{}\n", x + y)?);
+        }
+    }
+    Err(CommandError::BadArgs)
+}
+
+/// Implements a command that outputs the ANSI "clear console" sequence.
+fn clear_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
+    Ok(output.write_str("\x1b\x63")?)
+}
diff --git a/apps/system/components/DebugConsole/kata-uart-client/Cargo.toml b/apps/system/components/DebugConsole/kata-uart-client/Cargo.toml
new file mode 100644
index 0000000..63d7ca1
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-uart-client/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "kata-uart-client"
+version = "0.1.0"
+authors = ["Matt Harvey <mattharvey@google.com>"]
+edition = "2018"
+
+[dependencies]
+cty = "0.2.1"
+kata-io = { path = "../kata-io" }
diff --git a/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs b/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs
new file mode 100644
index 0000000..dcf8cd0
--- /dev/null
+++ b/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs
@@ -0,0 +1,37 @@
+#![no_std]
+
+use kata_io as io;
+
+// C interface to external UART driver.
+extern "C" {
+    static rx_dataport: *mut cty::c_char;
+    static tx_dataport: *mut cty::c_char;
+    fn uart_rx(n: cty::size_t);
+    fn uart_tx(n: cty::size_t);
+}
+
+pub struct Rx {}
+
+impl io::Read for Rx {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
+        unsafe {
+            uart_rx(buf.len());
+            let port = core::slice::from_raw_parts(rx_dataport, buf.len());
+            buf.copy_from_slice(&port);
+        }
+        Ok(buf.len())
+    }
+}
+
+pub struct Tx {}
+
+impl io::Write for Tx {
+    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
+        unsafe {
+            let port = core::slice::from_raw_parts_mut(tx_dataport, buf.len());
+            port.copy_from_slice(buf);
+            uart_tx(buf.len());
+        }
+        Ok(buf.len())
+    }
+}
diff --git a/apps/system/components/UartDriver/UartDriver.camkes b/apps/system/components/UartDriver/UartDriver.camkes
new file mode 100644
index 0000000..29b7d6a
--- /dev/null
+++ b/apps/system/components/UartDriver/UartDriver.camkes
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017, Data61
+ * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+ * ABN 41 687 119 230.
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DATA61_BSD)
+ */
+
+component UartDriver {
+	dataport Buf mem;
+  dataport Buf tx_dataport;
+  dataport Buf rx_dataport;
+
+  // consumes Interrupt interrupt;
+
+	provides uart_inf uart;
+}
diff --git a/apps/system/components/UartDriver/src/driver.c b/apps/system/components/UartDriver/src/driver.c
new file mode 100644
index 0000000..046360a
--- /dev/null
+++ b/apps/system/components/UartDriver/src/driver.c
@@ -0,0 +1,94 @@
+// 16550a UART driver
+//
+// Pared down from the xv6 RISC-V source (MIT license).
+// https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/uart.c
+
+#include <camkes.h>
+
+#define UART0       (unsigned int)mem
+
+// the UART control registers are memory-mapped
+// at address UART0. this macro returns the
+// address of one of the registers.
+#define Reg(reg) ((volatile unsigned char *)(UART0 + reg))
+
+// the UART control registers.
+// some have different meanings for
+// read vs write.
+// see http://byterunner.com/16550.html
+#define RHR 0                 // receive holding register (for input bytes)
+#define THR 0                 // transmit holding register (for output bytes)
+#define IER 1                 // interrupt enable register
+#define IER_RX_ENABLE (1<<0)
+#define IER_TX_ENABLE (1<<1)
+#define FCR 2                 // FIFO control register
+#define FCR_FIFO_ENABLE (1<<0)
+#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
+#define ISR 2                 // interrupt status register
+#define LCR 3                 // line control register
+#define LCR_EIGHT_BITS (3<<0)
+#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
+#define LSR 5                 // line status register
+#define LSR_RX_READY (1<<0)   // input is waiting to be read from RHR
+#define LSR_TX_IDLE (1<<5)    // THR can accept another character to send
+
+#define ReadReg(reg) (*(Reg(reg)))
+#define WriteReg(reg, v) (*(Reg(reg)) = (v))
+
+void uart__init()
+{
+  // disable interrupts (UART from generating, not hart from dispatching)
+  WriteReg(IER, 0x00);
+
+  // special mode to set baud rate.
+  WriteReg(LCR, LCR_BAUD_LATCH);
+
+  // LSB for baud rate of 38.4K.
+  WriteReg(0, 0x03);
+
+  // MSB for baud rate of 38.4K.
+  WriteReg(1, 0x00);
+
+  // leave set-baud mode,
+  // and set word length to 8 bits, no parity.
+  WriteReg(LCR, LCR_EIGHT_BITS);
+
+  // reset and enable FIFOs.
+  WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
+
+  // TODO (mattharvey): seL4 is not configured to dispatch UART interrupts to
+  // this driver yet. Until that time, this driver spins to wait. The proper
+  // thing will be to make a Rust embedded_hal implementation for Sparrow.
+  //
+  // enable transmit and receive interrupts.
+  // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
+}
+
+static int uart_received()
+{
+  return ReadReg(LSR) & LSR_RX_READY;
+}
+
+static int is_transmit_empty() {
+  return ReadReg(LSR) & LSR_TX_IDLE;
+}
+
+void uart_rx(size_t n) {
+  char *c = (char*)rx_dataport;
+  // TODO(mattharvey): Error return value for n > PAGE_SIZE
+  for (size_t i = 0; i < n && i < PAGE_SIZE; ++i) {
+    while (!uart_received());
+    *c = ReadReg(RHR);
+    ++c;
+  }
+}
+
+void uart_tx(size_t n) {
+  char *c = (char*)tx_dataport;
+  // TODO(mattharvey): Error return value for n > PAGE_SIZE
+  for (size_t i = 0; i < n && i < PAGE_SIZE; ++i) {
+    while(!is_transmit_empty());
+    WriteReg(THR, *c);
+    ++c;
+  }
+}
diff --git a/apps/system/interfaces/uart.idl4 b/apps/system/interfaces/uart.idl4
new file mode 100644
index 0000000..acfa770
--- /dev/null
+++ b/apps/system/interfaces/uart.idl4
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2017, Data61
+ * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+ * ABN 41 687 119 230.
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DATA61_BSD)
+ */
+
+procedure uart_inf {
+  void rx(in size_t n);
+  void tx(in size_t n);
+};
diff --git a/apps/system/rust.cmake b/apps/system/rust.cmake
new file mode 100644
index 0000000..26f218b
--- /dev/null
+++ b/apps/system/rust.cmake
@@ -0,0 +1,53 @@
+#
+# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+cmake_minimum_required(VERSION 3.8.2)
+include_guard(GLOBAL)
+
+# add_library but for rust libraries. Invokes cargo in the SOURCE_DIR that is provided,
+# all build output is placed in BUILD_DIR or CMAKE_CURRENT_BINARY_DIR if BUILD_DIR isn't provided.
+# lib_name: Name of library that is created
+# SOURCE_DIR: source directory of cargo project
+# BUILD_DIR: directory for cargo build output
+# TARGET: custom target to use. See in ../rust_targets/ for list of available targets.
+# LIB_FILENAME: filename of library created by cargo
+# DEPENDS: And target or file dependencies that need to be run before cargo
+function(RustAddLibrary lib_name)
+    cmake_parse_arguments(PARSE_ARGV 1 RUST "" "SOURCE_DIR;BUILD_DIR;TARGET;LIB_FILENAME" "DEPENDS")
+    if(NOT "${RUST_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Unknown arguments to RustAddLibrary ${RUST_UNPARSED_ARGUMENTS}")
+    endif()
+    if("${RUST_SOURCE_DIR}" STREQUAL "")
+        message(FATAL_ERROR "SOURCE_DIR must be set for RustAddLibrary")
+    endif()
+    if("${RUST_LIB_FILENAME}" STREQUAL "")
+        message(FATAL_ERROR "LIB_FILENAME must be set for RustAddLibrary")
+    endif()
+    if("${RUST_BUILD_DIR}" STREQUAL "")
+        set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR})
+    endif()
+
+    add_custom_target(
+        ${libmain}_custom
+        BYPRODUCTS
+        ${RUST_BUILD_DIR}/${RUST_LIB_FILENAME}
+        ${USES_TERMINAL_DEBUG}
+        DEPENDS ${RUST_DEPENDS}
+        WORKING_DIRECTORY ${RUST_SOURCE_DIR}
+        COMMAND
+            ${CMAKE_COMMAND} -E env cargo +nightly build
+            --target riscv32imc-unknown-none-elf
+            --target-dir ${RUST_BUILD_DIR} -Z unstable-options
+            --out-dir ${RUST_BUILD_DIR}
+    )
+
+    add_library(${lib_name} STATIC IMPORTED GLOBAL)
+    set_property(
+        TARGET ${lib_name}
+        PROPERTY IMPORTED_LOCATION "${RUST_BUILD_DIR}/${RUST_LIB_FILENAME}"
+    )
+    add_dependencies(${lib_name} ${libmain}_custom)
+endfunction()
diff --git a/apps/system/system.camkes b/apps/system/system.camkes
new file mode 100644
index 0000000..0acce4d
--- /dev/null
+++ b/apps/system/system.camkes
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017, Data61
+ * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+ * ABN 41 687 119 230.
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DATA61_BSD)
+ */
+
+import <std_connector.camkes>;
+
+import "interfaces/uart.idl4";
+import "components/UartDriver/UartDriver.camkes";
+import "components/DebugConsole/DebugConsole.camkes";
+
+component UART {
+    hardware;
+    dataport Buf mem;
+    // TODO(mattharvey): Make receives wait on interrupt.
+    // emits Interrupt interrupt;
+}
+
+assembly {
+    composition {
+        component UART uart;
+        component UartDriver drv;
+        component DebugConsole debug_console;
+
+        connection seL4HardwareMMIO uart_mem(from drv.mem, to uart.mem);
+        // TODO(mattharvey): Make receives wait on interrupt.
+        // connection seL4HardwareInterrupt uart_interrupt(
+        //     from uart.interrupt, to drv.interrupt);
+        connection seL4RPCCall uart_inf(from debug_console.uart, to drv.uart);
+
+        connection seL4SharedData tx_channel(
+            from debug_console.tx_dataport, to drv.tx_dataport);
+        connection seL4SharedData rx_channel(
+            from debug_console.rx_dataport, to drv.rx_dataport);
+    }
+
+    configuration {
+        uart.mem_paddr = 0x10000000;
+        uart.mem_size = 0x1000;
+        // seL4 claims 10 is bigger than irqMax if this is uncommented.
+        // uart.interrupt_irq_number = 10;
+
+        random.ID = 1;
+
+        uart.integrity_label = "drv";
+    }
+}
diff --git a/easy-settings.cmake b/easy-settings.cmake
new file mode 100644
index 0000000..a1cb6f3
--- /dev/null
+++ b/easy-settings.cmake
@@ -0,0 +1,5 @@
+set(CAMKES_APP "system" CACHE STRING "The one and only CAmkES application in this project")
+set(SIMULATION ON CACHE BOOL "Whether to build simulate script")
+set(RELEASE OFF CACHE BOOL "Performance optimized build")
+set(PLATFORM "spike" CACHE STRING "The one and only seL4 platform for Sparrow")
+set(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform")
diff --git a/rust.cmake b/rust.cmake
new file mode 100644
index 0000000..0332c72
--- /dev/null
+++ b/rust.cmake
@@ -0,0 +1,60 @@
+# Fork of tools/seL4/cmake-tool/helpers/rust.cmake.
+#
+# The differences are that a) xargo has been replaced with cargo +nightly, since
+# xargo was upstreamed into Rust proper after seL4 created rust.cmake, and b)
+# the Rust target is hard-coded to riscv32imc-unknown-none-elf to make this
+# file easier to read.
+
+#
+# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+cmake_minimum_required(VERSION 3.8.2)
+include_guard(GLOBAL)
+
+# add_library but for rust libraries. Invokes cargo in the SOURCE_DIR that is provided,
+# all build output is placed in BUILD_DIR or CMAKE_CURRENT_BINARY_DIR if BUILD_DIR isn't provided.
+# lib_name: Name of library that is created
+# SOURCE_DIR: source directory of cargo project
+# BUILD_DIR: directory for cargo build output
+# TARGET: custom target to use. See in ../rust_targets/ for list of available targets.
+# LIB_FILENAME: filename of library created by cargo
+# DEPENDS: And target or file dependencies that need to be run before cargo
+function(RustAddLibrary lib_name)
+    cmake_parse_arguments(PARSE_ARGV 1 RUST "" "SOURCE_DIR;BUILD_DIR;TARGET;LIB_FILENAME" "DEPENDS")
+    if(NOT "${RUST_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Unknown arguments to RustAddLibrary ${RUST_UNPARSED_ARGUMENTS}")
+    endif()
+    if("${RUST_SOURCE_DIR}" STREQUAL "")
+        message(FATAL_ERROR "SOURCE_DIR must be set for RustAddLibrary")
+    endif()
+    if("${RUST_LIB_FILENAME}" STREQUAL "")
+        message(FATAL_ERROR "LIB_FILENAME must be set for RustAddLibrary")
+    endif()
+    if("${RUST_BUILD_DIR}" STREQUAL "")
+        set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR})
+    endif()
+
+    add_custom_target(
+        ${libmain}_custom
+        BYPRODUCTS
+        ${RUST_BUILD_DIR}/${RUST_LIB_FILENAME}
+        ${USES_TERMINAL_DEBUG}
+        DEPENDS ${RUST_DEPENDS}
+        WORKING_DIRECTORY ${RUST_SOURCE_DIR}
+        COMMAND
+            ${CMAKE_COMMAND} -E env cargo +nightly build
+            --target riscv32imc-unknown-none-elf
+            --target-dir ${RUST_BUILD_DIR} -Z unstable-options
+            --out-dir ${RUST_BUILD_DIR}
+    )
+
+    add_library(${lib_name} STATIC IMPORTED GLOBAL)
+    set_property(
+        TARGET ${lib_name}
+        PROPERTY IMPORTED_LOCATION "${RUST_BUILD_DIR}/${RUST_LIB_FILENAME}"
+    )
+    add_dependencies(${lib_name} ${libmain}_custom)
+endfunction()
diff --git a/settings.cmake b/settings.cmake
new file mode 120000
index 0000000..776b137
--- /dev/null
+++ b/settings.cmake
@@ -0,0 +1 @@
+../camkes/settings.cmake
\ No newline at end of file