From cb814b353bfd039cf6b2661118432a16f9a07087 Mon Sep 17 00:00:00 2001 From: Berkus Decker <berkus+github@metta.systems> Date: Mon, 3 Aug 2020 02:32:13 +0300 Subject: [PATCH] Add Raspberry Pi target configuration * Use rlibc for memset/memcpy * Create linker script for raspberry pi 3 * Add aarch64 target file inspired by Robigalia * Force bitcode embedding for LTO --- .cargo/config.toml | 13 +++++ .gitignore | 2 + Cargo.lock | 17 +++++++ Justfile | 2 +- Makefile.toml | 52 +++++++++++++++++++ linker/aarch64.ld | 80 ++++++++++++++++++++++++++++++ nucleus/Cargo.toml | 2 + nucleus/Makefile.toml | 24 +++++++++ nucleus/src/main.rs | 13 ++++- targets/README.md | 25 ++++++++++ targets/aarch64-vesper-metta.json | 22 ++++++++ targets/bcm2710-rpi-3-b-plus.dtb | Bin 0 -> 27082 bytes 12 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 Makefile.toml create mode 100644 linker/aarch64.ld create mode 100644 nucleus/Makefile.toml create mode 100644 targets/README.md create mode 100644 targets/aarch64-vesper-metta.json create mode 100644 targets/bcm2710-rpi-3-b-plus.dtb diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0a6e1fa --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,13 @@ +[build] +# https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199/12 +pipelining = true + +[target.aarch64-vesper-metta] +rustflags = [ + "-C", "target-feature=-fp-armv8", + "-C", "target-cpu=cortex-a53", + "-C", "embed-bitcode=yes", +] + +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] diff --git a/.gitignore b/.gitignore index 4bfa550..7c0ef8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/ *.iml target/ +kernel8* +.gdb_history diff --git a/Cargo.lock b/Cargo.lock index af43cfc..e89c0f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,23 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rlibc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vesper" version = "0.0.1" +dependencies = [ + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +[metadata] +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" diff --git a/Justfile b/Justfile index a560f8b..06476c6 100644 --- a/Justfile +++ b/Justfile @@ -2,4 +2,4 @@ qemu: @echo No QEMU support device: - cargo build + cargo make sdcard diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..b881152 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,52 @@ +# +# SPDX-License-Identifier: BlueOak-1.0.0 +# +# Copyright (c) Berkus Decker <berkus+github@metta.systems> +# +[config] +min_version = "0.24.0" +default_to_workspace = true + +[env] +CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = "false" + +DEFAULT_TARGET = "aarch64-vesper-metta" + +DEVICE_FEATURES = "noserial" + +OBJCOPY = "cargo objcopy" +OBJCOPY_PARAMS = "-- --strip-all -O binary" + +UTILS_CONTAINER = "andrerichter/raspi3-utils" +DOCKER_CMD = "docker run -it --rm -v ${PWD}:/work -w /work -p 5900:5900" + +TARGET_JSON = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/${DEFAULT_TARGET}.json" + +KERNEL_ELF = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/kernel8" +KERNEL_BIN = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/kernel8.img" + +[tasks.default] +alias = "all" + +[tasks.modules] +command = "cargo" +args = ["modules", "tree"] + +[tasks.all] +dependencies = ["kernel-binary"] + +[tasks.kernel-binary] # Forward build to vesper/Makefile.toml +env = { "CARGO_MAKE_MEMBER_TASK" = "kernel-binary" } +run_task = "do-on-members" + +[tasks.build] # Forward build to vesper/Makefile.toml +env = { "CARGO_MAKE_MEMBER_TASK" = "build" } +run_task = "do-on-members" + +[tasks.sdcard] +env = { "CARGO_MAKE_MEMBER_TASK" = "sdcard" } +run_task = "do-on-members" + +[tasks.sdeject] +env = { "CARGO_MAKE_MEMBER_TASK" = "sdeject" } +run_task = "do-on-members" diff --git a/linker/aarch64.ld b/linker/aarch64.ld new file mode 100644 index 0000000..923ee1b --- /dev/null +++ b/linker/aarch64.ld @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: MIT OR BlueOak-1.0.0 + * Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com> + * Original code distributed under MIT, additional changes are under BlueOak-1.0.0 + */ + +ENTRY(_boot_cores); + +/* Symbols between __boot_start and __boot_end should be dropped after init is complete. + Symbols between __ro_start and __ro_end are the kernel code. + Symbols between __bss_start and __bss_end must be initialized to zero by r0 code in kernel. +*/ +SECTIONS +{ + . = 0x80000; /* AArch64 boot address is 0x80000, 4K-aligned */ + __boot_start = .; + .text : + { + KEEP(*(.text.boot)) + . = ALIGN(4096); + KEEP(*(.data.boot)) + . = ALIGN(4096); /* Here boot code ends */ + __boot_end = .; // __boot_end must be 4KiB aligned + __ro_start = .; + *(.text .text.*) + } + + .vectors ALIGN(2048): + { + *(.vectors) + } + + .rodata ALIGN(4): + { + *(.rodata .rodata.*) + FILL(0x00) + } + . = ALIGN(4096); /* Fill up to 4KiB */ + __ro_end = .; /* __ro_end must be 4KiB aligned */ + __data_start = .; /* __data_start must be 4KiB aligned */ + + .data : /* @todo align data to 4K -- it's already aligned up to __ro_end marker now */ + { + *(.data .data.*) + FILL(0x00) + } + + /* @todo could insert .data.boot here with proper alignment */ + + .bss ALIGN(8): + { + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4096); /* Align up to 4KiB */ + __bss_end = .; + } + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} + +PROVIDE(current_el0_synchronous = default_exception_handler); +PROVIDE(current_el0_irq = default_exception_handler); +PROVIDE(current_el0_fiq = default_exception_handler); +PROVIDE(current_el0_serror = default_exception_handler); + +PROVIDE(current_elx_synchronous = default_exception_handler); +PROVIDE(current_elx_irq = default_exception_handler); +PROVIDE(current_elx_fiq = default_exception_handler); +PROVIDE(current_elx_serror = default_exception_handler); + +PROVIDE(lower_aarch64_synchronous = default_exception_handler); +PROVIDE(lower_aarch64_irq = default_exception_handler); +PROVIDE(lower_aarch64_fiq = default_exception_handler); +PROVIDE(lower_aarch64_serror = default_exception_handler); + +PROVIDE(lower_aarch32_synchronous = default_exception_handler); +PROVIDE(lower_aarch32_irq = default_exception_handler); +PROVIDE(lower_aarch32_fiq = default_exception_handler); +PROVIDE(lower_aarch32_serror = default_exception_handler); diff --git a/nucleus/Cargo.toml b/nucleus/Cargo.toml index e048a41..9caf908 100644 --- a/nucleus/Cargo.toml +++ b/nucleus/Cargo.toml @@ -16,3 +16,5 @@ edition = "2018" maintenance = { status = "experimental" } [dependencies] +r0 = "0.2" +rlibc = "1.0" diff --git a/nucleus/Makefile.toml b/nucleus/Makefile.toml new file mode 100644 index 0000000..e59109e --- /dev/null +++ b/nucleus/Makefile.toml @@ -0,0 +1,24 @@ +# +# SPDX-License-Identifier: BlueOak-1.0.0 +# +# Copyright (c) Berkus Decker <berkus+github@metta.systems> +# +[tasks.kernel-binary] +script = [ + "cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${DEFAULT_TARGET}/release/vesper ${KERNEL_ELF}", + "${OBJCOPY} ${OBJCOPY_PARAMS} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${DEFAULT_TARGET}/release/vesper ${KERNEL_BIN}" +] + +[tasks.build] +env = { "TARGET_FEATURES" = "" } +args = ["build", "-Zbuild-std=core,compiler_builtins,alloc", "--target=${TARGET_JSON}", "--release", "--features=${TARGET_FEATURES}"] + +[tasks.sdcard] +dependencies = ["build", "kernel-binary"] +command = "cp" +args = ["${KERNEL_BIN}", "/Volumes/BOOT/"] + +[tasks.sdeject] +dependencies = ["sdcard"] +command = "diskutil" +args = ["unmount", "/Volumes/BOOT/"] diff --git a/nucleus/src/main.rs b/nucleus/src/main.rs index e7a11a9..f964b35 100644 --- a/nucleus/src/main.rs +++ b/nucleus/src/main.rs @@ -1,3 +1,12 @@ -fn main() { - println!("Hello, world!"); +#![no_std] +#![no_main] + +#[no_mangle] +pub extern "C" fn _boot_cores() -> ! { + loop {} +} + +#[panic_handler] +fn panicked(_info: &core::panic::PanicInfo) -> ! { + loop {} } diff --git a/targets/README.md b/targets/README.md new file mode 100644 index 0000000..0c97e63 --- /dev/null +++ b/targets/README.md @@ -0,0 +1,25 @@ +# vesper-targets + +These are [target +specifications](https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md) +suitable for cross-compiling Rust crates for Vesper. + +They are very much based on Robigalia's [sel4-targets](https://gitlab.com/robigalia/sel4-targets). + +## Status + +Complete for aarch64. Untested for anything else. + +## Generating target specifications: + +See [description in rust docs](https://doc.rust-lang.org/rustc/targets/custom.html). + +To generate a target specification json template, run + +``` +rustc +nightly -Z unstable-options --target=<your target name> --print target-spec-json +``` + +### To do + +"panic-strategy": "abort" is ok for baremetal targets, but not for -metta, right? Will rework for userspace targets when we have unwinding. diff --git a/targets/aarch64-vesper-metta.json b/targets/aarch64-vesper-metta.json new file mode 100644 index 0000000..363679e --- /dev/null +++ b/targets/aarch64-vesper-metta.json @@ -0,0 +1,22 @@ +{ + "llvm-target": "aarch64-unknown-none", + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "arch": "aarch64", + "os": "vesper", + "vendor": "metta", + "env": "", + "executables": true, + "panic-strategy": "abort", + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "pre-link-args": { + "ld.lld": [ + "--script=linker/aarch64.ld", + "--print-gc-sections" + ] + }, + "disable-redzone": true, + "target-endian": "little", + "target-c-int-width": "32", + "target-pointer-width": "64" +} diff --git a/targets/bcm2710-rpi-3-b-plus.dtb b/targets/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..bdc7e43f05cfe1f2d4001627672783f06ed9a155 GIT binary patch literal 27082 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 B18)ET literal 0 HcmV?d00001