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.
This commit is contained in:
Stefan 2021-06-09 18:38:46 +02:00
parent ed82c5487f
commit 7f47c7655d
12 changed files with 344 additions and 11 deletions

2
.gitignore vendored
View File

@ -5,5 +5,7 @@ rvc
*.o *.o
elfy/target elfy/target
elfy/Cargo.lock elfy/Cargo.lock
rust_payload/target
rust_payload/Cargo.lock
fw_payload.* fw_payload.*
*.dtb *.dtb

View File

@ -15,7 +15,8 @@ $(TARGET): $(OBJS)
.PHONY: clean .PHONY: clean
clean: clean:
$(RM) $(TARGET) $(OBJS) $(DEPS) $(RM) $(TARGET) $(OBJS) $(DEPS) fw_payload.*
$(MAKE) -C opensbi clean
-include $(DEPS) -include $(DEPS)
@ -24,16 +25,31 @@ dts.dtb: dts.dts
dtc -o $@ $< dtc -o $@ $<
OPENSBI_BUILD="opensbi/build/platform/generic/firmware" 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 fw_payload.bin: fw_payload.elf
cp $(OPENSBI_BUILD)/fw_payload.bin . cp $(OPENSBI_BUILD)/fw_payload.bin .
fw_payload.elf: opensbi fw_payload.elf: $(PAYLOAD)
env CROSS_COMPILE=riscv64-elf- \ env CROSS_COMPILE=riscv64-elf- \
PLATFORM=generic \ PLATFORM=generic \
PLATFORM_RISCV_XLEN=32 \ PLATFORM_RISCV_XLEN=32 \
PLATFORM_RISCV_ISA=rv32ima \ PLATFORM_RISCV_ISA=rv32ima \
PLATFORM_RISCV_ABI=ilp32 \ PLATFORM_RISCV_ABI=ilp32 \
FW_PAYLOAD_PATH=../$(PAYLOAD) \
FW_PIC=n \ FW_PIC=n \
ELFFLAGS=-L/usr/lib/gcc/riscv64-elf/10.2.0/rv32im/ilp32 \ ELFFLAGS=-L/usr/lib/gcc/riscv64-elf/10.2.0/rv32im/ilp32 \
$(MAKE) -C opensbi all $(MAKE) -C opensbi all
cp $(OPENSBI_BUILD)/fw_payload.elf . 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

View File

@ -4,7 +4,7 @@
#address-cells = <0x2>; #address-cells = <0x2>;
#size-cells = <0x2>; #size-cells = <0x2>;
compatible = "riscv-virtio"; compatible = "riscv-virtio";
model = "riscv-virtio,qemu"; model = "generic,rvc";
chosen { chosen {
bootargs = "ttyS0"; bootargs = "ttyS0";

9
rust_payload/Cargo.toml Normal file
View File

@ -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}

30
rust_payload/linker.ld Normal file
View File

@ -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
}

View File

@ -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);
}

View File

@ -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);
}
}

117
rust_payload/src/main.rs Normal file
View File

@ -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");
}

View File

@ -38,7 +38,7 @@ const uint CSR_MTVAL = 0x343;
const uint CSR_MIP = 0x344; const uint CSR_MIP = 0x344;
const uint _CSR_PMPCFG0 = 0x3a0; const uint _CSR_PMPCFG0 = 0x3a0;
const uint _CSR_PMPADDR0 = 0x3b0; const uint _CSR_PMPADDR0 = 0x3b0;
const uint _CSR_MCYCLE = 0xb00; const uint CSR_MCYCLE = 0xb00;
const uint CSR_CYCLE = 0xc00; const uint CSR_CYCLE = 0xc00;
const uint CSR_TIME = 0xc01; const uint CSR_TIME = 0xc01;
const uint _CSR_INSERT = 0xc02; 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_SIE: return cpu->csr.data[CSR_MIE] & 0x222;
case CSR_SIP: return cpu->csr.data[CSR_MIP] & 0x222; case CSR_SIP: return cpu->csr.data[CSR_MIP] & 0x222;
case CSR_TIME: return cpu->clint.mtime_lo; case CSR_TIME: return cpu->clint.mtime_lo;
case CSR_MCYCLE: return cpu->clock;
case CSR_CYCLE: return cpu->clock; case CSR_CYCLE: return cpu->clock;
case CSR_MHARTID: return 0; case CSR_MHARTID: return 0;
default: return cpu->csr.data[address & 0xffff]; 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) { if (address == CSR_SATP) {
// TODO: update MMU addressing mode // TODO: update MMU addressing mode
if (VERBOSE >= 1) if (VERBOSE >= 1)
printf("WARN: Ignoring write to CSR_SATP\n"); printf("WARN: Ignoring write to CSR_SATP (%08x)\n", value);
return; return;
} }
write_csr_raw(cpu, address, value); write_csr_raw(cpu, address, value);

View File

@ -6,8 +6,10 @@
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#include "types.h" #include "types.h"
#include "cpu.h" #include "cpu.h"
#include "uart.h"
#include "../elfy/elfy.h" #include "../elfy/elfy.h"
@ -31,6 +33,16 @@ void usage() {
exit(EXIT_FAILURE); 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[]) { int main(int argc, char *argv[]) {
char *elf = NULL; char *elf = NULL;
char *bin = NULL; char *bin = NULL;
@ -76,9 +88,15 @@ int main(int argc, char *argv[]) {
memcpy(mem, bin_ptr, get_filesize(bin)); 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) if (VERBOSE >= 1)
printf("CPU initialized!\n"); printf("CPU initialized!\n");
@ -92,6 +110,11 @@ int main(int argc, char *argv[]) {
if (VERBOSE >= 3) if (VERBOSE >= 3)
cpu_dump(&cpu); cpu_dump(&cpu);
/* char input = 0; */
/* if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) { */
/* uart_input_value = input; */
/* } */
if (SINGLE_STEP) { if (SINGLE_STEP) {
fflush(stdout); fflush(stdout);
fflush(stdin); fflush(stdin);
@ -99,7 +122,7 @@ int main(int argc, char *argv[]) {
gc: ch = getchar(); gc: ch = getchar();
switch (ch) { switch (ch) {
case '\n': break; case '\n': break;
case 'c': SINGLE_STEP = 0; break; case 'c': SINGLE_STEP = false; break;
default: goto gc; default: goto gc;
} }
} }

View File

@ -4,6 +4,8 @@
#include <stdio.h> #include <stdio.h>
#include "types.h" #include "types.h"
static uint uart_input_value = 0;
const uint SHIFT_RBR = 0; const uint SHIFT_RBR = 0;
const uint SHIFT_THR = 8; const uint SHIFT_THR = 8;
const uint SHIFT_IER = 16; const uint SHIFT_IER = 16;
@ -39,7 +41,8 @@ void uart_tick(cpu_t *cpu) {
bool rx_ip = false; bool rx_ip = false;
if ((cpu->clock % 0x38400) == 0 && UART_GET1(RBR) == 0) { 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) { if (value != 0) {
UART_SET1(RBR, value); UART_SET1(RBR, value);
UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE)); UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE));
@ -53,6 +56,7 @@ void uart_tick(cpu_t *cpu) {
uint thr = UART_GET1(THR); uint thr = UART_GET1(THR);
if ((cpu->clock & 0x16) == 0 && thr != 0) { if ((cpu->clock & 0x16) == 0 && thr != 0) {
printf("%c", (char)thr); printf("%c", (char)thr);
fflush(stdout);
UART_SET1(THR, 0); UART_SET1(THR, 0);
UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY)); UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY));
uart_update_iir(cpu); uart_update_iir(cpu);

View File

@ -19,8 +19,8 @@ function run_test {
popd popd
echo "Running: ./rvc -e \"./riscv-tests/isa/$1\"" echo "Running: ./rvc -e \"./riscv-tests/isa/$1\" -v 1"
timeout 5s ./rvc -e "./riscv-tests/isa/$1" timeout 5s ./rvc -e "./riscv-tests/isa/$1" -v 1
if [ $? -gt 0 ]; then if [ $? -gt 0 ]; then
echo "Test failed!" echo "Test failed!"