From 1a406ae2043baf0f357f1fba6e7479367b57579f Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 17 Mar 2023 11:08:06 +0000 Subject: [PATCH 01/10] efi, fdt: Mark the size of the FDT table as reserved This prevents memory allocations from overwriting the table and making it appear corrupted. Signed-off-by: Rob Bradford --- src/bootinfo.rs | 4 ++-- src/efi/mod.rs | 13 +++++++++++-- src/fdt.rs | 14 +++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/bootinfo.rs b/src/bootinfo.rs index fbadfbf7..d942deb8 100644 --- a/src/bootinfo.rs +++ b/src/bootinfo.rs @@ -11,8 +11,8 @@ pub trait Info { fn rsdp_addr(&self) -> Option { None } - // Address of FDT to use for booting if present - fn fdt_addr(&self) -> Option { + // Address/size of FDT used for booting + fn fdt_reservation(&self) -> Option { None } // The kernel command line (not including null terminator) diff --git a/src/efi/mod.rs b/src/efi/mod.rs index e14181e1..eba1c6e5 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -957,6 +957,15 @@ fn populate_allocator(info: &dyn bootinfo::Info, image_address: u64, image_size: ); } + if let Some(fdt_entry) = info.fdt_reservation() { + ALLOCATOR.borrow_mut().allocate_pages( + efi::ALLOCATE_ADDRESS, + efi::UNUSABLE_MEMORY, + (fdt_entry.size + 4095) / 4096, + fdt_entry.addr, + ); + } + // Add the loaded binary ALLOCATOR.borrow_mut().allocate_pages( efi::ALLOCATE_ADDRESS, @@ -1078,7 +1087,7 @@ pub fn efi_exec( let mut ct_index = 0; // Populate with FDT table if present - if let Some(fdt_addr) = info.fdt_addr() { + if let Some(fdt_entry) = info.fdt_reservation() { ct[ct_index] = efi::ConfigurationTable { vendor_guid: Guid::from_fields( 0xb1b621d5, @@ -1088,7 +1097,7 @@ pub fn efi_exec( 0x0b, &[0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0], ), - vendor_table: fdt_addr as *const u64 as *mut _, + vendor_table: fdt_entry.addr as *const u64 as *mut _, }; ct_index += 1; } diff --git a/src/fdt.rs b/src/fdt.rs index 0284761b..b823c555 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -10,7 +10,7 @@ use crate::{ pub struct StartInfo<'a> { acpi_rsdp_addr: Option, - fdt_addr: u64, + fdt_entry: MemoryEntry, fdt: Fdt<'a>, kernel_load_addr: u64, memory_layout: &'static [MemoryDescriptor], @@ -32,10 +32,14 @@ impl StartInfo<'_> { } }; - let fdt_addr = ptr as u64; + let fdt_entry = MemoryEntry { + addr: ptr as u64, + size: fdt.total_size() as u64, + entry_type: EntryType::Reserved, + }; Self { - fdt_addr, + fdt_entry, fdt, acpi_rsdp_addr, kernel_load_addr, @@ -62,8 +66,8 @@ impl Info for StartInfo<'_> { self.acpi_rsdp_addr } - fn fdt_addr(&self) -> Option { - Some(self.fdt_addr) + fn fdt_reservation(&self) -> Option { + Some(self.fdt_entry) } fn cmdline(&self) -> &[u8] { From b361372ec545c77a7c0e3fbd607fd0ba475de9fd Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 13:53:31 +0000 Subject: [PATCH 02/10] rtc: riscv: Add RTC support via goldfish RTC This is the the RTC device included with the QEMU virt machine type. For now the MMIO range is hardcoded but in the future it would be helpful to pull this from the FDT (this is tracked with a TODO.) Signed-off-by: Rob Bradford --- Cargo.toml | 3 +++ src/main.rs | 2 ++ src/rtc.rs | 3 +++ src/rtc_goldfish.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 src/rtc_goldfish.rs diff --git a/Cargo.toml b/Cargo.toml index 6c42ccfb..b2c464a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,9 @@ 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 } + [dev-dependencies] dirs = "5.0.0" rand = "0.8.5" diff --git a/src/main.rs b/src/main.rs index 8480d8d4..6e569444 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,6 +57,8 @@ mod pe; #[cfg(target_arch = "x86_64")] mod pvh; mod rtc; +#[cfg(target_arch = "riscv64")] +mod rtc_goldfish; #[cfg(target_arch = "aarch64")] mod rtc_pl031; #[cfg(target_arch = "aarch64")] diff --git a/src/rtc.rs b/src/rtc.rs index 3bb63967..61791b5e 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -6,3 +6,6 @@ pub use crate::rtc_pl031::{read_date, read_time}; #[cfg(target_arch = "x86_64")] pub use crate::cmos::{read_date, read_time}; + +#[cfg(target_arch = "riscv64")] +pub use crate::rtc_goldfish::{read_date, read_time}; diff --git a/src/rtc_goldfish.rs b/src/rtc_goldfish.rs new file mode 100644 index 00000000..b6b00851 --- /dev/null +++ b/src/rtc_goldfish.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Rivos Inc. + +use crate::mem::MemoryRegion; +use atomic_refcell::AtomicRefCell; +use chrono::{DateTime, Datelike, NaiveDateTime, Timelike, Utc}; + +// TODO: Fill from FDT +const RTC_GOLDFISH_ADDRESS: u64 = 0x101000; +static RTC_GOLDFISH: AtomicRefCell = + AtomicRefCell::new(RtcGoldfish::new(RTC_GOLDFISH_ADDRESS)); + +pub struct RtcGoldfish { + region: MemoryRegion, +} + +impl RtcGoldfish { + pub const fn new(base: u64) -> RtcGoldfish { + RtcGoldfish { + region: MemoryRegion::new(base, 8), + } + } + + fn read_ts(&self) -> u64 { + const NSECS_PER_SEC: u64 = 1_000_000_000; + + let low = u64::from(self.region.io_read_u32(0x0)); + let high = u64::from(self.region.io_read_u32(0x04)); + + let t = high << 32 | low; + t / NSECS_PER_SEC + } +} + +pub fn read_date() -> Result<(u8, u8, u8), ()> { + let ts = RTC_GOLDFISH.borrow_mut().read_ts(); + + let naive = NaiveDateTime::from_timestamp_opt(ts as i64, 0).ok_or(())?; + let datetime: DateTime = DateTime::from_utc(naive, Utc); + Ok(( + (datetime.year() - 2000) as u8, + datetime.month() as u8, + datetime.day() as u8, + )) +} + +pub fn read_time() -> Result<(u8, u8, u8), ()> { + let ts = RTC_GOLDFISH.borrow_mut().read_ts(); + let naive = NaiveDateTime::from_timestamp_opt(ts as i64, 0).ok_or(())?; + let datetime: DateTime = DateTime::from_utc(naive, Utc); + Ok(( + datetime.hour() as u8, + datetime.minute() as u8, + datetime.second() as u8, + )) +} From 961144594ebb0e6993bf32c71a826bbb29a6b9a6 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 13:56:01 +0000 Subject: [PATCH 03/10] delay: riscv: Add implementation of ndelay() This implementation uses a harcoded frequency that matches that from the QEMU virt platform. Signed-off-by: Rob Bradford --- src/delay.rs | 20 ++++++++++++++++---- src/main.rs | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 086c9b38..5b505bab 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -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 { @@ -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 { @@ -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 { diff --git a/src/main.rs b/src/main.rs index 6e569444..82e97e72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,8 +14,9 @@ #![feature(asm_const)] #![feature(alloc_error_handler)] -#![feature(stmt_expr_attributes)] #![feature(slice_take)] +#![feature(stdsimd)] +#![feature(stmt_expr_attributes)] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(unused_imports, dead_code))] From b98cf443e96edcdf2aa0d5eccaf412d7cf397b49 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 13:57:44 +0000 Subject: [PATCH 04/10] efi: riscv: Add EFI default boot path for the RV64 target This matches the one specified in EDKII Signed-off-by: Rob Bradford --- src/efi/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/efi/mod.rs b/src/efi/mod.rs index eba1c6e5..18180ff4 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -50,6 +50,8 @@ use var::VariableAllocator; pub const EFI_BOOT_PATH: &str = "\\EFI\\BOOT\\BOOTAA64.EFI"; #[cfg(target_arch = "x86_64")] pub const EFI_BOOT_PATH: &str = "\\EFI\\BOOT\\BOOTX64.EFI"; +#[cfg(target_arch = "riscv64")] +pub const EFI_BOOT_PATH: &str = "\\EFI\\BOOT\\BOOTRISCV64.EFI"; #[derive(Copy, Clone, PartialEq, Eq)] enum HandleType { From f3883e61bb6876df0f99bc576d7a8e9546a169aa Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 13:59:55 +0000 Subject: [PATCH 05/10] pe: riscv: Enable PE32+ loader support Signed-off-by: Rob Bradford --- src/pe.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pe.rs b/src/pe.rs index 699bcc44..3bc79f44 100644 --- a/src/pe.rs +++ b/src/pe.rs @@ -43,7 +43,14 @@ impl<'a> Loader<'a> { #[cfg(target_arch = "x86_64")] const MACHINE_TYPE: u16 = 0x8664; - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] + #[cfg(target_arch = "riscv64")] + const MACHINE_TYPE: u16 = 0x5064; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64", + target_arch = "riscv64" + ))] const OPTIONAL_HEADER_MAGIC: u16 = 0x20b; // PE32+ pub fn new(file: &'a mut dyn crate::fat::Read) -> Loader { From c2b9539d39457f2561250e6e30d132fa210f718d Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 14:01:40 +0000 Subject: [PATCH 06/10] efi: riscv: Add platform specific EFI Boot Protocol implementation This is required for booting Linux. Signed-off-by: Rob Bradford --- src/efi/mod.rs | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/efi/mod.rs b/src/efi/mod.rs index 18180ff4..eb245cfd 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -34,6 +34,9 @@ use r_efi::{ system::{ConfigurationTable, RuntimeServices}, }; +#[cfg(target_arch = "riscv64")] +use r_efi::{eficall, eficall_abi}; + use crate::bootinfo; use crate::layout; use crate::rtc; @@ -856,11 +859,45 @@ pub extern "efiapi" fn locate_handle_buffer( Status::UNSUPPORTED } +#[cfg(target_arch = "riscv64")] +#[repr(C)] +struct RiscVBootProtocol { + revision: u64, + get_boot_hart_id: eficall! {fn(*const RiscVBootProtocol, *mut u64) -> Status }, +} + +#[cfg(target_arch = "riscv64")] +extern "efiapi" fn get_boot_hart_id(_: *const RiscVBootProtocol, hart: *mut u64) -> Status { + unsafe { *hart = 0 }; + Status::SUCCESS +} + +#[cfg(target_arch = "riscv64")] +const RISC_V_BOOT_PROTOCOL: RiscVBootProtocol = RiscVBootProtocol { + revision: 0, + get_boot_hart_id, +}; + +#[cfg(target_arch = "riscv64")] +pub const RISV_V_BOOT_PROTOCOL_GUID: Guid = Guid::from_fields( + 0xccd15fec, + 0x6f73, + 0x4eec, + 0x83, + 0x95, + &[0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf], +); + pub extern "efiapi" fn locate_protocol( - _: *mut Guid, + _guid: *mut Guid, _: *mut c_void, - _: *mut *mut c_void, + _out: *mut *mut c_void, ) -> Status { + #[cfg(target_arch = "riscv64")] + if unsafe { *_guid } == RISV_V_BOOT_PROTOCOL_GUID { + unsafe { *_out = &RISC_V_BOOT_PROTOCOL as *const RiscVBootProtocol as *mut c_void }; + return Status::SUCCESS; + } // XXX: A recent version of Linux kernel fails to boot if EFI_UNSUPPORTED returned. Status::NOT_FOUND } From f9b0a53a7f112c93bfa19a4eb867f7f0be1f3bcb Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 14:28:06 +0000 Subject: [PATCH 07/10] serial: riscv: Add basic MMIO UART device This device doe not fully implement a UART driver but provides sufficient support to enable the log!() macro and EFI stdout support when running on the the QEMU virt platform. Signed-off-by: Rob Bradford --- src/main.rs | 2 ++ src/serial.rs | 9 +++++++++ src/uart_mmio.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 src/uart_mmio.rs diff --git a/src/main.rs b/src/main.rs index 82e97e72..e20470d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,8 @@ mod rtc; mod rtc_goldfish; #[cfg(target_arch = "aarch64")] mod rtc_pl031; +#[cfg(target_arch = "riscv64")] +mod uart_mmio; #[cfg(target_arch = "aarch64")] mod uart_pl011; mod virtio; diff --git a/src/serial.rs b/src/serial.rs index 38f875c2..8cb46cbb 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -25,6 +25,9 @@ use crate::{arch::aarch64::layout::map, uart_pl011::Pl011 as UartPl011}; #[cfg(target_arch = "x86_64")] use uart_16550::SerialPort as Uart16550; +#[cfg(target_arch = "riscv64")] +use crate::uart_mmio::UartMmio; + // We use COM1 as it is the standard first serial port. #[cfg(target_arch = "x86_64")] pub static PORT: AtomicRefCell = AtomicRefCell::new(unsafe { Uart16550::new(0x3f8) }); @@ -33,6 +36,12 @@ pub static PORT: AtomicRefCell = AtomicRefCell::new(unsafe { Uart1655 pub static PORT: AtomicRefCell = AtomicRefCell::new(UartPl011::new(map::mmio::PL011_START)); +// TODO: Fill from FDT? +#[cfg(target_arch = "riscv64")] +const SERIAL_PORT_ADDRESS: u64 = 0x1000_0000; +#[cfg(target_arch = "riscv64")] +pub static PORT: AtomicRefCell = AtomicRefCell::new(UartMmio::new(SERIAL_PORT_ADDRESS)); + pub struct Serial; impl fmt::Write for Serial { fn write_str(&mut self, s: &str) -> fmt::Result { diff --git a/src/uart_mmio.rs b/src/uart_mmio.rs new file mode 100644 index 00000000..a9ce21ca --- /dev/null +++ b/src/uart_mmio.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Rivos Inc. + +use crate::mem::MemoryRegion; +use core::fmt; + +pub struct UartMmio { + region: MemoryRegion, +} + +impl UartMmio { + pub const fn new(base: u64) -> UartMmio { + UartMmio { + region: MemoryRegion::new(base, 8), + } + } + + fn send(&mut self, byte: u8) { + self.region.io_write_u8(0, byte) + } + + pub fn init(&mut self) {} +} + +impl fmt::Write for UartMmio { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + self.send(byte); + } + Ok(()) + } +} From 76203c7f7ae2c708e260694ca3bd726892065f84 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 14:43:30 +0000 Subject: [PATCH 08/10] misc: riscv: Add support for starting on QEMU virt machine Add linker and compilation scripts that produce a binary that can be used with QEMU's RV64 virt machine type with the produced ELF binary loaded by OpenSBI. Signed-off-by: Rob Bradford --- Cargo.toml | 1 + riscv64gcv-unknown-none-elf.json | 21 ++++++++++++ riscv64gcv-unknown-none-elf.ld | 46 +++++++++++++++++++++++++ src/arch/mod.rs | 3 ++ src/arch/riscv64/asm.rs | 6 ++++ src/arch/riscv64/layout.rs | 58 ++++++++++++++++++++++++++++++++ src/arch/riscv64/mod.rs | 5 +++ src/arch/riscv64/ram64.s | 24 +++++++++++++ src/main.rs | 39 ++++++++++++++++++++- 9 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 riscv64gcv-unknown-none-elf.json create mode 100644 riscv64gcv-unknown-none-elf.ld create mode 100644 src/arch/riscv64/asm.rs create mode 100644 src/arch/riscv64/layout.rs create mode 100644 src/arch/riscv64/mod.rs create mode 100644 src/arch/riscv64/ram64.s diff --git a/Cargo.toml b/Cargo.toml index b2c464a9..d8f21e99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ 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" diff --git a/riscv64gcv-unknown-none-elf.json b/riscv64gcv-unknown-none-elf.json new file mode 100644 index 00000000..de0dabd5 --- /dev/null +++ b/riscv64gcv-unknown-none-elf.json @@ -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"] + } +} diff --git a/riscv64gcv-unknown-none-elf.ld b/riscv64gcv-unknown-none-elf.ld new file mode 100644 index 00000000..b00bc5e4 --- /dev/null +++ b/riscv64gcv-unknown-none-elf.ld @@ -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) + } +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index f98be70b..f3cc2fcd 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -6,3 +6,6 @@ pub mod aarch64; #[cfg(target_arch = "x86_64")] pub mod x86_64; + +#[cfg(target_arch = "riscv64")] +pub mod riscv64; diff --git a/src/arch/riscv64/asm.rs b/src/arch/riscv64/asm.rs new file mode 100644 index 00000000..797b84c2 --- /dev/null +++ b/src/arch/riscv64/asm.rs @@ -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")); diff --git a/src/arch/riscv64/layout.rs b/src/arch/riscv64/layout.rs new file mode 100644 index 00000000..68f9e908 --- /dev/null +++ b/src/arch/riscv64/layout.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo +// Copyright (c) 2021-2022 Andre Richter +// 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 { + unsafe { (code_start.get() as _)..(code_end.get() as _) } +} + +pub fn data_range() -> Range { + unsafe { (data_start.get() as _)..(data_end.get() as _) } +} + +pub fn stack_range() -> Range { + unsafe { (stack_start.get() as _)..(stack_end.get() as _) } +} + +pub fn reserved_range() -> Range { + 0x8000_0000..0x8020_0000 +} + +const NUM_MEM_DESCS: usize = 4; + +pub static MEM_LAYOUT: MemoryLayout = [ + 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, + }, +]; diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs new file mode 100644 index 00000000..60751062 --- /dev/null +++ b/src/arch/riscv64/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2023 Rivos Inc. + +pub mod asm; +pub mod layout; diff --git a/src/arch/riscv64/ram64.s b/src/arch/riscv64/ram64.s new file mode 100644 index 00000000..91bdafd2 --- /dev/null +++ b/src/arch/riscv64/ram64.s @@ -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 diff --git a/src/main.rs b/src/main.rs index e20470d5..f2b6fe4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ mod coreboot; mod delay; mod efi; mod fat; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] mod fdt; #[cfg(all(test, feature = "integration_tests"))] mod integration; @@ -188,6 +188,43 @@ pub extern "C" fn rust64_start(x0: *const u8) -> ! { main(&info) } +#[cfg(target_arch = "riscv64")] +#[no_mangle] +pub extern "C" fn rust64_start(a0: u64, a1: *const u8) -> ! { + use crate::bootinfo::{EntryType, Info, MemoryEntry}; + + serial::PORT.borrow_mut().init(); + + log!("Starting on RV64 0x{:x} 0x{:x}", a0, a1 as u64,); + + let info = fdt::StartInfo::new( + a1, + None, + 0x8040_0000, + &crate::arch::riscv64::layout::MEM_LAYOUT[..], + Some(MemoryEntry { + addr: 0x4000_0000, + size: 2 << 20, + entry_type: EntryType::Reserved, + }), + ); + + for i in 0..info.num_entries() { + let region = info.entry(i); + log!( + "Memory region {}MiB@0x{:x}", + region.size / 1024 / 1024, + region.addr + ); + } + + if let Some((base, length)) = info.find_compatible_region(&["pci-host-ecam-generic"]) { + pci::init(base as u64, length as u64); + } + + main(&info); +} + fn main(info: &dyn bootinfo::Info) -> ! { log!("\nBooting with {}", info.name()); From 4b4097052c49008ab63e227eb7a684aa6bcb82b6 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 14:56:23 +0000 Subject: [PATCH 09/10] README: riscv: Add documentation for new platform Signed-off-by: Rob Bradford --- README.md | 65 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 18b7613d..65952570 100644 --- a/README.md +++ b/README.md @@ -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 @@ -44,7 +34,23 @@ 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. @@ -52,7 +58,7 @@ 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) @@ -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. @@ -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 From 0920f5d67a2df2cef5615bf704a62e3da494ffc5 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 20 Mar 2023 15:09:03 +0000 Subject: [PATCH 10/10] build: riscv64: Add cross toolchain build testing Signed-off-by: Rob Bradford --- .github/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6162625f..0d69b9c1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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