From 909980f4ebf49a4bef8b7be9ce7bcf17b7d9b9f7 Mon Sep 17 00:00:00 2001 From: Mark Poliakov <mark@alnyan.me> Date: Tue, 21 Jan 2025 16:34:03 +0200 Subject: [PATCH] rv64: add jh7110/starfive visionfive2 support --- Cargo.lock | 1 + etc/ld/riscv/riscv64-unknown-jh7110.ld | 58 +++++ etc/riscv64-unknown-none.json | 1 + kernel/Cargo.toml | 3 + kernel/arch/riscv64/Cargo.toml | 7 + kernel/arch/riscv64/src/context.rs | 3 + kernel/arch/riscv64/src/mem/mod.rs | 46 ++-- kernel/arch/riscv64/src/mem/table.rs | 16 +- kernel/lib/device-tree/src/driver/tree.rs | 12 +- kernel/src/arch/riscv64/boot/entry.S | 33 ++- kernel/src/arch/riscv64/exception.rs | 52 +++-- kernel/src/arch/riscv64/mod.rs | 49 +++- kernel/src/arch/riscv64/timer.rs | 2 +- kernel/src/device/interrupt/riscv_plic.rs | 13 +- kernel/src/device/serial/mod.rs | 2 + kernel/src/device/serial/ns16550a.rs | 2 + kernel/src/device/serial/snps_dw_apb_uart.rs | 230 +++++++++++++++++++ kernel/src/task/mod.rs | 1 + kernel/tools/gentables/src/riscv64.rs | 2 +- xtask/src/build/cargo.rs | 17 +- xtask/src/build/mod.rs | 25 +- xtask/src/env.rs | 10 +- xtask/src/error.rs | 5 + xtask/src/qemu.rs | 7 +- 24 files changed, 532 insertions(+), 65 deletions(-) create mode 100644 etc/ld/riscv/riscv64-unknown-jh7110.ld create mode 100644 kernel/src/device/serial/snps_dw_apb_uart.rs diff --git a/Cargo.lock b/Cargo.lock index b4beeaa3..e81d7701 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/etc/ld/riscv/riscv64-unknown-jh7110.ld b/etc/ld/riscv/riscv64-unknown-jh7110.ld new file mode 100644 index 00000000..771efc0d --- /dev/null +++ b/etc/ld/riscv/riscv64-unknown-jh7110.ld @@ -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 = .); +}; diff --git a/etc/riscv64-unknown-none.json b/etc/riscv64-unknown-none.json index f8d0cec4..5a8d4d19 100644 --- a/etc/riscv64-unknown-none.json +++ b/etc/riscv64-unknown-none.json @@ -14,6 +14,7 @@ "panic-strategy": "abort", "dynamic-linking": true, "relocation-model": "pic", + "code-model": "large", "eh-frame-header": false, "crt-objects-fallback": "false", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 95ad7efc..ce2cbc59 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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 diff --git a/kernel/arch/riscv64/Cargo.toml b/kernel/arch/riscv64/Cargo.toml index c78f8c87..11c51c16 100644 --- a/kernel/arch/riscv64/Cargo.toml +++ b/kernel/arch/riscv64/Cargo.toml @@ -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 diff --git a/kernel/arch/riscv64/src/context.rs b/kernel/arch/riscv64/src/context.rs index 0b900a47..9761df87 100644 --- a/kernel/arch/riscv64/src/context.rs +++ b/kernel/arch/riscv64/src/context.rs @@ -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) } } diff --git a/kernel/arch/riscv64/src/mem/mod.rs b/kernel/arch/riscv64/src/mem/mod.rs index 26217a01..101a0557 100644 --- a/kernel/arch/riscv64/src/mem/mod.rs +++ b/kernel/arch/riscv64/src/mem/mod.rs @@ -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) { diff --git a/kernel/arch/riscv64/src/mem/table.rs b/kernel/arch/riscv64/src/mem/table.rs index 8a807441..1be91b4a 100644 --- a/kernel/arch/riscv64/src/mem/table.rs +++ b/kernel/arch/riscv64/src/mem/table.rs @@ -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, ) } diff --git a/kernel/lib/device-tree/src/driver/tree.rs b/kernel/lib/device-tree/src/driver/tree.rs index 34c9b78a..8e2103c5 100644 --- a/kernel/lib/device-tree/src/driver/tree.rs +++ b/kernel/lib/device-tree/src/driver/tree.rs @@ -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() }?; diff --git a/kernel/src/arch/riscv64/boot/entry.S b/kernel/src/arch/riscv64/boot/entry.S index 40246a1c..8150d5a1 100644 --- a/kernel/src/arch/riscv64/boot/entry.S +++ b/kernel/src/arch/riscv64/boot/entry.S @@ -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 diff --git a/kernel/src/arch/riscv64/exception.rs b/kernel/src/arch/riscv64/exception.rs index c88f619d..f5ab2d47 100644 --- a/kernel/src/arch/riscv64/exception.rs +++ b/kernel/src/arch/riscv64/exception.rs @@ -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 { diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index e297d5f5..8ae36b0d 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -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()?; } diff --git a/kernel/src/arch/riscv64/timer.rs b/kernel/src/arch/riscv64/timer.rs index 0e508c27..4d4cf07c 100644 --- a/kernel/src/arch/riscv64/timer.rs +++ b/kernel/src/arch/riscv64/timer.rs @@ -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); diff --git a/kernel/src/device/interrupt/riscv_plic.rs b/kernel/src/device/interrupt/riscv_plic.rs index b5e2a2cd..a67f5cf0 100644 --- a/kernel/src/device/interrupt/riscv_plic.rs +++ b/kernel/src/device/interrupt/riscv_plic.rs @@ -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)?; diff --git a/kernel/src/device/serial/mod.rs b/kernel/src/device/serial/mod.rs index 34ef1f06..e9d3db13 100644 --- a/kernel/src/device/serial/mod.rs +++ b/kernel/src/device/serial/mod.rs @@ -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; diff --git a/kernel/src/device/serial/ns16550a.rs b/kernel/src/device/serial/ns16550a.rs index b7feee8d..1e7ce1fd 100644 --- a/kernel/src/device/serial/ns16550a.rs +++ b/kernel/src/device/serial/ns16550a.rs @@ -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 { diff --git a/kernel/src/device/serial/snps_dw_apb_uart.rs b/kernel/src/device/serial/snps_dw_apb_uart.rs new file mode 100644 index 00000000..4951124e --- /dev/null +++ b/kernel/src/device/serial/snps_dw_apb_uart.rs @@ -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() + })) + } + } +} diff --git a/kernel/src/task/mod.rs b/kernel/src/task/mod.rs index 5145dd63..3beeefbf 100644 --- a/kernel/src/task/mod.rs +++ b/kernel/src/task/mod.rs @@ -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); diff --git a/kernel/tools/gentables/src/riscv64.rs b/kernel/tools/gentables/src/riscv64.rs index a6fa185f..2c4bdafd 100644 --- a/kernel/tools/gentables/src/riscv64.rs +++ b/kernel/tools/gentables/src/riscv64.rs @@ -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; } diff --git a/xtask/src/build/cargo.rs b/xtask/src/build/cargo.rs index bc964525..9e12c0c2 100644 --- a/xtask/src/build/cargo.rs +++ b/xtask/src/build/cargo.rs @@ -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 { diff --git a/xtask/src/build/mod.rs b/xtask/src/build/mod.rs index 1ad8a4f0..fd8f9a6e 100644 --- a/xtask/src/build/mod.rs +++ b/xtask/src/build/mod.rs @@ -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)?), diff --git a/xtask/src/env.rs b/xtask/src/env.rs index 49831a92..74b7e510 100644 --- a/xtask/src/env.rs +++ b/xtask/src/env.rs @@ -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 { diff --git a/xtask/src/error.rs b/xtask/src/error.rs index f2685386..307847a6 100644 --- a/xtask/src/error.rs +++ b/xtask/src/error.rs @@ -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)] diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index ce4fadcd..8e6fa1cb 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -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)) => {