rv64: add jh7110/starfive visionfive2 support
This commit is contained in:
parent
16f580e7af
commit
909980f4eb
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1024,6 +1024,7 @@ name = "kernel-arch-riscv64"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
|
58
etc/ld/riscv/riscv64-unknown-jh7110.ld
Normal file
58
etc/ld/riscv/riscv64-unknown-jh7110.ld
Normal file
@ -0,0 +1,58 @@
|
||||
ENTRY(__rv64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x40200000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
@ -14,6 +14,7 @@
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"code-model": "large",
|
||||
"eh-frame-header": false,
|
||||
|
||||
"crt-objects-fallback": "false",
|
||||
|
@ -95,5 +95,8 @@ fb_console = []
|
||||
aarch64_board_virt = ["kernel-arch-aarch64/aarch64_board_virt"]
|
||||
aarch64_board_raspi4b = ["kernel-arch-aarch64/aarch64_board_raspi4b"]
|
||||
|
||||
riscv64_board_virt = ["kernel-arch-riscv64/riscv64_board_virt"]
|
||||
riscv64_board_jh7110 = ["kernel-arch-riscv64/riscv64_board_jh7110"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -14,6 +14,13 @@ tock-registers.workspace = true
|
||||
bitflags.workspace = true
|
||||
static_assertions.workspace = true
|
||||
log.workspace = true
|
||||
cfg-if.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
riscv64_board_virt = []
|
||||
riscv64_board_jh7110 = []
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -150,6 +150,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
unsafe fn enter(&self) -> ! {
|
||||
unsafe {
|
||||
self.load_state();
|
||||
mem::tlb_flush_full();
|
||||
__rv64_enter_task(self.inner.get())
|
||||
}
|
||||
}
|
||||
@ -162,6 +163,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
unsafe {
|
||||
from.store_state();
|
||||
self.load_state();
|
||||
mem::tlb_flush_full();
|
||||
__rv64_switch_task(self.inner.get(), from.inner.get())
|
||||
}
|
||||
}
|
||||
@ -169,6 +171,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
unsafe fn switch_and_drop(&self, thread: *const ()) {
|
||||
unsafe {
|
||||
self.load_state();
|
||||
mem::tlb_flush_full();
|
||||
__rv64_switch_task_and_drop(self.inner.get(), thread)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use cfg_if::cfg_if;
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{page_align_down, page_index, EntryLevel, EntryLevelExt},
|
||||
table::{page_index, EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use memtables::riscv64::PageAttributes;
|
||||
use static_assertions::{const_assert, const_assert_eq};
|
||||
@ -30,14 +31,22 @@ split_spinlock! {
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "riscv64_board_virt")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
} else if #[cfg(feature = "riscv64_board_jh7110")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x40200000;
|
||||
} else if #[cfg(rust_analyzer)] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
}
|
||||
}
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80000000;
|
||||
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
|
||||
|
||||
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const_assert_eq!(KERNEL_START_L1I, 450);
|
||||
const_assert_eq!(KERNEL_L2I, 0);
|
||||
const_assert_eq!(KERNEL_L2I, 1);
|
||||
|
||||
// Runtime mappings
|
||||
// 1GiB of device memory space
|
||||
@ -120,8 +129,8 @@ unsafe fn map_device_memory_l3(
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] =
|
||||
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::W);
|
||||
}
|
||||
tlb_flush_va(DEVICE_MAPPING_OFFSET + l2i * L2::SIZE + l3i * L3::SIZE);
|
||||
}
|
||||
tlb_flush_full();
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
|
||||
}
|
||||
@ -148,9 +157,10 @@ unsafe fn map_device_memory_l2(
|
||||
for j in 0..count {
|
||||
DEVICE_MAPPING_L2[i + j] =
|
||||
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::W);
|
||||
// tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + (i + j) * L2::SIZE);
|
||||
// tlb_flush_va(DEVICE_MAPPING_OFFSET + (i + j) * L2::SIZE);
|
||||
}
|
||||
}
|
||||
tlb_flush_full();
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
|
||||
}
|
||||
@ -212,7 +222,7 @@ pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTabl
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
// tlb_flush_vaae1(page);
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
L2::SIZE => todo!(),
|
||||
@ -236,7 +246,7 @@ pub fn auto_address<T>(x: *const T) -> usize {
|
||||
/// Only meant to be called once per each HART during their early init.
|
||||
pub unsafe fn enable_mmu() {
|
||||
let l1_phys = auto_address(&raw const KERNEL_TABLES) as u64;
|
||||
|
||||
tlb_flush_full();
|
||||
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
|
||||
}
|
||||
|
||||
@ -249,7 +259,7 @@ pub unsafe fn unmap_lower_half() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
|
||||
tables.l1.data[kernel_l1i_lower] = 0;
|
||||
tlb_flush_va(page_align_down::<L1>(KERNEL_PHYS_BASE));
|
||||
tlb_flush_full();
|
||||
}
|
||||
|
||||
/// Sets up run-time kernel translation tables.
|
||||
@ -276,15 +286,25 @@ pub unsafe fn setup_fixed_tables() {
|
||||
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
tables.l1.data[DEVICE_MAPPING_L1I] =
|
||||
((device_mapping_l2_phys as u64) >> 2) | PageAttributes::V.bits();
|
||||
// tlb_flush_vaae1(DEVICE_MAPPING_OFFSET);
|
||||
|
||||
for l1i in 0..RAM_MAPPING_L1_COUNT {
|
||||
let physical = (l1i as u64) << L1::SHIFT;
|
||||
tables.l1.data[l1i + RAM_MAPPING_START_L1I] =
|
||||
(physical >> 2) | (PageAttributes::R | PageAttributes::W | PageAttributes::V).bits();
|
||||
tables.l1.data[l1i + RAM_MAPPING_START_L1I] = (physical >> 2)
|
||||
| (PageAttributes::R
|
||||
| PageAttributes::W
|
||||
| PageAttributes::A
|
||||
| PageAttributes::D
|
||||
| PageAttributes::V)
|
||||
.bits();
|
||||
}
|
||||
|
||||
// tlb_flush_all()
|
||||
tlb_flush_full();
|
||||
}
|
||||
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe {
|
||||
core::arch::asm!("sfence.vma");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
|
@ -130,7 +130,13 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
// TODO validate address alignment
|
||||
Self(
|
||||
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
|
||||
(address.into_u64() >> 2)
|
||||
| (PageAttributes::R
|
||||
| PageAttributes::A
|
||||
| PageAttributes::D
|
||||
| PageAttributes::V
|
||||
| attrs)
|
||||
.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
@ -156,7 +162,13 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
impl PageEntry<L3> {
|
||||
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
|
||||
(address.into_u64() >> 2)
|
||||
| (PageAttributes::R
|
||||
| PageAttributes::A
|
||||
| PageAttributes::D
|
||||
| PageAttributes::V
|
||||
| attrs)
|
||||
.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
@ -105,7 +105,11 @@ impl Node {
|
||||
let inner = self.device.or_init_with_opt(|| {
|
||||
let compatible = self.compatible?;
|
||||
let drivers = DRIVERS.read();
|
||||
let driver = drivers.iter().find(|d| d.matches(compatible))?;
|
||||
let driver = drivers.iter().find(|d| d.matches(compatible));
|
||||
if driver.is_none() {
|
||||
// log::warn!("No driver for {compatible:?}");
|
||||
}
|
||||
let driver = driver?;
|
||||
|
||||
let device = driver.imp.probe(&self, &cx);
|
||||
|
||||
@ -210,7 +214,11 @@ impl Node {
|
||||
/// * `Err(Error::DoesNotExist)` - couldn't find a device/driver for this node.
|
||||
/// * `Err(other)` - initialization failed.
|
||||
pub fn force_init(self: Arc<Self>) -> Result<(), Error> {
|
||||
let device = self.clone().probe().ok_or(Error::DoesNotExist)?;
|
||||
let device = self
|
||||
.clone()
|
||||
.probe()
|
||||
.ok_or(Error::DoesNotExist)
|
||||
.inspect_err(|_| log::error!("Does not exist: probe({:?})", self.name))?;
|
||||
|
||||
self.init_token.try_init_with_opt(|| {
|
||||
unsafe { device.init() }?;
|
||||
|
@ -6,13 +6,41 @@
|
||||
.endm
|
||||
|
||||
.pushsection .text.entry
|
||||
.option norvc
|
||||
|
||||
.option push
|
||||
.option rvc
|
||||
|
||||
.global __rv64_entry
|
||||
.global __boot_header
|
||||
.global __rv64_secondary_entry
|
||||
|
||||
.type __rv64_entry, @function
|
||||
.type __boot_header, @object
|
||||
__boot_header:
|
||||
__rv64_entry:
|
||||
// Look <s>ma</s>u-boot, I'm Linux!
|
||||
.ascii "MZ" // Magic 0
|
||||
j __rv64_real_entry // Jump to real entry (if entered by non-Linux bootloader)
|
||||
.long 0
|
||||
.quad 0x200000 // Offset from RAM start
|
||||
.quad 2000000 // Image size TODO fill this by post-tooling
|
||||
.quad 0 // Kernel flags
|
||||
.long 0x2 // Header version
|
||||
.long 0
|
||||
.quad 0
|
||||
.ascii "RISCV\x00\x00\x00" // Magic 1
|
||||
.ascii "RSC\x05" // Magic 2
|
||||
.long 0
|
||||
.size __rv64_entry, . - __rv64_entry
|
||||
.size __boot_header, . - __boot_header
|
||||
|
||||
.option pop
|
||||
|
||||
.option push
|
||||
.option norvc
|
||||
|
||||
.type __rv64_real_entry, @function
|
||||
__rv64_real_entry:
|
||||
// a0 - bootstrap HART ID
|
||||
// a1 - device tree blob
|
||||
// mhartid == a0
|
||||
@ -34,7 +62,7 @@ __rv64_entry:
|
||||
LOAD_PCREL .L03, t0, {entry_smode_lower} - {kernel_virt_offset}
|
||||
|
||||
jr t0
|
||||
.size __rv64_entry, . - __rv64_entry
|
||||
.size __rv64_real_entry, . - __rv64_real_entry
|
||||
|
||||
.type __rv64_secondary_entry, @function
|
||||
__rv64_secondary_entry:
|
||||
@ -50,4 +78,5 @@ __rv64_secondary_entry:
|
||||
jr t0
|
||||
.size __rv64_secondary_entry, . - __rv64_secondary_entry
|
||||
|
||||
.option pop
|
||||
.popsection
|
||||
|
@ -1,11 +1,16 @@
|
||||
use core::arch::global_asm;
|
||||
|
||||
use abi::{arch::SavedFrame, primitive_enum, process::Signal, SyscallFunction};
|
||||
use abi::{
|
||||
arch::SavedFrame,
|
||||
primitive_enum,
|
||||
process::{ExitCode, Signal},
|
||||
SyscallFunction,
|
||||
};
|
||||
use kernel_arch::task::TaskFrame;
|
||||
use libk::{device::external_interrupt_controller, task::thread::Thread};
|
||||
use tock_registers::interfaces::ReadWriteable;
|
||||
|
||||
use kernel_arch_riscv64::registers::STVEC;
|
||||
use kernel_arch_riscv64::{mem, registers::STVEC};
|
||||
|
||||
use crate::syscall;
|
||||
|
||||
@ -55,7 +60,8 @@ pub fn init_smode_exceptions() {
|
||||
let address = (&raw const __rv64_smode_trap_vectors).addr();
|
||||
|
||||
STVEC.set_base(address);
|
||||
STVEC.modify(STVEC::MODE::Vectored);
|
||||
STVEC.modify(STVEC::MODE::Direct);
|
||||
// STVEC.modify(STVEC::MODE::Vectored);
|
||||
}
|
||||
|
||||
unsafe fn umode_exception_handler(frame: &mut TrapFrame) {
|
||||
@ -63,28 +69,15 @@ unsafe fn umode_exception_handler(frame: &mut TrapFrame) {
|
||||
|
||||
let cause = Cause::try_from(frame.scause).ok();
|
||||
|
||||
let dump = match cause {
|
||||
let (dump, dump_tval) = match cause {
|
||||
Some(Cause::LoadPageFault)
|
||||
| Some(Cause::StorePageFault)
|
||||
| Some(Cause::LoadAccessFault)
|
||||
| Some(Cause::StoreAccessFault)
|
||||
| Some(Cause::InstructionPageFault)
|
||||
| Some(Cause::InstructionAccessFault) => {
|
||||
let translation = if let Some(space) = thread.try_get_process().map(|p| p.space()) {
|
||||
space.translate(frame.stval as usize).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
|
||||
if let Some(physical) = translation {
|
||||
log::warn!(" * tval translates to {physical:#x}");
|
||||
} else {
|
||||
log::warn!(" * tval does not translate");
|
||||
}
|
||||
|
||||
true
|
||||
(true, true)
|
||||
}
|
||||
Some(Cause::EcallUmode) => {
|
||||
let func = frame.an[0];
|
||||
@ -94,17 +87,19 @@ unsafe fn umode_exception_handler(frame: &mut TrapFrame) {
|
||||
|
||||
let args = &frame.an[1..];
|
||||
let result = syscall::raw_syscall_handler(func, args) as _;
|
||||
mem::tlb_flush_full();
|
||||
frame.an[0] = result;
|
||||
frame.sepc += 4;
|
||||
false
|
||||
(false, false)
|
||||
}
|
||||
_ => {
|
||||
thread.raise_signal(Signal::MemoryAccessViolation);
|
||||
true
|
||||
(true, false)
|
||||
}
|
||||
};
|
||||
|
||||
if dump {
|
||||
let process = thread.process();
|
||||
log::warn!(
|
||||
"U-mode exception cause={:?} ({}), epc={:#x}, sp={:#x}, tval={:#x}",
|
||||
cause,
|
||||
@ -113,6 +108,20 @@ unsafe fn umode_exception_handler(frame: &mut TrapFrame) {
|
||||
frame.sp,
|
||||
frame.stval
|
||||
);
|
||||
log::warn!("In thread {} ({:?})", thread.id, *thread.name.read());
|
||||
log::warn!("Of process {} ({:?})", process.id, process.name);
|
||||
|
||||
if dump_tval {
|
||||
let translation = process.space().translate(frame.stval as usize).ok();
|
||||
|
||||
if let Some(physical) = translation {
|
||||
log::warn!(" * tval translates to {physical:#x}");
|
||||
} else {
|
||||
log::warn!(" * tval does not translate");
|
||||
}
|
||||
}
|
||||
|
||||
thread.exit_process(ExitCode::BySignal(Ok(Signal::MemoryAccessViolation)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +178,7 @@ unsafe extern "C" fn smode_interrupt_handler(frame: *mut TrapFrame) {
|
||||
intc.handle_pending_irqs();
|
||||
}
|
||||
}
|
||||
n => todo!("Unhandled interrupt #{n}"),
|
||||
n => log::warn!("Unhandled interrupt #{n}"),
|
||||
}
|
||||
|
||||
if !smode && let Some(thread) = Thread::get_current() {
|
||||
@ -192,6 +201,7 @@ unsafe extern "C" fn smode_general_trap_handler(frame: *mut TrapFrame) {
|
||||
if !smode && let Some(thread) = Thread::get_current() {
|
||||
thread.handle_pending_signals(frame);
|
||||
}
|
||||
mem::tlb_flush_full();
|
||||
}
|
||||
|
||||
impl TaskFrame for TrapFrame {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
use core::sync::atomic::{self, AtomicU32, Ordering};
|
||||
use core::sync::atomic::{self, AtomicBool, AtomicU32, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
@ -44,11 +44,13 @@ pub static BOOT_HART_ID: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
pub struct Riscv64 {
|
||||
dt: OneTimeInit<DeviceTree<'static>>,
|
||||
initrd: OneTimeInit<PhysicalRef<'static, [u8]>>,
|
||||
smp: AtomicBool,
|
||||
}
|
||||
|
||||
pub static PLATFORM: Riscv64 = Riscv64 {
|
||||
dt: OneTimeInit::new(),
|
||||
initrd: OneTimeInit::new(),
|
||||
smp: AtomicBool::new(true),
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -67,14 +69,22 @@ impl Platform for Riscv64 {
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<bool, Error> {
|
||||
smp::send_ipi(target, msg)?;
|
||||
Ok(true)
|
||||
if self.smp.load(Ordering::Acquire) {
|
||||
smp::send_ipi(target, msg)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn start_application_processors(&self) {
|
||||
let dt = self.dt.get();
|
||||
if let Err(error) = smp::start_secondary_harts(dt) {
|
||||
log::error!("Couldn't start secondary harts: {error:?}");
|
||||
// TODO asymmetric systems with different hart types are not yet supported.
|
||||
// e.g., in JH7110 there're two different types of cores
|
||||
if self.smp.load(Ordering::Acquire) {
|
||||
let dt = self.dt.get();
|
||||
if let Err(error) = smp::start_secondary_harts(dt) {
|
||||
log::error!("Couldn't start secondary harts: {error:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,16 +204,22 @@ impl Riscv64 {
|
||||
// Create device tree sysfs nodes
|
||||
device_tree::util::create_sysfs_nodes(dt);
|
||||
|
||||
let (_, machine_name) = Self::machine_name(dt);
|
||||
let (machine_compatible, machine_name) = Self::machine_name(dt);
|
||||
|
||||
unflatten_device_tree(dt);
|
||||
|
||||
if let Some(machine_compatible) = machine_compatible {
|
||||
Self::apply_board_workarounds(machine_compatible);
|
||||
}
|
||||
|
||||
if let Err(error) = Self::setup_clock_timebase(dt) {
|
||||
log::error!("Could not setup clock timebase from device tree: {error:?}");
|
||||
}
|
||||
Self::setup_chosen_stdout(dt).ok();
|
||||
|
||||
libk::debug::disable_early_sinks();
|
||||
if let Err(error) = Self::setup_chosen_stdout(dt) {
|
||||
log::error!("chosen-stdout setup error: {error:?}");
|
||||
} else {
|
||||
libk::debug::disable_early_sinks();
|
||||
}
|
||||
|
||||
if let Some(machine) = machine_name {
|
||||
log::info!("Running on {machine:?}");
|
||||
@ -241,6 +257,17 @@ impl Riscv64 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_board_workarounds(compatible: &str) {
|
||||
#[allow(clippy::single_match)]
|
||||
match compatible {
|
||||
"starfive,visionfive-2-v1.3b" => {
|
||||
log::warn!("VisionFive 2: disabling SMP, OS doesn't yet handle the HARTs properly");
|
||||
PLATFORM.smp.store(false, Ordering::Release);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn machine_name(dt: &'static DeviceTree) -> (Option<&'static str>, Option<&'static str>) {
|
||||
(
|
||||
dt.root().prop_string("compatible"),
|
||||
@ -254,6 +281,7 @@ impl Riscv64 {
|
||||
.prop_cell("timebase-frequency", 1)
|
||||
.ok_or(Error::DoesNotExist)?;
|
||||
timer::FREQUENCY.store(timebase_frequency, Ordering::Release);
|
||||
log::info!("System timer frequency: {timebase_frequency}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -266,6 +294,7 @@ impl Riscv64 {
|
||||
let node = stdout_path.and_then(device_tree::driver::find_node);
|
||||
|
||||
if let Some(node) = node {
|
||||
log::info!("Probe chosen stdout: {:?}", node.name());
|
||||
node.force_init()?;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ pub fn init_hart(is_bsp: bool) {
|
||||
if is_bsp {
|
||||
LAST_TICK.store(now, Ordering::Release);
|
||||
}
|
||||
sbi::sbi_set_timer(now + frequency / TICK_RATE);
|
||||
sbi::sbi_set_timer(now.wrapping_add(frequency / TICK_RATE));
|
||||
} else {
|
||||
SIE.modify(SIE::STIE::CLEAR);
|
||||
sbi::sbi_set_timer(u64::MAX);
|
||||
|
@ -82,7 +82,7 @@ struct Context {
|
||||
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
|
||||
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
|
||||
// TODO scale the table depending on effective MAX_IRQS value
|
||||
table: IrqSafeRwLock<FixedInterruptTable<32>>,
|
||||
table: IrqSafeRwLock<FixedInterruptTable<64>>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
@ -112,12 +112,15 @@ impl Plic {
|
||||
|
||||
fn validate_irq(&self, irq: Irq) -> Result<u32, Error> {
|
||||
let Irq::External(irq) = irq else {
|
||||
log::error!("plic: irq {irq:?} is not an external interrupt");
|
||||
return Err(Error::InvalidArgument);
|
||||
};
|
||||
if irq == 0 {
|
||||
log::error!("plic: irq cannot be zero");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if irq as usize >= self.max_irqs {
|
||||
log::error!("plic: irq ({}) >= max_irqs ({})", irq, self.max_irqs);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
Ok(irq)
|
||||
@ -138,7 +141,8 @@ impl ExternalInterruptController for Plic {
|
||||
let bsp_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
|
||||
let context = self
|
||||
.hart_context(bsp_hart_id)
|
||||
.ok_or(Error::InvalidArgument)?
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
|
||||
.context
|
||||
.get();
|
||||
|
||||
@ -159,7 +163,8 @@ impl ExternalInterruptController for Plic {
|
||||
let irq = self.validate_irq(irq)?;
|
||||
let context = self
|
||||
.hart_context(bsp_hart_id)
|
||||
.ok_or(Error::InvalidArgument)?
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
|
||||
.context
|
||||
.get();
|
||||
let mut table = context.table.write();
|
||||
@ -277,7 +282,7 @@ fn map_context_to_hart(target: u32) -> Option<u32> {
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["sifive,plic-1.0.0", "riscv,plic0"],
|
||||
compatible: ["starfive,jh7110-plic", "sifive,plic-1.0.0", "riscv,plic0"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
@ -8,3 +8,5 @@ pub mod pl011;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod ns16550a;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod snps_dw_apb_uart;
|
||||
|
@ -109,6 +109,7 @@ impl Io {
|
||||
|
||||
impl Device for Ns16550a {
|
||||
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
||||
log::info!("Init ns16550a @ {:#x}", self.base);
|
||||
let mut io = Io {
|
||||
regs: DeviceMemoryIo::map(self.base, Default::default())?,
|
||||
};
|
||||
@ -191,6 +192,7 @@ device_tree_driver!(
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
log::debug!("ns16550a base = {base:#x}");
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
Some(Arc::new(Ns16550a {
|
||||
|
230
kernel/src/device/serial/snps_dw_apb_uart.rs
Normal file
230
kernel/src/device/serial/snps_dw_apb_uart.rs
Normal file
@ -0,0 +1,230 @@
|
||||
//! Synopsys DesignWare 8250 driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::Device,
|
||||
interrupt::{FullIrq, InterruptHandler},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
IER [
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
// Transmit buffer available
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
// Receive data available
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
// Data ready bit
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
// Transmitter holding register empty
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// DLAB=0, Write: transmitter holding register/Read: receiver buffer register
|
||||
// DLAB=1, Read/Write: divisor latch low
|
||||
(0x000 => DR: ReadWrite<u32>),
|
||||
// DLAB=0: Interrupt enable register
|
||||
// DLAB=1: Divisor latch high
|
||||
(0x004 => IER: ReadWrite<u32, IER::Register>),
|
||||
// Read: interrupt identification register/Write: frame control register
|
||||
(0x008 => IIR: ReadWrite<u32>),
|
||||
// Line control register
|
||||
(0x00C => LCR: ReadWrite<u32>),
|
||||
// Modem control register
|
||||
(0x010 => MCR: ReadWrite<u32>),
|
||||
// Line status register
|
||||
(0x014 => LSR: ReadOnly<u32, LSR::Register>),
|
||||
// Modem status register
|
||||
(0x018 => MSR: ReadOnly<u32>),
|
||||
// Scratchpad
|
||||
(0x01C => SCR: ReadWrite<u32>),
|
||||
// Low-power divisor latch low
|
||||
(0x020 => LPDLL: ReadWrite<u32>),
|
||||
// Low-power divisor latch high
|
||||
(0x024 => LPDLH: ReadWrite<u32>),
|
||||
(0x028 => _0),
|
||||
// Shadow receive/transmit buffer
|
||||
(0x030 => SDR: [ReadWrite<u32>; 16]),
|
||||
(0x070 => FAR: ReadWrite<u32>),
|
||||
(0x074 => TFR: ReadOnly<u32>),
|
||||
(0x078 => RFW: WriteOnly<u32>),
|
||||
(0x07C => USR: ReadOnly<u32>),
|
||||
(0x080 => TFL: ReadOnly<u32>),
|
||||
(0x084 => RFL: ReadOnly<u32>),
|
||||
(0x088 => SRR: WriteOnly<u32>),
|
||||
(0x08C => SRTS: ReadWrite<u32>),
|
||||
(0x090 => SBCR: ReadWrite<u32>),
|
||||
(0x094 => SDMAM: ReadWrite<u32>),
|
||||
(0x098 => SFE: ReadWrite<u32>),
|
||||
(0x09C => SRT: ReadWrite<u32>),
|
||||
(0x0A0 => STET: ReadWrite<u32>),
|
||||
(0x0A4 => HTX: ReadWrite<u32>),
|
||||
(0x0A8 => DMASA: WriteOnly<u32>),
|
||||
(0x0AC => _1),
|
||||
(0x0F4 => CPR: ReadOnly<u32>),
|
||||
(0x0F8 => UCV: ReadOnly<u32>),
|
||||
(0x0FC => CTR: ReadOnly<u32>),
|
||||
(0x100 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Io {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
io: IrqSafeSpinlock<Io>,
|
||||
}
|
||||
|
||||
/// Synopsys DesignWare 8250 UART
|
||||
pub struct DwUart {
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
fn send(&mut self, byte: u8) {
|
||||
// TODO
|
||||
if byte == b'\n' {
|
||||
self.send(b'\r');
|
||||
}
|
||||
|
||||
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.regs.IER.set(0);
|
||||
}
|
||||
|
||||
fn handle_irq(&mut self) -> Option<u8> {
|
||||
let status = self.regs.IIR.get();
|
||||
|
||||
if status & 0xF == 4 {
|
||||
Some(self.regs.DR.get() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for DwUart {
|
||||
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let output = inner.output();
|
||||
let byte = output.io.lock().handle_irq();
|
||||
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for DwUart {
|
||||
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
||||
let regs = DeviceMemoryIo::map(self.base, Default::default())?;
|
||||
let mut io = Io { regs };
|
||||
io.init();
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
io: IrqSafeSpinlock::new(io),
|
||||
};
|
||||
|
||||
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
|
||||
TerminalOptions::const_default(),
|
||||
input,
|
||||
output,
|
||||
)));
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, Default::default(), self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
|
||||
let output = self.inner.get().output();
|
||||
let io = output.io.lock();
|
||||
io.regs.IER.modify(IER::ERBFI::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Synopsys DesignWare 8250 UART"
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for DwUart {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["snps,dw-apb-uart"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
Some(Arc::new(DwUart {
|
||||
base,
|
||||
irq,
|
||||
inner: OneTimeInit::new()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
@ -73,6 +73,7 @@ pub unsafe fn enter() -> ! {
|
||||
AP_CAN_ENTER.signal();
|
||||
}
|
||||
|
||||
log::info!("Start queue_index={}", cpu.queue_index());
|
||||
let queue = CpuQueue::for_cpu(cpu.queue_index());
|
||||
cpu.set_scheduler(queue);
|
||||
|
||||
|
@ -32,7 +32,7 @@ const L3_SHIFT: usize = 12;
|
||||
const L3_PAGE_SIZE: usize = 1 << L3_SHIFT;
|
||||
|
||||
fn segment_attributes(f: u32) -> PageAttributes {
|
||||
let mut attrs = PageAttributes::R;
|
||||
let mut attrs = PageAttributes::R | PageAttributes::A | PageAttributes::D;
|
||||
if f & PF_W != 0 {
|
||||
attrs |= PageAttributes::W;
|
||||
}
|
||||
|
@ -152,9 +152,20 @@ impl<'e> CargoBuilder<'e> {
|
||||
"-Zunstable-options",
|
||||
]);
|
||||
|
||||
match env.board {
|
||||
Board::virt | Board::default => command.arg("--features=aarch64_board_virt"),
|
||||
Board::raspi4b => command.arg("--features=aarch64_board_raspi4b"),
|
||||
match (env.arch, env.board) {
|
||||
(Arch::aarch64, Board::virt | Board::default) => {
|
||||
command.arg("--features=aarch64_board_virt");
|
||||
}
|
||||
(Arch::aarch64, Board::raspi4b) => {
|
||||
command.arg("--features=aarch64_board_raspi4b");
|
||||
}
|
||||
(Arch::riscv64, Board::virt | Board::default) => {
|
||||
command.arg("--features=riscv64_board_virt");
|
||||
}
|
||||
(Arch::riscv64, Board::jh7110) => {
|
||||
command.arg("--features=riscv64_board_jh7110");
|
||||
}
|
||||
(_, _) => (),
|
||||
};
|
||||
|
||||
if env.profile == Profile::Release {
|
||||
|
@ -39,10 +39,11 @@ impl CheckAction {
|
||||
pub struct ToolsBuilt(pub PathBuf);
|
||||
pub struct KernelBuilt(pub PathBuf);
|
||||
pub struct KernelProcessed(pub KernelBuilt);
|
||||
pub struct KernelBin(pub PathBuf);
|
||||
pub struct InitrdGenerated(pub PathBuf);
|
||||
pub struct ImageBuilt(pub PathBuf);
|
||||
pub enum AllBuilt {
|
||||
Riscv64(KernelProcessed, InitrdGenerated),
|
||||
Riscv64(KernelBin, InitrdGenerated),
|
||||
X86_64(ImageBuilt),
|
||||
AArch64(KernelProcessed, InitrdGenerated),
|
||||
I686(ImageBuilt),
|
||||
@ -62,6 +63,26 @@ pub fn build_kernel(env: &BuildEnv, _: AllOk) -> Result<KernelBuilt, Error> {
|
||||
Ok(KernelBuilt(env.kernel_output_dir.join("yggdrasil-kernel")))
|
||||
}
|
||||
|
||||
pub fn make_kernel_bin(
|
||||
env: &BuildEnv,
|
||||
kernel: KernelProcessed,
|
||||
_: AllOk,
|
||||
) -> Result<KernelBin, Error> {
|
||||
log::info!("Building yggdrasil-kernel.bin");
|
||||
let kernel_bin = env.kernel_output_dir.join("yggdrasil-kernel.bin");
|
||||
|
||||
let status = Command::new("llvm-objcopy")
|
||||
.args(["-O", "binary"])
|
||||
.arg(kernel.0 .0)
|
||||
.arg(&kernel_bin)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err(Error::ExternalCommandFailed);
|
||||
}
|
||||
|
||||
Ok(KernelBin(kernel_bin))
|
||||
}
|
||||
|
||||
pub fn generate_kernel_tables(
|
||||
symbol_path: impl AsRef<Path>,
|
||||
kernel: KernelBuilt,
|
||||
@ -105,7 +126,7 @@ pub fn build_all(env: &BuildEnv) -> Result<AllBuilt, Error> {
|
||||
|
||||
// Build target-specific image
|
||||
let image = match env.arch {
|
||||
Arch::riscv64 => AllBuilt::Riscv64(kernel, initrd),
|
||||
Arch::riscv64 => AllBuilt::Riscv64(make_kernel_bin(env, kernel, check)?, initrd),
|
||||
Arch::aarch64 => AllBuilt::AArch64(kernel, initrd),
|
||||
Arch::x86_64 => AllBuilt::X86_64(x86_64::build_image(env, kernel, initrd)?),
|
||||
Arch::i686 => AllBuilt::I686(i686::build_image(env, kernel, initrd)?),
|
||||
|
@ -81,9 +81,14 @@ pub enum Board {
|
||||
#[default]
|
||||
default,
|
||||
|
||||
// Generic aarch64/riscv board
|
||||
virt,
|
||||
|
||||
// AArch64 boards
|
||||
raspi4b,
|
||||
virt,
|
||||
|
||||
// RISC-V boards
|
||||
jh7110,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -136,6 +141,7 @@ impl BuildEnv {
|
||||
let kernel_triple = match (arch, board) {
|
||||
(Arch::aarch64, Board::virt | Board::default) => "aarch64-unknown-qemu",
|
||||
(Arch::riscv64, Board::virt | Board::default) => "riscv64-unknown-qemu",
|
||||
(Arch::riscv64, Board::jh7110) => "riscv64-unknown-jh7110",
|
||||
(Arch::aarch64, Board::raspi4b) => "aarch64-unknown-raspi4b",
|
||||
(Arch::x86_64, Board::default) => "x86_64-unknown-none",
|
||||
(Arch::i686, Board::default) => "i686-unknown-none",
|
||||
@ -223,7 +229,7 @@ impl Profile {
|
||||
|
||||
impl Arch {
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
[Self::aarch64, Self::x86_64, Self::i686].into_iter()
|
||||
[Self::aarch64, Self::x86_64, Self::i686, Self::riscv64].into_iter()
|
||||
}
|
||||
|
||||
pub fn user_triple(&self) -> &str {
|
||||
|
@ -4,6 +4,8 @@ use std::{
|
||||
process::{Command, ExitStatusError},
|
||||
};
|
||||
|
||||
use crate::env::Board;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("{0}")]
|
||||
@ -47,6 +49,9 @@ pub enum Error {
|
||||
CompilerRtConfigFailed(CommandFailed),
|
||||
#[error("compiler-rt build failed: {0}")]
|
||||
CompilerRtBuildFailed(CommandFailed),
|
||||
|
||||
#[error("No qemu support for board {0:?}")]
|
||||
UnsupportedEmulation(Board),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -11,7 +11,7 @@ use qemu::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
build::{self, AllBuilt, ImageBuilt, InitrdGenerated, KernelBuilt, KernelProcessed},
|
||||
build::{self, AllBuilt, ImageBuilt, InitrdGenerated, KernelBin, KernelBuilt, KernelProcessed},
|
||||
env::{Board, BuildEnv},
|
||||
error::Error,
|
||||
util::run_external_command,
|
||||
@ -177,6 +177,9 @@ fn run_aarch64(
|
||||
.with_serial(QemuSerialTarget::MonStdio)
|
||||
.with_cpu(aarch64::Cpu::Max)
|
||||
.with_memory_megabytes(config.machine.aarch64.memory),
|
||||
_ => {
|
||||
return Err(Error::UnsupportedEmulation(env.board));
|
||||
}
|
||||
};
|
||||
|
||||
if env.board != Board::raspi4b {
|
||||
@ -354,7 +357,7 @@ pub fn run(
|
||||
add_devices_from_config(&mut devices, disk.as_ref(), &config)?;
|
||||
|
||||
let mut command = match built {
|
||||
AllBuilt::Riscv64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => {
|
||||
AllBuilt::Riscv64(KernelBin(kernel), InitrdGenerated(initrd)) => {
|
||||
run_riscv64(&config, &env, qemu, devices, kernel, initrd)?
|
||||
}
|
||||
AllBuilt::AArch64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user