mirror of
https://github.com/PiMaker/rvc.git
synced 2024-11-21 11:30:07 +00:00
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:
parent
ed82c5487f
commit
7f47c7655d
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,5 +5,7 @@ rvc
|
||||
*.o
|
||||
elfy/target
|
||||
elfy/Cargo.lock
|
||||
rust_payload/target
|
||||
rust_payload/Cargo.lock
|
||||
fw_payload.*
|
||||
*.dtb
|
||||
|
20
Makefile
20
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
|
||||
|
2
dts.dts
2
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";
|
||||
|
9
rust_payload/Cargo.toml
Normal file
9
rust_payload/Cargo.toml
Normal 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
30
rust_payload/linker.ld
Normal 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
|
||||
|
||||
}
|
64
rust_payload/src/low_level/mod.rs
Normal file
64
rust_payload/src/low_level/mod.rs
Normal 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);
|
||||
}
|
67
rust_payload/src/low_level/opensbi.rs
Normal file
67
rust_payload/src/low_level/opensbi.rs
Normal 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
117
rust_payload/src/main.rs
Normal 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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
29
src/main.c
29
src/main.c
@ -6,8 +6,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
|
Loading…
Reference in New Issue
Block a user