From 7f47c7655d56e69338c15a933a1c327a6d81812a Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 9 Jun 2021 18:38:46 +0200 Subject: [PATCH] Add rust_payload and fixes to allow running it as supervisor Note that the 'riscv32ima-unknown-none-elf' target is not available by default in rustc, you need a patched version. I'm also pretty sure just converting the ELF binary to raw using objdump messes with stack and heap addresses, but it seems to work fine for now. --- .gitignore | 2 + Makefile | 20 ++++- dts.dts | 2 +- rust_payload/Cargo.toml | 9 ++ rust_payload/linker.ld | 30 +++++++ rust_payload/src/low_level/mod.rs | 64 ++++++++++++++ rust_payload/src/low_level/opensbi.rs | 67 +++++++++++++++ rust_payload/src/main.rs | 117 ++++++++++++++++++++++++++ src/csr.h | 5 +- src/main.c | 29 ++++++- src/uart.h | 6 +- test.sh | 4 +- 12 files changed, 344 insertions(+), 11 deletions(-) create mode 100644 rust_payload/Cargo.toml create mode 100644 rust_payload/linker.ld create mode 100644 rust_payload/src/low_level/mod.rs create mode 100644 rust_payload/src/low_level/opensbi.rs create mode 100644 rust_payload/src/main.rs diff --git a/.gitignore b/.gitignore index a3d76208..7af67f87 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ rvc *.o elfy/target elfy/Cargo.lock +rust_payload/target +rust_payload/Cargo.lock fw_payload.* *.dtb diff --git a/Makefile b/Makefile index 67013fcc..0ec2c48d 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,8 @@ $(TARGET): $(OBJS) .PHONY: clean clean: - $(RM) $(TARGET) $(OBJS) $(DEPS) + $(RM) $(TARGET) $(OBJS) $(DEPS) fw_payload.* + $(MAKE) -C opensbi clean -include $(DEPS) @@ -24,16 +25,31 @@ dts.dtb: dts.dts dtc -o $@ $< OPENSBI_BUILD="opensbi/build/platform/generic/firmware" +PAYLOAD="rust_payload/target/riscv32ima-unknown-none-elf/release/rust_payload.bin" + +$(PAYLOAD): $(shell find rust_payload/src -type f) + cd rust_payload; \ + cargo +riscv32ima rustc -Zbuild-std --release --target "riscv32ima-unknown-none-elf" -- -Clink-arg=-Tlinker.ld -Clinker=riscv32-elf-ld + riscv32-elf-objcopy -O binary \ + rust_payload/target/riscv32ima-unknown-none-elf/release/rust_payload $(PAYLOAD) fw_payload.bin: fw_payload.elf cp $(OPENSBI_BUILD)/fw_payload.bin . -fw_payload.elf: opensbi +fw_payload.elf: $(PAYLOAD) env CROSS_COMPILE=riscv64-elf- \ PLATFORM=generic \ PLATFORM_RISCV_XLEN=32 \ PLATFORM_RISCV_ISA=rv32ima \ PLATFORM_RISCV_ABI=ilp32 \ + FW_PAYLOAD_PATH=../$(PAYLOAD) \ FW_PIC=n \ ELFFLAGS=-L/usr/lib/gcc/riscv64-elf/10.2.0/rv32im/ilp32 \ $(MAKE) -C opensbi all cp $(OPENSBI_BUILD)/fw_payload.elf . + +.PHONY: run +run: fw_payload.bin $(TARGET) dts.dtb + ./rvc -b fw_payload.bin -d dts.dtb + +run-v1: fw_payload.bin $(TARGET) dts.dtb + ./rvc -b fw_payload.bin -d dts.dtb -v1 diff --git a/dts.dts b/dts.dts index 36e5f2ba..fb56813d 100644 --- a/dts.dts +++ b/dts.dts @@ -4,7 +4,7 @@ #address-cells = <0x2>; #size-cells = <0x2>; compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; + model = "generic,rvc"; chosen { bootargs = "ttyS0"; diff --git a/rust_payload/Cargo.toml b/rust_payload/Cargo.toml new file mode 100644 index 00000000..4fe77897 --- /dev/null +++ b/rust_payload/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust_payload" +version = "0.1.0" +edition = "2018" + +[dependencies] +riscv = "0.6.0" +linked_list_allocator = "0.9.0" +lite-json = {version="0.1.3", default-features=false} diff --git a/rust_payload/linker.ld b/rust_payload/linker.ld new file mode 100644 index 00000000..971744fc --- /dev/null +++ b/rust_payload/linker.ld @@ -0,0 +1,30 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x80400000, LENGTH = 0x800000 +} + +SECTIONS +{ + .kernel : { + *(.start_here) + *(.text .text.*) + *(.rodata .rodata.*) + *(.data .data.*) + } > ram + + .stack (NOLOAD) : { + . = . + 0x20000; + PROVIDE(_end_stack = .); + } > ram + + .heap (NOLOAD) : { + PROVIDE(_begin_heap = .); + . = . + 0x40000; + PROVIDE(_end_heap = .); + } > ram + +} diff --git a/rust_payload/src/low_level/mod.rs b/rust_payload/src/low_level/mod.rs new file mode 100644 index 00000000..e4386348 --- /dev/null +++ b/rust_payload/src/low_level/mod.rs @@ -0,0 +1,64 @@ +use core::panic::PanicInfo; +use linked_list_allocator::LockedHeap; + +mod opensbi; +pub use opensbi::*; + +#[naked] +#[no_mangle] +#[link_section = ".start_here"] +#[allow(dead_code)] +/// The actual entry point of the binary, sets up stack and jumps to 'main' +extern "C" fn _start() -> ! { + unsafe { + asm!( + " + la sp, {end_stack} + j main + ", + end_stack = sym _end_stack, + options(noreturn) + ); + } +} + +#[alloc_error_handler] +fn alloc_error(_layout: core::alloc::Layout) -> ! { + println("ALLOC ERROR!"); + loop {} +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + println("PANIC!"); + loop {} +} + +// defined by linker script +extern "C" { + static _end_stack: usize; + static _begin_heap: usize; + static _end_heap: usize; +} + +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + +pub unsafe fn init_heap() { + let stack_end = &_end_stack as *const usize as usize; + let heap_start = &_begin_heap as *const usize as usize; + let heap_end = &_end_heap as *const usize as usize; + let heap_size = heap_end - heap_start; + + print("> stack_end: "); + print_usize(stack_end); + print("\n> heap_start: "); + print_usize(heap_start); + print("\n> heap_end: "); + print_usize(heap_end); + print("\n> heap_size: "); + print_usize(heap_size); + print("\n"); + + ALLOCATOR.lock().init(heap_start, heap_size); +} diff --git a/rust_payload/src/low_level/opensbi.rs b/rust_payload/src/low_level/opensbi.rs new file mode 100644 index 00000000..48775ed7 --- /dev/null +++ b/rust_payload/src/low_level/opensbi.rs @@ -0,0 +1,67 @@ +pub const _SBI_SET_TIMER: usize = 0x00; +pub const SBI_CONSOLE_PUTCHAR: usize = 0x01; +pub const SBI_CONSOLE_GETCHAR: usize = 0x02; + +#[inline(always)] +pub fn ecall(nr: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { + let ret: usize; + unsafe { + asm!( + "ecall", + in("x17") nr, + in("x10") arg0, + in("x11") arg1, + in("x12") arg2, + lateout("x10") ret + ); + } + ret +} + +#[inline(always)] +pub fn print_char(ch: u8) { + ecall(SBI_CONSOLE_PUTCHAR, ch as usize, 0, 0); +} + +pub fn println(s: &str) { + print(s); + print_char('\n' as u8); +} + +pub fn print(s: &str) { + s.chars().for_each(|c| print_char(c as u8)); +} + +#[inline(always)] +fn print_hex_part(u: u8) { + match u { + 0 => print("0"), + 1 => print("1"), + 2 => print("2"), + 3 => print("3"), + 4 => print("4"), + 5 => print("5"), + 6 => print("6"), + 7 => print("7"), + 8 => print("8"), + 9 => print("9"), + 10 => print("A"), + 11 => print("B"), + 12 => print("C"), + 13 => print("D"), + 14 => print("E"), + 15 => print("F"), + _ => unreachable!(), + } +} + +pub fn print_usize(u: usize) { + print("0x"); + for i in 0..=3 { + let part = ((u >> ((3 - i) * 8)) & 0xff) as u8; + let l = part & 0xF; + let h = (part >> 4) & 0xF; + print_hex_part(h); + print_hex_part(l); + } +} diff --git a/rust_payload/src/main.rs b/rust_payload/src/main.rs new file mode 100644 index 00000000..63966d9c --- /dev/null +++ b/rust_payload/src/main.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] +#![feature(asm)] +#![feature(naked_functions)] +#![feature(alloc_error_handler)] + +#[macro_use] +extern crate alloc; + +mod low_level; +use low_level::*; + +use riscv::register; + +#[no_mangle] +pub fn main() -> ! { + print("\n"); + + println("Welcome to Rust-land!"); + + print("Cycles before: "); + print_usize(register::time::read()); + print("\n\n"); + + println("Initializing heap..."); + unsafe { + init_heap(); + } + print("\n"); + + // input_test(); + // print("\n"); + + json_test(); + print("\n"); + + print("Cycles after: "); + print_usize(register::time::read()); + print("\n"); + + println("Tests done, entering infinite loop!"); + loop {} +} + +/// Test opensbi uart input +fn input_test() { + print("Please enter some text: "); + let mut s = vec![]; + loop { + let i = loop { + let i = ecall(SBI_CONSOLE_GETCHAR, 0, 0, 0); + if i != 0 && i != usize::MAX { + break i as u8 as char; + } + }; + + match i { + '\n' => break, + _ => s.push(i as u8) + } + } + print("Your text was: "); + for c in s { + print_char(c); + } + print("\n"); +} + +/// From lite-json docs, a simple example to showcase functionality +fn json_test() { + use lite_json::json::{JsonValue, NumberValue}; + use lite_json::Serialize; + + println("Preparing JSON test..."); + + let mut object_elements = vec![]; + + let boolean_value = true; + let object_key = "boolean".chars().collect(); + object_elements.push((object_key, JsonValue::Boolean(boolean_value))); + + let array_value = vec![ + JsonValue::Boolean(true), + JsonValue::Boolean(false), + JsonValue::Boolean(true), + ]; + let object_key = "array".chars().collect(); + object_elements.push((object_key, JsonValue::Array(array_value))); + + let string_value = "Hello World!".chars().collect(); + let object_key = "string".chars().collect(); + object_elements.push((object_key, JsonValue::String(string_value))); + + let number_value = NumberValue { + integer: 1234, + fraction: 0, + fraction_length: 0, + exponent: 0, + }; + let object_key = "number".chars().collect(); + object_elements.push((object_key, JsonValue::Number(number_value))); + + let object_key = "null".chars().collect(); + object_elements.push((object_key, JsonValue::Null)); + + let object_value = JsonValue::Object(object_elements); + + println("JSON dump:"); + + let json = object_value.format(2); + for x in json { + print_char(x as u8); + } + + print("\n"); +} + diff --git a/src/csr.h b/src/csr.h index b3f0d7e2..179c143d 100644 --- a/src/csr.h +++ b/src/csr.h @@ -38,7 +38,7 @@ const uint CSR_MTVAL = 0x343; const uint CSR_MIP = 0x344; const uint _CSR_PMPCFG0 = 0x3a0; const uint _CSR_PMPADDR0 = 0x3b0; -const uint _CSR_MCYCLE = 0xb00; +const uint CSR_MCYCLE = 0xb00; const uint CSR_CYCLE = 0xc00; const uint CSR_TIME = 0xc01; const uint _CSR_INSERT = 0xc02; @@ -56,6 +56,7 @@ uint read_csr_raw(cpu_t *cpu, uint address) { case CSR_SIE: return cpu->csr.data[CSR_MIE] & 0x222; case CSR_SIP: return cpu->csr.data[CSR_MIP] & 0x222; case CSR_TIME: return cpu->clint.mtime_lo; + case CSR_MCYCLE: return cpu->clock; case CSR_CYCLE: return cpu->clock; case CSR_MHARTID: return 0; default: return cpu->csr.data[address & 0xffff]; @@ -122,7 +123,7 @@ void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) { if (address == CSR_SATP) { // TODO: update MMU addressing mode if (VERBOSE >= 1) - printf("WARN: Ignoring write to CSR_SATP\n"); + printf("WARN: Ignoring write to CSR_SATP (%08x)\n", value); return; } write_csr_raw(cpu, address, value); diff --git a/src/main.c b/src/main.c index a787d7f1..2edf2442 100644 --- a/src/main.c +++ b/src/main.c @@ -6,8 +6,10 @@ #include #include #include +#include #include "types.h" #include "cpu.h" +#include "uart.h" #include "../elfy/elfy.h" @@ -31,6 +33,16 @@ void usage() { exit(EXIT_FAILURE); } +static cpu_t cpu; + +void term(int signum) +{ + printf("\n\nCaught signal!\n"); + cpu_dump(&cpu); + printf("\n"); + exit(EXIT_FAILURE); +} + int main(int argc, char *argv[]) { char *elf = NULL; char *bin = NULL; @@ -76,9 +88,15 @@ int main(int argc, char *argv[]) { memcpy(mem, bin_ptr, get_filesize(bin)); } - uint8_t *dtb_ptr = get_mmap_ptr("./dts.dtb"); + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = term; + sigaction(SIGTERM, &action, NULL); + sigaction(SIGINT, &action, NULL); - cpu_t cpu = cpu_init(mem, dtb_ptr); + fcntl(0, F_SETFL, O_NONBLOCK); + + cpu = cpu_init(mem, dtb ? get_mmap_ptr(dtb) : NULL); if (VERBOSE >= 1) printf("CPU initialized!\n"); @@ -92,6 +110,11 @@ int main(int argc, char *argv[]) { if (VERBOSE >= 3) cpu_dump(&cpu); + /* char input = 0; */ + /* if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) { */ + /* uart_input_value = input; */ + /* } */ + if (SINGLE_STEP) { fflush(stdout); fflush(stdin); @@ -99,7 +122,7 @@ int main(int argc, char *argv[]) { gc: ch = getchar(); switch (ch) { case '\n': break; - case 'c': SINGLE_STEP = 0; break; + case 'c': SINGLE_STEP = false; break; default: goto gc; } } diff --git a/src/uart.h b/src/uart.h index 372e592d..d2c7c37a 100644 --- a/src/uart.h +++ b/src/uart.h @@ -4,6 +4,8 @@ #include #include "types.h" +static uint uart_input_value = 0; + const uint SHIFT_RBR = 0; const uint SHIFT_THR = 8; const uint SHIFT_IER = 16; @@ -39,7 +41,8 @@ void uart_tick(cpu_t *cpu) { bool rx_ip = false; if ((cpu->clock % 0x38400) == 0 && UART_GET1(RBR) == 0) { - uint value = 0; // TODO: Add actual input logic + uint value = uart_input_value; // TODO: Add actual input logic + uart_input_value = 0; if (value != 0) { UART_SET1(RBR, value); UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE)); @@ -53,6 +56,7 @@ void uart_tick(cpu_t *cpu) { uint thr = UART_GET1(THR); if ((cpu->clock & 0x16) == 0 && thr != 0) { printf("%c", (char)thr); + fflush(stdout); UART_SET1(THR, 0); UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY)); uart_update_iir(cpu); diff --git a/test.sh b/test.sh index 168c4e4f..3a62cbd9 100755 --- a/test.sh +++ b/test.sh @@ -19,8 +19,8 @@ function run_test { popd - echo "Running: ./rvc -e \"./riscv-tests/isa/$1\"" - timeout 5s ./rvc -e "./riscv-tests/isa/$1" + echo "Running: ./rvc -e \"./riscv-tests/isa/$1\" -v 1" + timeout 5s ./rvc -e "./riscv-tests/isa/$1" -v 1 if [ $? -gt 0 ]; then echo "Test failed!"