Merge #272 272: Add the RISC-V entry point assembly to libtock_runtime r=hudson-ayers a=jrvanwhy To support the entry point assembly, I rewrote the linker script ([link to the new version](https://github.com/jrvanwhy/libtock-rs/blob/riscv-asm/core/runtime/layout_generic.ld) in case you don't want to see the diff). The rust toolchain is unable to compile raw assembly files on its own. I don't want a separate toolchain to be a build requirement of `libtock-rs`, so instead I committed the compiled library into the repository. To keep the compiled library and source code in sync, I added an actions workflow that verifies the compiled library matches the provided sources. There is one part of this PR I am unhappy with. I have to manually `strip` local symbols from the compiled library -- i.e., removing the `riscv64-linux-gnu-strip` invocation from `assemble.sh` gives the following: ``` Disassembly of section .start: 00000000 <start>: 0: 00000417 auipc s0,0x0 4: 87aa mv a5,a0 6: 4384 lw s1,0(a5) 8: 00940c63 beq s0,s1,20 <.Lset_brk> c: 4521 li a0,8 e: 4585 li a1,1 10: 4609 li a2,2 12: 4709 li a4,2 14: 00000073 ecall 18: 4501 li a0,0 1a: 4719 li a4,6 1c: 00000073 ecall 00000020 <.Lset_brk>: 20: 4501 li a0,0 22: 43cc lw a1,4(a5) 24: 4715 li a4,5 26: 00000073 ecall 2a: 0087a103 lw sp,8(a5) 2e: 47c8 lw a0,12(a5) 30: c909 beqz a0,42 <.Lzero_bss> 32: 4b8c lw a1,16(a5) 34: 4bd0 lw a2,20(a5) 00000036 <.Ldata_loop_body>: 36: 4194 lw a3,0(a1) 38: c214 sw a3,0(a2) 3a: 1571 addi a0,a0,-4 3c: 0591 addi a1,a1,4 3e: 0611 addi a2,a2,4 40: f97d bnez a0,36 <.Ldata_loop_body> 00000042 <.Lzero_bss>: 42: 4f88 lw a0,24(a5) 44: c519 beqz a0,52 <.Lcall_rust_start> 46: 4fcc lw a1,28(a5) 00000048 <.Lbss_loop_body>: 48: 00058023 sb zero,0(a1) 4c: 157d addi a0,a0,-1 4e: 0585 addi a1,a1,1 50: fd65 bnez a0,48 <.Lbss_loop_body> 00000052 <.Lcall_rust_start>: 52: fafff0ef jal ra,0 <start> ``` I'd appreciate advice on how to prevent `riscv64-linux-gnu-as` from including the local symbols in its output. Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
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 db80220..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 @@ -42,6 +42,7 @@ # 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
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) + } +}