Merge #195

195: README: Update to better describe Tock kernel version support r=alistair23 a=alistair23

Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Co-authored-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..45ddcde
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tock"]
+	path = tock
+	url = https://github.com/tock/tock.git
diff --git a/.travis.yml b/.travis.yml
index 66cee0b..52e5d38 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,36 +13,8 @@
 
 cache: cargo
 
-# Once Travis supports a version of Ubuntu that inlcudes QEMU 5.1+
-#    we can apt install QEMU for RISC-V.
-# Until then we need to build it ourselves
-before_install:
-  #   - sudo apt-get -y install qemu-system-misc
-  # addons:
-  #   apt:
-  #     update: true
-  - git clone https://github.com/alistair23/qemu.git -b riscv-tock.next
-  - pushd qemu
-  - ./configure --target-list=riscv32-softmmu
-  - make -j8
-  - sudo ln -s $PWD/riscv32-softmmu/qemu-system-riscv32 /usr/bin/
-  - popd
-
 install:
-  - make setup
-  # Build Tock, it needs to be outside of the libtock-rs source
-  - pushd ../
-  - git clone https://github.com/tock/tock.git
-  - cd tock/boards/hifive1
-  # Use a known working version of Tock
-  - git checkout 152189fe077aea955332ee3c28ccbee519d8b072
-  - make
-  - popd
+  - make -j8 setup
 
 script:
   - make test
-  # Run a QEMU instance of the HiFive1 app
-  - PLATFORM=hifive1 cargo rrv32imac --example libtock_test --features=alloc --features=__internal_disable_gpio_in_integration_test
-  - pushd test-runner
-  - cargo run
-  - popd
diff --git a/Cargo.toml b/Cargo.toml
index ee68117..b8e0bec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -58,8 +58,10 @@
 debug = true
 
 [workspace]
+exclude = [ "tock" ]
 members = [
     "codegen",
     "core",
-    "test-runner"
+    "test-runner",
+    "tools/print-sizes",
 ]
diff --git a/Makefile b/Makefile
index f1f6dd5..d205cc7 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@
 	@echo " - opentitan"
 	@echo " - hifive1"
 	@echo " - nrf52"
+	@echo " - apollo3"
 	@echo
 	@echo "Run 'make setup' to setup Rust to build libtock-rs."
 	@echo "Run 'make <board>' to build libtock-rs for that board"
@@ -21,6 +22,7 @@
 	@echo "    Set the FEATURES flag to enable features"
 	@echo "Run 'make flash-<board> EXAMPLE=<>' to flash EXAMPLE to that board"
 	@echo "Run 'make test' to test any local changes you have made"
+	@echo "Run 'make print-sizes' to print size data for the example binaries"
 
 ifdef FEATURES
 features=--features=$(FEATURES)
@@ -31,7 +33,7 @@
 endif
 
 .PHONY: setup
-setup:
+setup: setup-qemu
 	rustup target add thumbv7em-none-eabi
 	rustup target add riscv32imac-unknown-none-elf
 	rustup target add riscv32imc-unknown-none-elf
@@ -40,20 +42,45 @@
 	cargo install elf2tab --version 0.4.0
 	cargo install stack-sizes
 
+# Sets up QEMU in the tock/ directory. We use Tock's QEMU which may contain
+# patches to better support boards that Tock supports.
+.PHONY: setup-qemu
+setup-qemu:
+	$(MAKE) -C tock emulation-setup
+
+# Builds a Tock kernel for the HiFive board for use by QEMU tests.
+.PHONY: kernel-hifive
+kernel-hifive:
+	$(MAKE) -C tock/boards/hifive1 \
+		$(CURDIR)/tock/target/riscv32imac-unknown-none-elf/release/hifive1.elf
+
+# Prints out the sizes of the example binaries.
+.PHONY: print-sizes
+print-sizes: examples
+	cargo run --release -p print-sizes
+
+# Runs the libtock_test tests in QEMU on a simulated HiFive board.
+.PHONY: test-qemu-hifive
+test-qemu-hifive: kernel-hifive setup-qemu
+	PLATFORM=hifive1 cargo rrv32imac --example libtock_test --features=alloc \
+		--features=__internal_disable_gpio_in_integration_test
+	cargo run -p test-runner
+
 .PHONY: examples
 examples:
-	PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples
+	PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples -p libtock -p libtock-core
 	PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples --features=alloc
 	PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --example panic --features=custom_panic_handler,custom_alloc_error_handler
 	PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --example alloc_error --features=alloc,custom_alloc_error_handler
-	PLATFORM=opentitan cargo build --release --target=riscv32imc-unknown-none-elf --examples # Important: This is testing a platform without atomics support
+	# Important: This tests a platform without atomic instructions.
+	PLATFORM=opentitan cargo build --release --target=riscv32imc-unknown-none-elf --examples -p libtock -p libtock-core
 
 .PHONY: test
-test:
+test: examples test-qemu-hifive
 	PLATFORM=nrf52 cargo fmt --all -- --check
 	PLATFORM=nrf52 cargo clippy --workspace --all-targets
 	PLATFORM=nrf52 cargo test --workspace
-	make examples
+	echo '[ SUCCESS ] libtock-rs tests pass'
 
 .PHONY: analyse-stack-sizes
 analyse-stack-sizes:
@@ -109,5 +136,5 @@
 
 .PHONY: clean
 clean:
-	rm -rf target
-	rm Cargo.lock
+	cargo clean
+	$(MAKE) -C tock clean
diff --git a/README.md b/README.md
index 8684d4c..72506cd 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@
 1.  Clone the repository:
 
     ```shell
-    git clone https://github.com/tock/libtock-rs
+    git clone --recursive https://github.com/tock/libtock-rs
     cd libtock-rs
     ```
 
@@ -108,7 +108,7 @@
 
 ## License
 
-Licensed under either of
+libtock-rs is licensed under either of
 
 - Apache License, Version 2.0
   ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
@@ -117,6 +117,8 @@
 
 at your option.
 
+Submodules have their own licenses.
+
 ### Contribution
 
 Unless you explicitly state otherwise, any contribution intentionally submitted
diff --git a/boards/layout_nrf52.ld b/boards/layout_nrf52.ld
index 419a0bc..40cdb10 100644
--- a/boards/layout_nrf52.ld
+++ b/boards/layout_nrf52.ld
@@ -3,7 +3,7 @@
 MEMORY {
   /* The application region is 64 bytes (0x40) */
   FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0
-  SRAM (rwx) : ORIGIN = 0x20002000, LENGTH = 62K
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
 }
 
 /*
diff --git a/boards/layout_opentitan.ld b/boards/layout_opentitan.ld
index db48eef..0ae4b60 100644
--- a/boards/layout_opentitan.ld
+++ b/boards/layout_opentitan.ld
@@ -10,7 +10,7 @@
    * the kernel binary, check for the actual address of APP_MEMORY!
    */
   FLASH (rx) : ORIGIN = 0x20030040, LENGTH = 32M
-  SRAM (rwx) : ORIGIN = 0x10002800, LENGTH = 512K
+  SRAM (rwx) : ORIGIN = 0x10002D00, LENGTH = 512K
 }
 
 /*
diff --git a/core/examples/empty_main.rs b/core/examples/empty_main.rs
new file mode 100644
index 0000000..dd062bc
--- /dev/null
+++ b/core/examples/empty_main.rs
@@ -0,0 +1,14 @@
+// The most minimal libtock-core example possible. This file primarily exists
+// for code size measurement, as this should create the smallest-possible
+// libtock-core app.
+
+#![no_std]
+
+// If you don't *use* anything from libtock-core directly, cargo will not link
+// it into the executable. However, we still need the runtime and lang items.
+// Therefore a libtock-core app that doesn't directly mention anything in
+// libtock-core needs to explicitly declare its dependency on libtock-core as
+// follows.
+extern crate libtock_core;
+
+fn main() {}
diff --git a/doc/Dependencies.md b/doc/Dependencies.md
new file mode 100644
index 0000000..f157049
--- /dev/null
+++ b/doc/Dependencies.md
@@ -0,0 +1,29 @@
+Third Party Dependencies
+========================
+
+This document is about dependencies that are required to build applications
+using `libtock-rs`. These dependencies are not contained in the libtock-rs
+repository, but are used by libtock-rs when libtock-rs is used as a dependency
+of an application. Dependencies required to run `libtock-rs`' tests (such as
+`make`) are outside the scope of this document.
+
+## Unaudited Required Dependencies
+
+`libtock-rs` has the following required build dependencies, none of which are
+currently audited:
+
+* The Rust toolchain, including
+  [`cargo`](https://github.com/rust-lang/cargo),
+  [`rustc`](https://github.com/rust-lang/rust/tree/master/src/rustc), and
+  [`libcore`](https://github.com/rust-lang/rust/tree/master/src/libcore). The
+  specific toolchain version used is specified by the `rust-toolchain` file at
+  the root of the repository.
+* [`syn`](https://crates.io/crates/syn), pulled in by `libtock_codegen`.
+* [`quote`](https://crates.io/crates/quote), pulled in by `libtock_codegen`.
+* [`proc-macro2`](https://crates.io/crates/proc-macro2), pulled in by
+  `libtock_codegen`.
+
+## Avoiding Optional Dependencies
+
+To avoid pulling in optional dependencies, users should use `libtock-core`
+instead of `libtock`. `libtock-core` is in the `core/` directory.
diff --git a/examples-features/libtock_test.rs b/examples-features/libtock_test.rs
index 5fe9e59..9a64e67 100644
--- a/examples-features/libtock_test.rs
+++ b/examples-features/libtock_test.rs
@@ -1,5 +1,6 @@
 // Libtock regression tests to be used with real hardware.
-// Requires P0.03 and P0.04 to be connected (on a nRF52 DK).
+// Requires P0.03 and P0.04 to be connected (on a nRF52 DK) and
+// P0.01 and P0.03 to be connected (on a nRF52840dk).
 
 #![no_std]
 extern crate alloc;
diff --git a/test-runner/src/main.rs b/test-runner/src/main.rs
index 99b941c..e297e8e 100644
--- a/test-runner/src/main.rs
+++ b/test-runner/src/main.rs
@@ -14,14 +14,15 @@
 async fn perform_tests() -> Result<(), Box<dyn std::error::Error>> {
     let mut failed_tests = Vec::new();
 
-    let  tests = Command::new("qemu-system-riscv32")
+    let tests = Command::new("tock/tools/qemu/riscv32-softmmu/qemu-system-riscv32")
         .arg("-M")
         .arg("sifive_e,revb=true")
         .arg("-kernel")
-        .arg("../../tock/target/riscv32imac-unknown-none-elf/release/hifive1")
+        .arg("tock/target/riscv32imac-unknown-none-elf/release/hifive1")
         .arg("-device")
-        .arg("loader,file=./../target/riscv32imac-unknown-none-elf/tab/hifive1/libtock_test/rv32imac.tbf,addr=0x20040000")
+        .arg("loader,file=target/riscv32imac-unknown-none-elf/tab/hifive1/libtock_test/rv32imac.tbf,addr=0x20040000")
         .arg("-nographic")
+        .stdin(Stdio::null())
         .stdout(Stdio::piped())
         .kill_on_drop(true)
         .spawn()?;
diff --git a/tock b/tock
new file mode 160000
index 0000000..152189f
--- /dev/null
+++ b/tock
@@ -0,0 +1 @@
+Subproject commit 152189fe077aea955332ee3c28ccbee519d8b072
diff --git a/tools/flash.sh b/tools/flash.sh
index f24127e..ea75dee 100755
--- a/tools/flash.sh
+++ b/tools/flash.sh
@@ -3,7 +3,7 @@
 set -eux
 
 artifact="$(basename $1)"
-rust_target_folder="$(readlink -f $(dirname $1)/../..)"
+rust_target_folder="$(cd $(dirname $1)/../.. && pwd -P)"
 if [ -z $APP_HEAP_SIZE ]; then
 	echo "Set APP_HEAP_SIZE to a value"
 	exit 1
diff --git a/tools/print-sizes/Cargo.toml b/tools/print-sizes/Cargo.toml
new file mode 100644
index 0000000..97bdc48
--- /dev/null
+++ b/tools/print-sizes/Cargo.toml
@@ -0,0 +1,13 @@
+# Finds all the libtock-core and libtock-rs examples and prints the sizes of
+# several of their sections. Searches the target/$ARCH/release directory. Note
+# that print-sizes will not build the examples; that is done by the
+# `print-sizes` Makefile action.
+
+[package]
+authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
+edition = "2018"
+name = "print-sizes"
+version = "0.1.0"
+
+[dependencies]
+elf = "0.0.10"
diff --git a/tools/print-sizes/src/main.rs b/tools/print-sizes/src/main.rs
new file mode 100644
index 0000000..dcd7777
--- /dev/null
+++ b/tools/print-sizes/src/main.rs
@@ -0,0 +1,142 @@
+// Architectures that we expect the examples to be built for.
+const ARCHITECTURES: [&str; 2] = ["riscv32imc-unknown-none-elf", "thumbv7em-none-eabi"];
+
+// The order of these fields actually matters, because it affects the derived
+// Ord impl. I have a suspicion that when I introduce size diffs into the CI,
+// this order will make the eventual diffs easier to understand than other
+// orderings.
+#[derive(Eq, PartialEq, PartialOrd, Ord)]
+struct Example {
+    name: String,
+    arch: &'static str,
+    path: std::path::PathBuf,
+}
+
+// Finds the example binaries and returns a list of their paths.
+fn find_examples() -> Vec<Example> {
+    // Find target/ using std::env::current_exe().
+    let exe_dir = std::env::current_exe().expect("Unable to find executable location");
+    let target_dir = exe_dir
+        .parent()
+        .expect("Unable to find target/ directory")
+        .parent()
+        .expect("Unable to find target/ directory");
+
+    let mut examples = Vec::new();
+
+    for arch in &ARCHITECTURES {
+        // Set examples_dir to target/$ARCH/examples/
+        let mut examples_dir = target_dir.to_path_buf();
+        examples_dir.push(arch);
+        examples_dir.push("release");
+        examples_dir.push("examples");
+
+        // If the architecture's examples directory exists, iterate through the
+        // files through it and search for examples. If the directory doesn't
+        // exist we skip this architecture.
+        if let Ok(read_dir) = examples_dir.read_dir() {
+            for file in read_dir.filter_map(Result::ok) {
+                use std::os::unix::ffi::OsStrExt;
+
+                // Skip entries that are not files. If file_type() returns
+                // Err(_) we skip the entry as well.
+                if !file.file_type().map_or(false, |t| t.is_file()) {
+                    continue;
+                }
+
+                // Skip files with dots (*.d files) and hyphens (-$HASH) in
+                // them.
+                if file.file_name().as_bytes().contains(&b'.')
+                    || file.file_name().as_bytes().contains(&b'-')
+                {
+                    continue;
+                }
+
+                examples.push(Example {
+                    name: file.file_name().to_string_lossy().into_owned(),
+                    arch,
+                    path: file.path(),
+                });
+            }
+        }
+    }
+
+    examples
+}
+
+struct ElfSizes {
+    bss: u64,
+    data: u64,
+    rodata: u64,
+    text: u64,
+}
+
+fn get_sizes(path: &std::path::Path) -> ElfSizes {
+    let file = elf::File::open_path(path).expect("Unable to open example binary");
+    let mut sizes = ElfSizes {
+        bss: 0,
+        data: 0,
+        rodata: 0,
+        text: 0,
+    };
+    for section in file.sections {
+        match section.shdr.name.as_ref() {
+            ".bss" => sizes.bss = section.shdr.size,
+            ".data" => sizes.data = section.shdr.size,
+            ".rodata" => sizes.rodata = section.shdr.size,
+            ".text" => sizes.text = section.shdr.size,
+            _ => {}
+        }
+    }
+    sizes
+}
+
+struct ExampleData {
+    name: String,
+    arch: &'static str,
+    sizes: ElfSizes,
+}
+
+fn main() {
+    let mut examples = find_examples();
+    examples.sort_unstable();
+    let example_data: Vec<_> = examples
+        .drain(..)
+        .map(|example| ExampleData {
+            name: example.name,
+            arch: example.arch,
+            sizes: get_sizes(&example.path),
+        })
+        .collect();
+
+    let name_width = 20;
+    let arch_width = example_data
+        .iter()
+        .map(|a| a.arch.len())
+        .max()
+        .expect("No examples found");
+    let section_width = 7;
+
+    // TODO: We do not currently print out .rodata's size. Currently, the linker
+    // script embeds .rodata in .text, so we don't see it as a separate section
+    // here. We should modify the linker script to put .rodata in its own
+    // section. Until that is done, .rodata's size will be counted as part of
+    // .text, so we'll just print .text's size for now.
+    println!(
+        "{0:1$} {2:3$} {4:>7$} {5:>7$} {6:>7$}",
+        "Example", name_width, "Architecture", arch_width, ".bss", ".data", ".text", section_width
+    );
+    for data in example_data {
+        println!(
+            "{0:1$} {2:3$} {4:7$} {5:7$} {6:7$}",
+            data.name,
+            name_width,
+            data.arch,
+            arch_width,
+            data.sizes.bss,
+            data.sizes.data,
+            data.sizes.text,
+            section_width
+        );
+    }
+}