rv64: add jh7110/starfive visionfive2 support

This commit is contained in:
Mark Poliakov 2025-01-21 16:34:03 +02:00
parent 16f580e7af
commit 909980f4eb
24 changed files with 532 additions and 65 deletions

1
Cargo.lock generated
View File

@ -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",

View 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 = .);
};

View File

@ -14,6 +14,7 @@
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"code-model": "large",
"eh-frame-header": false,
"crt-objects-fallback": "false",

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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()
}))
}
}
}

View File

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

View File

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

View File

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

View File

@ -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)?),

View File

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

View File

@ -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)]

View File

@ -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)) => {