Merge #273
273: Make ErrorCode an enum that exactly represents the ErrorCode values the kernel can return. r=hudson-ayers a=jrvanwhy
I previously made `ErrorCode` a struct that can represent any `u32` value because it was unclear what values future Tock versions may return. The Tock 2.0 TRD now says that the kernel will never return an error code of 0 or an error code greater than 1023, so we can rely on that.
This has the benefit of enabling niche optimizations on the type. The zero value is now a usable niche (zero being the most efficient value to compare against in many cases), as well as all values above 1023.
There are two drawbacks to this approach:
1. 3 more uses of `unsafe` in `CommandReturn`, maybe a few extra uses in the `Syscalls` implementation.
2. A huge block of reserved numbers in `ErrorCode`.
I used the following code to generate the number block, and vim commands to format it:
```
fn main() {
for i in 1..=1023 {
print!("N{:05} = {:05}, ", i, i);
if i % 5 == 0 { println!(); }
}
}
```
Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/.cargo/config b/.cargo/config
index 96fe275..8292c1c 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -6,10 +6,6 @@
rthumbv7em = "run --release --target=thumbv7em-none-eabi --example"
rtv7em = "rthumbv7em"
-# Deny warnings on all architectures. build.rustflags cannot be used here as the lower section would override its effect.
-[target.'cfg(all())']
-rustflags = ["-D", "warnings"]
-
# Common settings for all embedded targets
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))']
rustflags = [
diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml
index 94a8b10..d394011 100644
--- a/.github/workflows/artifacts.yml
+++ b/.github/workflows/artifacts.yml
@@ -12,6 +12,7 @@
- name: Install dependencies
run: |
+ sudo apt-get install binutils-riscv64-unknown-elf
cargo install elf2tab --version 0.4.0
- name: Build Hello World
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5bdae5f..e9b448b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,9 +16,9 @@
# 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
+ # until support for 20.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
+ runs-on: ubuntu-20.04
steps:
# Clones a single commit from the libtock-rs repository. The commit cloned
@@ -35,8 +35,16 @@
# 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.
+ #
+ # We have to append the "-D warnings" flag to .cargo/config rather than
+ # using the RUSTFLAGS environment variable because if we set RUSTFLAGS
+ # cargo will ignore the rustflags config in .cargo/config, breaking
+ # relocation.
- name: Build and Test
run: |
+ sudo apt-get install binutils-riscv64-unknown-elf
cd "${GITHUB_WORKSPACE}"
+ echo "[target.'cfg(all())']" >> .cargo/config
+ echo 'rustflags = ["-D", "warnings"]' >> .cargo/config
make -j2 setup
make -j2 test
diff --git a/.github/workflows/mac-os.yml b/.github/workflows/mac-os.yml
new file mode 100644
index 0000000..c813ace
--- /dev/null
+++ b/.github/workflows/mac-os.yml
@@ -0,0 +1,27 @@
+# This workflow verifies libtock-rs is usable on Mac OS.
+
+name: ci-mac-os
+
+# We run this workflow during pull request review, but not for Bors merges, as
+# it takes over an hour to run.
+on: pull_request
+
+jobs:
+ ci-mac-os:
+ runs-on: macos-10.15
+
+ 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.
+ - name: Clone repository
+ uses: actions/checkout@v2.3.0
+
+ # Install the toolchains we need, then run `cargo build`.
+ - name: Build and Test
+ run: |
+ brew tap riscv/riscv
+ brew update
+ brew install riscv-gnu-toolchain --with-multilib
+ cd "${GITHUB_WORKSPACE}"
+ LIBTOCK_PLATFORM=hifive1 cargo build -p libtock_runtime \
+ --target=riscv32imac-unknown-none-elf
diff --git a/.github/workflows/size-diff.yml b/.github/workflows/size-diff.yml
index cbf2939..33a0870 100644
--- a/.github/workflows/size-diff.yml
+++ b/.github/workflows/size-diff.yml
@@ -17,9 +17,9 @@
# 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
+ # until support for 20.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
+ runs-on: ubuntu-20.04
steps:
# Clones a single commit from the libtock-rs repository. The commit cloned
@@ -39,6 +39,7 @@
# master.
- name: Compute sizes
run: |
+ sudo apt-get install binutils-riscv64-unknown-elf
UPSTREAM_REMOTE_NAME="${UPSTREAM_REMOTE_NAME:-origin}"
GITHUB_BASE_REF="${GITHUB_BASE_REF:-master}"
cd "${GITHUB_WORKSPACE}"
diff --git a/Makefile b/Makefile
index da40d28..e228ab3 100644
--- a/Makefile
+++ b/Makefile
@@ -79,8 +79,9 @@
.PHONY: test
test: examples test-qemu-hifive
LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo fmt --all -- --check
- LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo clippy --workspace --all-targets
- LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo miri test --workspace
+ PLATFORM=nrf52 cargo clippy --all-targets --exclude libtock_runtime --workspace
+ LIBTOCK_PLATFORM=hifive1 cargo clippy --target=riscv32imac-unknown-none-elf -p libtock_runtime
+ PLATFORM=nrf52 cargo miri test --exclude libtock_runtime --workspace
echo '[ SUCCESS ] libtock-rs tests pass'
.PHONY: analyse-stack-sizes
diff --git a/core/runtime/asm/asm_riscv32.S b/core/runtime/asm/asm_riscv32.S
new file mode 100644
index 0000000..db1036e
--- /dev/null
+++ b/core/runtime/asm/asm_riscv32.S
@@ -0,0 +1,86 @@
+/* rt_header is defined by the general linker script (libtock_layout.ld). It has
+ * the following layout:
+ *
+ * Field | Offset
+ * ------------------------------------
+ * Address of the start symbol | 0
+ * Initial process break | 4
+ * Top of the stack | 8
+ * Size of .data | 12
+ * Start of .data in flash | 16
+ * Start of .data in ram | 20
+ * Size of .bss | 24
+ * Start of .bss in ram | 28
+ */
+
+/* start is the entry point -- the first code executed by the kernel. The kernel
+ * passes arguments through 4 registers:
+ *
+ * a0 Pointer to beginning of the process binary's code. The linker script
+ * locates rt_header at this address.
+ *
+ * a1 Address of the beginning of the process's usable memory region.
+ * a2 Size of the process' allocated memory region (including grant region)
+ * a3 Process break provided by the kernel.
+ *
+ * We currently only use the value in a0. It is copied into a5 early on because
+ * a0-a4 are needed to invoke system calls.
+ */
+.section .start, "ax"
+.globl start
+start:
+ /* First, verify the process binary was loaded at the correct address. The
+ * check is performed by comparing the program counter at the start to the
+ * address of `start`, which is stored in rt_header. */
+ auipc s0, 0 /* s0 = pc */
+ mv a5, a0 /* Save rt_header so syscalls don't overwrite it */
+ lw s1, 0(a5) /* s1 = rt_header.start */
+ beq s0, s1, .Lset_brk /* Skip error handling code if pc is correct */
+ /* If the beq on the previous line did not jump, then the binary is not at
+ * the correct location. Report the error via LowLevelDebug then exit. */
+ li a0, 8 /* LowLevelDebug driver number */
+ li a1, 1 /* Command: Print alert code */
+ li a2, 2 /* Alert code 2 (incorrect location) */
+ li a4, 2 /* `command` class */
+ ecall
+ li a0, 0 /* exit-terminate */
+ /* TODO: Set a completion code, once completion codes are decided */
+ li a4, 6 /* `exit` class */
+ ecall
+
+.Lset_brk:
+ /* memop(): set brk to rt_header's initial break value */
+ li a0, 0 /* operation: set break */
+ lw a1, 4(a5) /* rt_header's initial process break */
+ li a4, 5 /* `memop` class */
+ ecall
+
+ /* Set the stack pointer */
+ lw sp, 8(a5) /* sp = rt_header._stack_top */
+
+ /* Copy .data into place. */
+ lw a0, 12(a5) /* remaining = rt_header.data_size */
+ beqz a0, .Lzero_bss /* Jump to zero_bss if remaining is zero */
+ lw a1, 16(a5) /* src = rt_header.data_flash_start */
+ lw a2, 20(a5) /* dest = rt_header.data_ram_start */
+.Ldata_loop_body:
+ lw a3, 0(a1) /* a3 = *src */
+ sw a3, 0(a2) /* *dest = a3 */
+ addi a0, a0, -4 /* remaining -= 4 */
+ addi a1, a1, 4 /* src += 4 */
+ addi a2, a2, 4 /* dest += 4 */
+ bnez a0, .Ldata_loop_body /* Iterate again if remaining != 0 */
+
+.Lzero_bss:
+ lw a0, 24(a5) /* remaining = rt_header.bss_size */
+ beqz a0, .Lcall_rust_start /* Jump to call_Main if remaining is zero */
+ lw a1, 28(a5) /* dest = rt_header.bss_start */
+.Lbss_loop_body:
+ sb zero, 0(a1) /* *dest = zero */
+ addi a0, a0, -1 /* remaining -= 1 */
+ addi a1, a1, 1 /* dest += 1 */
+ bnez a0, .Lbss_loop_body /* Iterate again if remaining != 0 */
+
+.Lcall_rust_start:
+ /* Note: rust_start must be a diverging function (i.e. return `!`) */
+ jal rust_start
diff --git a/core/runtime/build.rs b/core/runtime/build.rs
index d0281b5..347d82c 100644
--- a/core/runtime/build.rs
+++ b/core/runtime/build.rs
@@ -1,6 +1,8 @@
use std::fs::copy;
use std::path::PathBuf;
+mod extern_asm;
+
// auto_layout() identifies the correct linker scripts to use based on the
// LIBTOCK_PLATFORM environment variable, and copies the linker scripts into
// OUT_DIR. The cargo invocation must pass -C link-arg=-Tlayout.ld to rustc
@@ -8,7 +10,7 @@
#[cfg(not(feature = "no_auto_layout"))]
fn auto_layout(out_dir: &str) {
const PLATFORM_CFG_VAR: &str = "LIBTOCK_PLATFORM";
- const LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";
+ const LAYOUT_GENERIC_FILENAME: &str = "libtock_layout.ld";
// Note: we need to print these rerun-if commands before using the variable
// or file, so that if the build script fails cargo knows when to re-run it.
@@ -49,7 +51,9 @@
#[cfg(not(feature = "no_auto_layout"))]
auto_layout(out_dir);
- // This link search path is used by both auto_layout() and extern_asm().
- // TODO: Add external assembly and extern_asm().
+ extern_asm::build_and_link(out_dir);
+
+ // This link search path is used by both auto_layout() and
+ // extern_asm::build_and_link().
println!("cargo:rustc-link-search={}", out_dir);
}
diff --git a/core/runtime/extern_asm.rs b/core/runtime/extern_asm.rs
new file mode 100644
index 0000000..d0488f8
--- /dev/null
+++ b/core/runtime/extern_asm.rs
@@ -0,0 +1,140 @@
+//! Build script module for compiling the external assembly (used for the entry
+//! point) and linking it into the process binary. Requires out_dir to be added
+//! to rustc's link search path.
+
+pub(crate) fn build_and_link(out_dir: &str) {
+ use std::env::var;
+ let arch = var("CARGO_CFG_TARGET_ARCH").expect("Unable to read CARGO_CFG_TARGET_ARCH");
+
+ // Identify the toolchain configurations to try for the target architecture.
+ // We support trying multiple toolchains because not all toolchains are
+ // available on every OS that we want to support development on.
+ let build_configs = match arch.as_str() {
+ "riscv32" => &[
+ // First try riscv64-unknown-elf, as it is the toolchain used by
+ // libtock-c and the toolchain used in the CI environment.
+ AsmBuildConfig {
+ triple: "riscv64-unknown-elf",
+ as_extra_args: &["-march=rv32imc"],
+ strip: true,
+ },
+ // Second try riscv32-unknown-elf. This is the best match for Tock's
+ // risc-v targets, but is not as widely available (and has not been
+ // tested with libtock-rs yet).
+ AsmBuildConfig {
+ triple: "riscv32-unknown-elf",
+ as_extra_args: &[],
+ strip: false, // Untested, may need to change.
+ },
+ // Last try riscv64-linux-gnu, as it is the only option on Debian 10
+ AsmBuildConfig {
+ triple: "riscv64-linux-gnu",
+ as_extra_args: &["-march=rv32imc"],
+ strip: true,
+ },
+ ],
+ unknown_arch => {
+ panic!("Unsupported architecture {}", unknown_arch);
+ }
+ };
+
+ // Loop through toolchain configs until one works.
+ for &build_config in build_configs {
+ if try_build(&arch, build_config, out_dir).is_ok() {
+ return;
+ }
+ }
+}
+
+#[derive(Clone, Copy)]
+struct AsmBuildConfig {
+ // Triple name, which is prepended to the command names.
+ triple: &'static str,
+
+ // Extra arguments to pass to the assembler.
+ as_extra_args: &'static [&'static str],
+
+ // Do we need to strip the object file before packing it into the library
+ // archive? This should be set to true on platforms where the assembler adds
+ // local symbols to the object file.
+ strip: bool,
+}
+
+// Indicates the toolchain in the build config is unavailable.
+struct ToolchainUnavailable;
+
+fn try_build(
+ arch: &str,
+ build_config: AsmBuildConfig,
+ out_dir: &str,
+) -> Result<(), ToolchainUnavailable> {
+ use std::path::PathBuf;
+ use std::process::Command;
+
+ // Invoke the assembler to produce an object file.
+ let asm_source = &format!("asm/asm_{}.S", arch);
+ let obj_file_path = [out_dir, "libtock_rt_asm.o"].iter().collect::<PathBuf>();
+ let obj_file = obj_file_path.to_str().expect("Non-Unicode obj_file_path");
+ let as_result = Command::new(format!("{}-as", build_config.triple))
+ .args(build_config.as_extra_args)
+ .args(&[asm_source, "-o", obj_file])
+ .status();
+
+ match as_result {
+ Err(error) => {
+ if error.kind() == std::io::ErrorKind::NotFound {
+ // This `as` command does not exist. Return an error so
+ // build_an_link can try another config (if one is available).
+ return Err(ToolchainUnavailable);
+ } else {
+ panic!("Error invoking assembler: {}", error);
+ }
+ }
+ Ok(status) => {
+ assert!(status.success(), "Assembler returned an error");
+ }
+ }
+
+ // At this point, we know this toolchain is installed. We will fail if later
+ // commands are uninstalled rather than trying a different build config.
+
+ println!("cargo:rerun-if-changed={}", asm_source);
+
+ // Run `strip` if necessary.
+ if build_config.strip {
+ let strip_cmd = format!("{}-strip", build_config.triple);
+ let status = Command::new(&strip_cmd)
+ .args(&["-K", "start", "-K", "rust_start", obj_file])
+ .status()
+ .unwrap_or_else(|_| panic!("Failed to invoke {}", strip_cmd));
+ assert!(status.success(), "{} returned an error", strip_cmd);
+ }
+
+ // Remove the archive file in case there is something unexpected in it. This
+ // prevents issues from persisting across invocations of this script.
+ const ARCHIVE_NAME: &str = "tock_rt_asm";
+ let archive_path: PathBuf = [out_dir, &format!("lib{}.a", ARCHIVE_NAME)]
+ .iter()
+ .collect();
+ if let Err(error) = std::fs::remove_file(&archive_path) {
+ if error.kind() != std::io::ErrorKind::NotFound {
+ panic!("Unable to remove archive file {}", archive_path.display());
+ }
+ }
+
+ // Create the library archive.
+ let ar_cmd = format!("{}-ar", build_config.triple);
+ let archive = archive_path.to_str().expect("Non-Unicode archive_path");
+ let status = std::process::Command::new(&ar_cmd)
+ // c == Do not complain if archive needs to be created.
+ // r == Insert or replace file in archive.
+ .args(&["cr", archive, obj_file])
+ .status()
+ .unwrap_or_else(|_| panic!("Failed to invoke {}", ar_cmd));
+ assert!(status.success(), "{} returned an error", ar_cmd);
+
+ // Tell rustc to link the binary against the library archive.
+ println!("cargo:rustc-link-lib=static={}", ARCHIVE_NAME);
+
+ Ok(())
+}
diff --git a/core/runtime/layout_generic.ld b/core/runtime/layout_generic.ld
deleted file mode 100644
index 0bbef87..0000000
--- a/core/runtime/layout_generic.ld
+++ /dev/null
@@ -1,171 +0,0 @@
-/* Userland Generic Layout
- *
- * Currently, due to incomplete ROPI-RWPI support in rustc (see
- * https://github.com/tock/libtock-rs/issues/28), this layout implements static
- * linking. An application init script must define the FLASH and SRAM address
- * ranges as well as MPU_MIN_ALIGN before including this layout file.
- *
- * Here is a an example application linker script to get started:
- * MEMORY {
- * /* FLASH memory region must start immediately *after* the Tock
- * * Binary Format headers, which means you need to offset the
- * * beginning of FLASH memory region relative to where the
- * * application is loaded.
- * FLASH (rx) : ORIGIN = 0x10030, LENGTH = 0x0FFD0
- * SRAM (RWX) : ORIGIN = 0x20000, LENGTH = 0x10000
- * }
- * MPU_MIN_ALIGN = 8K;
- * INCLUDE ../libtock-rs/layout.ld
- */
-
-ENTRY(_start)
-
-SECTIONS {
- /* Section for just the app crt0 header.
- * This must be first so that the app can find it.
- */
- .crt0_header :
- {
- _beginning = .; /* Start of the app in flash. */
- /**
- * Populate the header expected by `crt0`:
- *
- * struct hdr {
- * uint32_t got_sym_start;
- * uint32_t got_start;
- * uint32_t got_size;
- * uint32_t data_sym_start;
- * uint32_t data_start;
- * uint32_t data_size;
- * uint32_t bss_start;
- * uint32_t bss_size;
- * uint32_t reldata_start;
- * uint32_t stack_size;
- * };
- */
- /* Offset of GOT symbols in flash */
- LONG(LOADADDR(.got) - _beginning);
- /* Offset of GOT section in memory */
- LONG(_got);
- /* Size of GOT section */
- LONG(SIZEOF(.got));
- /* Offset of data symbols in flash */
- LONG(LOADADDR(.data) - _beginning);
- /* Offset of data section in memory */
- LONG(_data);
- /* Size of data section */
- LONG(SIZEOF(.data));
- /* Offset of BSS section in memory */
- LONG(_bss);
- /* Size of BSS section */
- LONG(SIZEOF(.bss));
- /* First address offset after program flash, where elf2tab places
- * .rel.data section */
- LONG(LOADADDR(.endflash) - _beginning);
- /* The size of the stack requested by this application */
- LONG(_stack_top_aligned - _sstack);
- /* Pad the header out to a multiple of 32 bytes so there is not a gap
- * between the header and subsequent .data section. It's unclear why,
- * but LLD is aligning sections to a multiple of 32 bytes. */
- . = ALIGN(32);
- } > FLASH =0xFF
-
- /* Text section, Code! */
- .text :
- {
- . = ALIGN(4);
- _text = .;
- KEEP (*(.start))
- *(.text*)
- *(.rodata*)
- KEEP (*(.syscalls))
- *(.ARM.extab*)
- . = ALIGN(4); /* Make sure we're word-aligned here */
- _etext = .;
- } > FLASH =0xFF
-
- /* Application stack */
- .stack (NOLOAD) :
- {
- /* elf2tab requires that the `_sram_origin` symbol be present to
- * mark the first address in the SRAM memory. Since ELF files do
- * not really need to specify this address as they only care about
- * loading into flash, we need to manually mark this address for
- * elf2tab. elf2tab will use it to add a fixed address header in the
- * TBF header if needed.
- */
- _sram_origin = .;
- _sstack = .;
- KEEP(*(.stack_buffer))
- _stack_top_unaligned = .;
- . = ALIGN(8);
- _stack_top_aligned = .;
- } > SRAM
-
- /* Data section, static initialized variables
- * Note: This is placed in Flash after the text section, but needs to be
- * moved to SRAM at runtime
- */
- .data : AT (_etext)
- {
- . = ALIGN(4); /* Make sure we're word-aligned here */
- _data = .;
- KEEP(*(.data*))
- *(.sdata*) /* RISC-V small-pointer data section */
- . = ALIGN(4); /* Make sure we're word-aligned at the end of flash */
- } > SRAM
-
- /* Global Offset Table */
- .got :
- {
- . = ALIGN(4); /* Make sure we're word-aligned here */
- _got = .;
- *(.got*)
- *(.got.plt*)
- . = ALIGN(4);
- } > SRAM
-
- /* BSS section, static uninitialized variables */
- .bss :
- {
- . = ALIGN(4); /* Make sure we're word-aligned here */
- _bss = .;
- KEEP(*(.bss* .sbss*))
- *(COMMON)
- . = ALIGN(4);
- } > SRAM
-
- /* End of flash. */
- .endflash :
- {
- } > FLASH
-
- /* ARM Exception support
- *
- * This contains compiler-generated support for unwinding the stack,
- * consisting of key-value pairs of function addresses and information on
- * how to unwind stack frames.
- * https://wiki.linaro.org/KenWerner/Sandbox/libunwind?action=AttachFile&do=get&target=libunwind-LDS.pdf
- *
- * .ARM.exidx is sorted, so has to go in its own output section.
- *
- * __NOTE__: It's at the end because we currently don't actually serialize
- * it to the binary in elf2tbf. If it was before the RAM sections, it would
- * through off our calculations of the header.
- */
- PROVIDE_HIDDEN (__exidx_start = .);
- .ARM.exidx :
- {
- /* (C++) Index entries for section unwinding */
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } > FLASH
- PROVIDE_HIDDEN (__exidx_end = .);
-
- /DISCARD/ :
- {
- *(.eh_frame)
- }
-}
-
-ASSERT((_stack_top_aligned - _stack_top_unaligned) == 0, "
-STACK_SIZE must be 8 byte multiple")
diff --git a/core/runtime/layouts/apollo3.ld b/core/runtime/layouts/apollo3.ld
index 8713c27..464cf98 100644
--- a/core/runtime/layouts/apollo3.ld
+++ b/core/runtime/layouts/apollo3.ld
@@ -1,11 +1,9 @@
/* Layout for the Apollo3 MCU, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0005FFC0
- SRAM (rwx) : ORIGIN = 0x10002000, LENGTH = 0x2000
+ FLASH (X) : ORIGIN = 0x00040000, LENGTH = 0x00060000
+ RAM (W) : ORIGIN = 0x10002000, LENGTH = 0x2000
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/hail.ld b/core/runtime/layouts/hail.ld
index 179a883..db48142 100644
--- a/core/runtime/layouts/hail.ld
+++ b/core/runtime/layouts/hail.ld
@@ -1,11 +1,9 @@
/* Layout for the Hail board, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+ FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x00060000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 62K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/hifive1.ld b/core/runtime/layouts/hifive1.ld
index 20294ce..78dc51b 100644
--- a/core/runtime/layouts/hifive1.ld
+++ b/core/runtime/layouts/hifive1.ld
@@ -1,18 +1,12 @@
/* Layout for the RISC-V 32 boards, used by the examples in this repository. */
MEMORY {
- /*
- * The TBF header can change in size so use 0x40 combined with
- * --protected-region-size with elf2tab to cover a header upto that
- * size.
- *
- * Note that the SRAM address may need to be changed depending on
+ /* Note that the SRAM address may need to be changed depending on
* the kernel binary, check for the actual address of APP_MEMORY!
*/
- FLASH (rx) : ORIGIN = 0x20040040, LENGTH = 32M
- SRAM (rwx) : ORIGIN = 0x80002400, LENGTH = 0x1C00
+ FLASH (X) : ORIGIN = 0x20040000, LENGTH = 32M
+ RAM (W) : ORIGIN = 0x80002400, LENGTH = 0x1C00
}
-MPU_MIN_ALIGN = 1K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/imxrt1050.ld b/core/runtime/layouts/imxrt1050.ld
index 3e7f904..1458870 100644
--- a/core/runtime/layouts/imxrt1050.ld
+++ b/core/runtime/layouts/imxrt1050.ld
@@ -1,11 +1,9 @@
/* Layout for the iMX.RT1050 board, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x63002040, LENGTH = 0xFFFFC0
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 112K
+ FLASH (X) : ORIGIN = 0x63002000, LENGTH = 0x1000000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 112K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/msp432.ld b/core/runtime/layouts/msp432.ld
index 2c9d2a4..170d4b1 100644
--- a/core/runtime/layouts/msp432.ld
+++ b/core/runtime/layouts/msp432.ld
@@ -1,9 +1,7 @@
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x00020040, LENGTH = 0x0001FFC0
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 0x2000
+ FLASH (X) : ORIGIN = 0x00020000, LENGTH = 0x00020000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 0x2000
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nrf52.ld b/core/runtime/layouts/nrf52.ld
index 942e86b..fbbcdc3 100644
--- a/core/runtime/layouts/nrf52.ld
+++ b/core/runtime/layouts/nrf52.ld
@@ -1,11 +1,9 @@
/* Layout for the nRF52-DK, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+ FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x00060000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 62K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nrf52840.ld b/core/runtime/layouts/nrf52840.ld
index 31bf346..06b6f6b 100644
--- a/core/runtime/layouts/nrf52840.ld
+++ b/core/runtime/layouts/nrf52840.ld
@@ -1,11 +1,9 @@
/* Layout for the nRF52840-DK, usable by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x000CFFC0
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+ FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x000D0000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 62K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nucleo_f429zi.ld b/core/runtime/layouts/nucleo_f429zi.ld
index 3e407b4..a9c634a 100644
--- a/core/runtime/layouts/nucleo_f429zi.ld
+++ b/core/runtime/layouts/nucleo_f429zi.ld
@@ -1,11 +1,9 @@
/* Layout for the Nucleo F429zi, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x08040040, LENGTH = 255K
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 112K
+ FLASH (X) : ORIGIN = 0x08040000, LENGTH = 255K
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 112K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nucleo_f446re.ld b/core/runtime/layouts/nucleo_f446re.ld
index 83e698b..d3953f5 100644
--- a/core/runtime/layouts/nucleo_f446re.ld
+++ b/core/runtime/layouts/nucleo_f446re.ld
@@ -1,11 +1,9 @@
/* Layout for the Nucleo F446re, used by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x08040040, LENGTH = 255K
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 176K
+ FLASH (X) : ORIGIN = 0x08040000, LENGTH = 255K
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 176K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/opentitan.ld b/core/runtime/layouts/opentitan.ld
index ada781c..50ea340 100644
--- a/core/runtime/layouts/opentitan.ld
+++ b/core/runtime/layouts/opentitan.ld
@@ -1,18 +1,12 @@
/* Layout for the RISC-V 32 boards, used by the examples in this repository. */
MEMORY {
- /*
- * The TBF header can change in size so use 0x40 combined with
- * --protected-region-size with elf2tab to cover a header upto that
- * size.
- *
- * Note that the SRAM address may need to be changed depending on
+ /* Note that the SRAM address may need to be changed depending on
* the kernel binary, check for the actual address of APP_MEMORY!
*/
- FLASH (rx) : ORIGIN = 0x20030040, LENGTH = 32M
- SRAM (rwx) : ORIGIN = 0x10004000, LENGTH = 512K
+ FLASH (X) : ORIGIN = 0x20030000, LENGTH = 32M
+ RAM (W) : ORIGIN = 0x10004000, LENGTH = 512K
}
-MPU_MIN_ALIGN = 1K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/stm32f3discovery.ld b/core/runtime/layouts/stm32f3discovery.ld
index 9368003..05b0b54 100644
--- a/core/runtime/layouts/stm32f3discovery.ld
+++ b/core/runtime/layouts/stm32f3discovery.ld
@@ -1,11 +1,9 @@
/* Layout for the stm32f3discovery board, usable by the examples in this repository. */
MEMORY {
- /* The application region is 64 bytes (0x40) */
- FLASH (rx) : ORIGIN = 0x08020040, LENGTH = 0x00020000
- SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 48K
+ FLASH (X) : ORIGIN = 0x08020000, LENGTH = 0x00020000
+ RAM (W) : ORIGIN = 0x20004000, LENGTH = 48K
}
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/libtock_layout.ld b/core/runtime/libtock_layout.ld
new file mode 100644
index 0000000..fcff09a
--- /dev/null
+++ b/core/runtime/libtock_layout.ld
@@ -0,0 +1,129 @@
+/* Layout file for Tock process binaries that use libtock-rs. This currently
+ * implements static linking, because we do not have a working
+ * position-independent relocation solution. This layout works for all
+ * platforms libtock-rs supports (ARM and RISC-V).
+ *
+ * This layout should be included by a script that defines the FLASH and RAM
+ * regions for the board as well as TBF_HEADER_SIZE. Here is a an example
+ * process binary linker script to get started:
+ * MEMORY {
+ * FLASH (X) : ORIGIN = 0x10000, LENGTH = 0x10000
+ * RAM (W) : ORIGIN = 0x20000, LENGTH = 0x10000
+ * }
+ * TBF_HEADER_SIZE = 0x40;
+ * INCLUDE ../libtock-rs/layout.ld
+ *
+ * FLASH refers to the area the process binary occupies in flash, including TBF
+ * headers. RAM refers to the area the process will have access to in memory.
+ * STACK_SIZE is the size of the process' stack (this layout file may round the
+ * stack size up for alignment purposes). TBF_HEADER_SIZE must correspond to the
+ * --protected-region-size flag passed to elf2tab.
+ *
+ * This places the flash sections in the following order:
+ * 1. .rt_header -- Constants used by runtime initialization.
+ * 2. .text -- Executable code.
+ * 3. .rodata -- Read-only global data (e.g. most string constants).
+ * 4. .data -- Read-write data, copied to RAM at runtime.
+ *
+ * This places the RAM sections in the following order:
+ * 1. .stack -- The stack grows downward. Putting it first gives us
+ * MPU-based overflow detection.
+ * 2. .data -- Read-write data, initialized by copying from flash.
+ * 3. .bss -- Zero-initialized read-write global data.
+ * 4. Heap -- The heap (optional) comes after .bss and grows upwards to
+ * the process break.
+ */
+
+/* TODO: Should TBF_HEADER_SIZE be configured via a similar mechanism to the
+ * stack size? We should see if that is possible.
+ */
+
+/* GNU LD looks for `start` as an entry point by default, while LLVM's LLD looks
+ * for `_start`. To be compatible with both, we manually specify an entry point.
+ */
+ENTRY(start)
+
+SECTIONS {
+ /* Sections located in FLASH at runtime.
+ */
+
+ /* Add a section where elf2tab will place the TBF headers, so that the rest
+ * of the FLASH sections are in the right locations. */
+ .tbf_header (NOLOAD) : {
+ . = . + TBF_HEADER_SIZE;
+ } > FLASH
+
+ /* Runtime header. Contains values the linker knows that the runtime needs
+ * to look up.
+ */
+ .rt_header : {
+ rt_header = .;
+ LONG(start);
+ LONG(ADDR(.bss) + SIZEOF(.bss)); /* Initial process break */
+ LONG(_stack_top);
+ LONG(SIZEOF(.data));
+ LONG(LOADADDR(.data));
+ LONG(ADDR(.data));
+ LONG(SIZEOF(.bss));
+ LONG(ADDR(.bss));
+ } > FLASH
+
+ /* Text section -- the application's code. */
+ .text ALIGN(4) : {
+ *(.start)
+ *(.text)
+ } > FLASH
+
+ /* Read-only data section. Contains strings and other global constants. */
+ .rodata ALIGN(4) : {
+ *(.rodata)
+ /* .data is placed after .rodata in flash. data_flash_start is used by
+ * AT() to place .data in flash as well as in rt_header.
+ */
+ _data_flash_start = .;
+ } > FLASH
+
+ /* Sections located in RAM at runtime.
+ */
+
+ /* Reserve space for the stack. Aligned to a multiple of 16 bytes for the
+ * RISC-V calling convention:
+ * https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
+ */
+ .stack (NOLOAD) : {
+ KEEP(*(.stack_buffer))
+ . = ALIGN(16);
+ _stack_top = .; /* Used in rt_header */
+ } > RAM
+
+ /* Read-write data section. This is deployed as part of FLASH but is copied
+ * into RAM at runtime.
+ */
+ .data ALIGN(4) : AT(_data_flash_start) {
+ data_ram_start = .;
+ /* .sdata is the RISC-V small data section */
+ *(.sdata .data)
+ /* Pad to word alignment so the relocation loop can use word-sized
+ * copies.
+ */
+ . = ALIGN(4);
+ } > RAM
+
+ /* BSS section. These are zero-initialized static variables. This section is
+ * not copied from FLASH into RAM but rather directly initialized, and is
+ * mainly put in this linker script so that we get an error if it overflows
+ * the RAM region.
+ */
+ .bss ALIGN(4) (NOLOAD) : {
+ /* .sbss is the RISC-V small data section */
+ *(.sbss .bss)
+ } > RAM
+
+ _heap_start = ADDR(.bss) + SIZEOF(.bss); /* Used by rt_header */
+
+ /* Sections we do not need. */
+ /DISCARD/ :
+ {
+ *(.ARM.exidx .eh_frame)
+ }
+}