Merge #229

229: Set the artifacts workflow to only run when `master` is pushed to. r=hudson-ayers a=jrvanwhy

In the current setup, every time I push any branch to my fork I get an artifacts action run, which seems very wasteful. This change makes the actions workflow only run when master is modified. Note that the workflow will still run in forks, but only when the fork's master branch is updated.

@alistair23 , is this a desirable change?

Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..744ea69
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,44 @@
+# This workflow provides automated testing. It builds and runs tests on each PR.
+
+name: ci
+on: pull_request
+
+jobs:
+  ci:
+    # Using ubuntu-latest can cause breakage when ubuntu-latest is updated to
+    # point at a new Ubuntu version. Instead, explicitly specify the version, so
+    # we can update when we need to. This *could* break if we don't update it
+    # until support for 18.04 is dropped, but it is likely we'll have a reason
+    # to update to a newer Ubuntu before then anyway.
+    runs-on: ubuntu-18.04
+
+    steps:
+      # Clones a single commit from the libtock-rs repository. The commit cloned
+      # is a merge commit between the PR's target branch and the PR's source.
+      # Note that we checkout submodules so that we can invoke Tock's CI setup
+      # scripts, but we do not recursively checkout submodules as we need Tock's
+      # makefile to set up the qemu submodule itself.
+      - name: Clone repository
+        uses: actions/checkout@v2.3.0
+        with:
+          submodules: true
+
+      # Install a Rust toolchain with the components necessary to build
+      # libtock-rs and its examples. This action doesn't seem to be able to
+      # install toolchains for multiple targets, so I add the targets later in
+      # the "Build and Test" step.
+      - name: Install Rust toolchain
+        uses: actions-rs/toolchain@v1.0.6
+        with:
+          profile: minimal
+
+      # The main test step. We let the makefile do most of the work because the
+      # makefile can be tested locally. We experimentally determined that -j2 is
+      # optimal for the Azure Standard_DS2_v2 VM, which is the VM type used by
+      # GitHub Actions at the time of this writing.
+      - name: Build and Test
+        run: |
+          cd "${GITHUB_WORKSPACE}"
+          rustup target add riscv32imc-unknown-none-elf thumbv7em-none-eabi
+          make -j2 setup
+          make -j2 test
diff --git a/core/platform/src/lib.rs b/core/platform/src/lib.rs
index 24c46f5..2e3dec6 100644
--- a/core/platform/src/lib.rs
+++ b/core/platform/src/lib.rs
@@ -6,8 +6,10 @@
 //      kernel
 //   2. The PlatformApi trait and Platform implementation.
 //   3. A system call trait so that Platform works in both real Tock apps and
-//      unit test environments.
+//      unit test environments. [DONE]
 
 mod error_code;
+mod syscalls;
 
 pub use error_code::ErrorCode;
+pub use syscalls::{MemopNoArg, MemopWithArg, Syscalls};
diff --git a/core/platform/src/syscalls.rs b/core/platform/src/syscalls.rs
new file mode 100644
index 0000000..9a78169
--- /dev/null
+++ b/core/platform/src/syscalls.rs
@@ -0,0 +1,99 @@
+//! Provides the Syscalls trait which directly represents Tock's system call
+//! APIs. Syscalls is implemented by both `libtock_runtime` which makes system
+//! calls into a real Tock kernel, and `libtock_fake` which is a fake Tock
+//! kernel.
+
+// TODO: Implement `libtock_runtime` and `libtock_fake`.
+
+/// Syscalls represents Tock's system call APIs. It is designed to be
+/// implemented as easily as possible -- its arguments and return values
+/// correspond directly to registers in the ABI. For a higher-level abstraction,
+/// see Platform.
+///
+/// By design, syscalls is designed to be zero-cost in a TBF binary and
+/// functional (but not zero-cost) in unit tests. In a TBF binary, Syscalls is
+/// implemented with the `'static` lifetime, and is a zero-sized type. Syscalls
+/// requires `Copy` in order to support defining it usefully on zero-sized
+/// types. When used in unit tests, the Syscalls implementation carries a
+/// lifetime local to that unit test.
+///
+/// With the exception of `memop`, this trait aligns closely to Tock's
+/// kernel::Driver trait.
+pub trait Syscalls<'k>: Copy {
+    /// Calls the `allow` system call.
+    ///
+    /// # Safety
+    /// `allow` is unsafe because callers must guarantee that `pointer` and
+    /// `length` refer to memory that the kernel can mutate safely. The buffer
+    /// must last for the lifetime 'k.
+    // `driver` and `minor` are `usize` because the kernel internally treats
+    // them as `usize`s. `allow`'s return value is a kernel `ReturnCode`;
+    // Platform translates the `isize` into a `ReturnCode`.
+    unsafe fn allow(self, driver: usize, minor: usize, pointer: *mut u8, length: usize) -> isize;
+
+    /// Calls the `command` system call.
+    // `driver`, `minor`, `arg1`, and `arg2` are all `usize` (rather than `u32`)
+    // because the kernel refers to them internally as `usize`s. command returns
+    // a kernel ReturnCode; Platform is responsible for translating an isize
+    // into the local ReturnCode.
+    fn command(self, driver: usize, minor: usize, arg1: usize, arg2: usize) -> isize;
+
+    /// Calls the `memop` system call with an argument. Note that memop() cannot
+    /// cause memory unsafety, although it can cause the app to fault (e.g. Brk
+    /// can move the app break below the stack, causing a fault). The isize
+    /// returned is a kernel ReturnCode.
+    // Platform performs the translation from isize into ReturnCode to keep
+    // Syscalls implementations simple.
+    fn memop_arg(self, op: MemopWithArg, arg: usize) -> isize;
+
+    /// Calls the `memop` system call with no arguments. This version is
+    /// slightly cheaper because it does not need to set the argument register.
+    // We're okay with leaking the value in the argument register because
+    // memop() is always handled by the core kernel, never by an untrusted
+    // capsule.
+    fn memop_noarg(self, op: MemopNoArg) -> isize;
+
+    /// Calls the `subscribe` system call.
+    ///
+    /// # Safety
+    /// `subscribe` is unsafe because the callback can potentially be unsafe,
+    /// and callers of `subscribe` must assert that calling the callback with
+    /// the provided `data` value is safe. The callback must last for the 'k
+    /// lifetime.
+    // Driver, minor, the callback args, and data are all represented as `usize`
+    // because that is the type the kernel uses internally to store them (e.g.
+    // as opposed to u32).
+    unsafe fn subscribe(
+        self,
+        driver: usize,
+        minor: usize,
+        callback: Option<unsafe extern "C" fn(usize, usize, usize, usize)>,
+        data: usize,
+    );
+
+    /// Puts the process to sleep until a callback becomes pending, then invokes
+    /// the callback.
+    fn yieldk(self);
+}
+
+#[non_exhaustive]
+#[repr(usize)]
+pub enum MemopWithArg {
+    Brk = 0,
+    Sbrk = 1,
+    FlashRegionStart = 8,
+    FlashRegionEnd = 9,
+    SpecifyStackTop = 10,
+    SpecifyHeapStart = 11,
+}
+
+#[non_exhaustive]
+#[repr(usize)]
+pub enum MemopNoArg {
+    MemoryStart = 2,
+    MemoryEnd = 3,
+    FlashStart = 4,
+    FlashEnd = 5,
+    GrantStart = 6,
+    FlashRegions = 7,
+}