Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RISC-V support #239

Merged
merged 10 commits into from
Mar 21, 2023
2 changes: 2 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
tests: true
- target: aarch64-unknown-none.json
tests: false
- target: riscv64gcv-unknown-none-elf.json
tests: false
steps:
- name: Code checkout
uses: actions/checkout@v2
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ chrono = { version = "0.4", default-features = false }
uart_16550 = "0.2.18"
x86_64 = "0.14.10"

[target.'cfg(target_arch = "riscv64")'.dependencies]
chrono = { version = "0.4", default-features = false }
fdt = "0.1.5"

[dev-dependencies]
dirs = "5.0.0"
rand = "0.8.5"
Expand Down
65 changes: 52 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ This project was originally developed using
currently support resetting the virtio block device it is not possible to boot
all the way into the OS.

## Building

To compile:

cargo build --release --target x86_64-unknown-none.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem

The result will be in:

target/x86_64-unknown-none/release/hypervisor-fw

## Features

* virtio (PCI) block support
Expand All @@ -44,15 +34,31 @@ target/x86_64-unknown-none/release/hypervisor-fw
* PE32+ loader
* Minimal EFI environment (sufficient to boot shim + GRUB2 as used by Ubuntu)

## Running
## x86-64 Support

### Building

To compile:

```
cargo build --release --target x86_64-unknown-none.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem
```

The result will be in:

```
target/x86_64-unknown-none/release/hypervisor-fw
```

### Running

Works with Cloud Hypervisor and QEMU via their PVH loaders as an alternative to
the Linux kernel.

Cloud Hypervisor and QEMU are currently the primary development targets for the
firmware although support for other VMMs will be considered.

### Cloud Hypervisor
#### Cloud Hypervisor

As per [getting
started](https://github.com/cloud-hypervisor/cloud-hypervisor/blob/master/README.md#2-getting-started)
Expand All @@ -73,7 +79,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
$ popd
```

### QEMU
#### QEMU

Use the QEMU `-kernel` parameter to specify the path to the firmware.

Expand All @@ -88,6 +94,39 @@ $ qemu-system-x86_64 -machine q35,accel=kvm -cpu host,-vmx -m 1G\
-device virtio-blk-pci,drive=os,disable-legacy=on
```

## RISC-V Support

Experimental RISC-V support is available. This is currently designed to run as a
payload from OpenSBI under QEMU virt. It is expected wider platform support
will become available in the future.

### Building

To compile:

```
cargo build --release --target riscv64gcv-unknown-none-elf.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem
```

The result will be in:

```
target/riscv64gcv-unknown-none-elf/release/hypervisor-fw
```

### Running

Currently only QEMU has been tested.

#### QEMU

```
$ qemu-system-riscv64 -M virt -cpu rv64 -smp 1 -m 1024 \
-nographic -kernel target/riscv64gcv-unknown-none-elf/release/hypervisor-fw \
-drive id=mydrive,file=root.img,format=raw \
-device virtio-blk-pci,drive=mydrive,disable-legacy=on
```

## Testing

"cargo test" needs disk images from make-test-disks.sh
Expand Down
21 changes: 21 additions & 0 deletions riscv64gcv-unknown-none-elf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"arch": "riscv64",
"code-model": "medium",
"cpu": "generic-rv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
"eh-frame-header": false,
"emit-debug-gdb-scripts": false,
"features": "+m,+a,+f,+d,+c,+v",
"is-builtin": false,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-abiname": "lp64d",
"llvm-target": "riscv64",
"max-atomic-width": 64,
"panic-strategy": "abort",
"relocation-model": "static",
"target-pointer-width": "64",
"pre-link-args": {
"ld.lld": ["--script=riscv64gcv-unknown-none-elf.ld"]
}
}
46 changes: 46 additions & 0 deletions riscv64gcv-unknown-none-elf.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
ENTRY(ram64_start)

/* OpenSBI loads here */
ram_min = 0x80200000;

SECTIONS
{
/* Mapping the program headers and note into RAM makes the file smaller. */
. = ram_min;

/* These sections are mapped into RAM from the file. Omitting :ram from
later sections avoids emitting empty sections in the final binary. */
code_start = .;
.text.boot : { *(.text.boot) }
.text : { *(.text .text.*) }
. = ALIGN(4K);
code_end = .;

data_start = .;

.data : {
. = ALIGN(4096);
*(.data .data.*)
. = ALIGN(8);
PROVIDE(__global_pointer$ = . + 0x800);
}

.rodata : { *(.rodata .rodata.*) }

/* The BSS section isn't mapped from file data. It is just zeroed in RAM. */
.bss : {
*(.bss .bss.*)
}
. = ALIGN(4K);
data_end = .;

stack_start = .;
.stack (NOLOAD) : ALIGN(4K) { . += 128K; }
stack_end = .;

/* Strip symbols from the output binary (comment out to get symbols) */
/DISCARD/ : {
*(.symtab)
*(.strtab)
}
}
3 changes: 3 additions & 0 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ pub mod aarch64;

#[cfg(target_arch = "x86_64")]
pub mod x86_64;

#[cfg(target_arch = "riscv64")]
pub mod riscv64;
6 changes: 6 additions & 0 deletions src/arch/riscv64/asm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 Rivos Inc.

use core::arch::global_asm;

global_asm!(include_str!("ram64.s"));
58 changes: 58 additions & 0 deletions src/arch/riscv64/layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2022 Akira Moroo
// Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
// Copyright (C) 2023 Rivos Inc.

use core::{cell::UnsafeCell, ops::Range};

use crate::layout::{MemoryAttribute, MemoryDescriptor, MemoryLayout};

extern "Rust" {
static code_start: UnsafeCell<()>;
static code_end: UnsafeCell<()>;
static data_start: UnsafeCell<()>;
static data_end: UnsafeCell<()>;
static stack_start: UnsafeCell<()>;
static stack_end: UnsafeCell<()>;
}

pub fn code_range() -> Range<usize> {
unsafe { (code_start.get() as _)..(code_end.get() as _) }
}

pub fn data_range() -> Range<usize> {
unsafe { (data_start.get() as _)..(data_end.get() as _) }
}

pub fn stack_range() -> Range<usize> {
unsafe { (stack_start.get() as _)..(stack_end.get() as _) }
}

pub fn reserved_range() -> Range<usize> {
0x8000_0000..0x8020_0000
}

const NUM_MEM_DESCS: usize = 4;

pub static MEM_LAYOUT: MemoryLayout<NUM_MEM_DESCS> = [
MemoryDescriptor {
name: "Code",
range: code_range,
attribute: MemoryAttribute::Code,
},
MemoryDescriptor {
name: "Data",
range: data_range,
attribute: MemoryAttribute::Data,
},
MemoryDescriptor {
name: "Stack",
range: stack_range,
attribute: MemoryAttribute::Data,
},
MemoryDescriptor {
name: "SBI",
range: reserved_range,
attribute: MemoryAttribute::Unusable,
},
];
5 changes: 5 additions & 0 deletions src/arch/riscv64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 Rivos Inc.

pub mod asm;
pub mod layout;
24 changes: 24 additions & 0 deletions src/arch/riscv64/ram64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2021 by Rivos Inc.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

.option norvc

.section .text.boot

// The entry point for the boot CPU.
.global ram64_start
ram64_start:

.option push
.option norelax
la gp, __global_pointer$
.option pop
csrw sstatus, zero
csrw sie, zero

la sp, stack_end
call rust64_start
wfi_loop:
wfi
j wfi_loop
4 changes: 2 additions & 2 deletions src/bootinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ pub trait Info {
fn rsdp_addr(&self) -> Option<u64> {
None
}
// Address of FDT to use for booting if present
fn fdt_addr(&self) -> Option<u64> {
// Address/size of FDT used for booting
fn fdt_reservation(&self) -> Option<MemoryEntry> {
None
}
// The kernel command line (not including null terminator)
Expand Down
20 changes: 16 additions & 4 deletions src/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
// Copyright (C) 2018 Google LLC

use core::arch::asm;
#[cfg(target_arch = "riscv64")]
use core::arch::riscv64::pause;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::_rdtsc;

const NSECS_PER_SEC: u64 = 1000000000;
const CPU_KHZ_DEFAULT: u64 = 200;
const PAUSE_THRESHOLD_TICKS: u64 = 150;

#[cfg(target_arch = "aarch64")]
#[inline]
unsafe fn rdtsc() -> u64 {
Expand All @@ -18,6 +16,13 @@ unsafe fn rdtsc() -> u64 {
value
}

#[cfg(target_arch = "riscv64")]
unsafe fn rdtsc() -> u64 {
let r: u64;
unsafe { asm!("csrr {rd}, time", rd = out(reg) r) };
r
}

#[cfg(target_arch = "x86_64")]
#[inline]
unsafe fn rdtsc() -> u64 {
Expand All @@ -37,6 +42,13 @@ unsafe fn pause() {
}

pub fn ndelay(ns: u64) {
#[cfg(not(target_arch = "riscv64"))]
const CPU_KHZ_DEFAULT: u64 = 200;
#[cfg(target_arch = "riscv64")]
const CPU_KHZ_DEFAULT: u64 = 1_000_000; /* QEMU currently defines as 1GHz */
const NSECS_PER_SEC: u64 = 1_000_000_000;
const PAUSE_THRESHOLD_TICKS: u64 = 150;

let delta = ns * CPU_KHZ_DEFAULT / NSECS_PER_SEC;
let mut pause_delta = 0;
unsafe {
Expand Down
Loading