diff --git a/.gitignore b/.gitignore index 9d91ac5720..305250feec 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ actual.out qemu.log rusty-tags.vi +.idea diff --git a/Cargo.lock b/Cargo.lock index b305bb4329..44fd07c805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -546,7 +546,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.29", + "syn 2.0.39", "which", ] @@ -746,7 +746,7 @@ version = "0.1.1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -854,7 +854,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1363,7 +1363,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1407,7 +1407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1436,9 +1436,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1681,7 +1681,7 @@ checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1783,9 +1783,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -1809,7 +1809,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1866,7 +1866,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] [[package]] @@ -1889,10 +1889,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtio-drivers" -version = "0.4.0" -source = "git+https://github.com/rcore-os/virtio-drivers.git?rev=409ee72#409ee723c92adf309e825a7b87f53049707ed306" +version = "0.7.1" +source = "git+https://github.com/rcore-os/virtio-drivers.git?rev=4b60f5d#4b60f5d341a7211dfec7382062f965c5433c51c2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "log", "zerocopy", ] @@ -1964,7 +1964,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -1986,7 +1986,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2171,9 +2171,9 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.6.3" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b9c234616391070b0b173963ebc65a9195068e7ed3731c6edac2ec45ebe106" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" dependencies = [ "byteorder", "zerocopy-derive", @@ -2181,11 +2181,11 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.6.3" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7f3a471f98d0a61c34322fbbfd10c384b07687f680d4119813713f72308d91" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.39", ] diff --git a/Makefile b/Makefile index b351596584..54b69cafdc 100644 --- a/Makefile +++ b/Makefile @@ -103,8 +103,13 @@ else ifeq ($(ARCH), aarch64) ACCEL ?= n PLATFORM_NAME ?= aarch64-qemu-virt TARGET := aarch64-unknown-none-softfloat +else ifeq ($(ARCH), loongarch64) + ACCEL ?= n + PLATFORM_NAME ?= loongarch64-qemu-virt + TARGET := loongarch64-unknown-none + BUS := pci else - $(error "ARCH" must be one of "x86_64", "riscv64", or "aarch64") + $(error "ARCH" must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") endif export AX_ARCH=$(ARCH) @@ -118,6 +123,9 @@ export AX_GW=$(GW) # Binutils CROSS_COMPILE ?= $(ARCH)-linux-musl- +ifeq ($(ARCH), loongarch64) + CROSS_COMPILE := $(ARCH)-unknown-linux-gnu- +endif CC := $(CROSS_COMPILE)gcc AR := $(CROSS_COMPILE)ar RANLIB := $(CROSS_COMPILE)ranlib diff --git a/apps/exception/expect_debug_loongarch64.out b/apps/exception/expect_debug_loongarch64.out new file mode 100644 index 0000000000..12039d63d7 --- /dev/null +++ b/apps/exception/expect_debug_loongarch64.out @@ -0,0 +1,19 @@ +smp = 1 +build_mode = release +log_level = debug + +Primary CPU 0 started, +Found physcial memory regions: + .text (READ | EXECUTE | RESERVED) + .rodata (READ | RESERVED) + .data .tdata .tbss .percpu (READ | WRITE | RESERVED) + .percpu (READ | WRITE | RESERVED) + boot stack (READ | WRITE | RESERVED) + .bss (READ | WRITE | RESERVED) + free memory (READ | WRITE | FREE) +Initialize platform devices... +Primary CPU 0 init OK. +Running exception tests... +Exception(Breakpoint) @ 0x[0-9a-f]\{16\} +Exception tests run OK! +Shutting down... diff --git a/apps/exception/src/main.rs b/apps/exception/src/main.rs index e196cabd0a..7ceea54298 100644 --- a/apps/exception/src/main.rs +++ b/apps/exception/src/main.rs @@ -15,6 +15,8 @@ fn raise_break_exception() { asm!("brk #0"); #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] asm!("ebreak"); + #[cfg(any(target_arch = "loongarch64"))] + asm!("break 0"); } } diff --git a/crates/kernel_guard/src/arch/loongarch64.rs b/crates/kernel_guard/src/arch/loongarch64.rs new file mode 100644 index 0000000000..7df5c57160 --- /dev/null +++ b/crates/kernel_guard/src/arch/loongarch64.rs @@ -0,0 +1,18 @@ +use core::arch::asm; + +/// Bit 2: Supervisor Interrupt Enable +const IE_BIT: usize = 1 << 2; + +#[inline] +pub fn local_irq_save_and_disable() -> usize { + let mut flags: usize = 0; + // clear the `IE` bit, and return the old CSR + unsafe { asm!("csrxchg {}, {}, 0x0", inout(reg) flags, in(reg) IE_BIT) }; + flags & IE_BIT +} + +#[inline] +pub fn local_irq_restore(mut flags: usize) { + // restore the `IE` bit + unsafe { asm!("csrxchg {}, {}, 0x0", inout(reg) flags, in(reg) IE_BIT) }; +} diff --git a/crates/kernel_guard/src/arch/mod.rs b/crates/kernel_guard/src/arch/mod.rs index 3a05149726..88b4ba46a1 100644 --- a/crates/kernel_guard/src/arch/mod.rs +++ b/crates/kernel_guard/src/arch/mod.rs @@ -10,5 +10,8 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")] { mod aarch64; pub use self::aarch64::*; + } else if #[cfg(target_arch = "loongarch64")] { + mod loongarch64; + pub use self::loongarch64::*; } } diff --git a/crates/page_table/src/arch/loongarch64.rs b/crates/page_table/src/arch/loongarch64.rs new file mode 100644 index 0000000000..a1f6e01d89 --- /dev/null +++ b/crates/page_table/src/arch/loongarch64.rs @@ -0,0 +1,17 @@ +//! LoongArch64 specific page table structures. + +use crate::{PageTable64, PagingMetaData}; + +use page_table_entry::loongarch64::LA64PTE; +/// Metadata of LoongArch64 page tables. +#[derive(Copy, Clone, Debug)] +pub struct LA64MetaData; + +impl const PagingMetaData for LA64MetaData { + const LEVELS: usize = 4; + const PA_MAX_BITS: usize = 48; + const VA_MAX_BITS: usize = 48; +} + +/// Page table for LoongArch64 systems. +pub type LA64PageTable = PageTable64; diff --git a/crates/page_table/src/arch/mod.rs b/crates/page_table/src/arch/mod.rs index 13fff18b94..1925eabff1 100644 --- a/crates/page_table/src/arch/mod.rs +++ b/crates/page_table/src/arch/mod.rs @@ -6,3 +6,6 @@ pub mod riscv; #[cfg(any(target_arch = "aarch64", doc))] pub mod aarch64; + +#[cfg(any(target_arch = "loongarch64", doc))] +pub mod loongarch64; diff --git a/crates/page_table_entry/src/arch/loongarch64.rs b/crates/page_table_entry/src/arch/loongarch64.rs new file mode 100644 index 0000000000..e13de3120b --- /dev/null +++ b/crates/page_table_entry/src/arch/loongarch64.rs @@ -0,0 +1,165 @@ +//! loongarch64 page table entries. + +use crate::{GenericPTE, MappingFlags}; +use core::fmt; +use memory_addr::PhysAddr; + +bitflags::bitflags! { + /// Page-table entry flags. + #[derive(Debug)] + pub struct PTEFlags: usize { + /// Whether the PTE is valid. + const V = 1 << 0; + /// Indicates the virtual page has been written since the last time the + /// D bit was cleared. + const D = 1 << 1; + /// Privilege Level with 2 bits. + const PLVL = 1 << 2; + /// Privilege Level with 2 bits. + const PLVH = 1 << 3; + /// Memory Access Type controls the type of access, such as whether it + /// can be cached by Cache, etc. + const MATL = 1 << 4; + /// Memory Access Type controls the type of access, such as whether it + /// can be cached by Cache, etc. + const MATH = 1 << 5; + /// Designates a global mapping OR Whether the page is huge page. + const GH = 1 << 6; + /// Whether the physical page is exist. + const P = 1 << 7; + /// Whether the page is writable. + const W = 1 << 8; + /// Designates a global mapping when using huge page. + const G = 1 << 12; + /// Whether the page is not readable. + const NR = 1 << 61; + /// Whether the page is not executable. + const NX = 1 << 62; + /// Whether the privilege Level is restricted. When RPLV is 0, the PTE + /// can be accessed by any program with privilege Level higher than PLV. + const RPLV = 1 << 63; + } +} + +impl From for MappingFlags { + fn from(f: PTEFlags) -> Self { + let mut ret = Self::empty(); + if !f.contains(PTEFlags::NR) { + ret |= Self::READ; + } + if f.contains(PTEFlags::W) { + ret |= Self::WRITE; + } + if !f.contains(PTEFlags::NX) { + ret |= Self::EXECUTE; + } + if f.contains(PTEFlags::PLVL | PTEFlags::PLVH) { + ret |= Self::USER; + } + if f.contains(PTEFlags::MATH) { + // MAT = 2 (Weakly-ordered UnCached) -> UNCACHED + ret |= Self::UNCACHED; + } else if !f.contains(PTEFlags::MATL) { + // MAT = 0 (Strongly-ordered UnCached) -> DEVICE + ret |= Self::DEVICE; + } + ret + } +} + +impl From for PTEFlags { + fn from(f: MappingFlags) -> Self { + if f.is_empty() { + return Self::empty(); + } + let mut ret = Self::V; + if !f.contains(MappingFlags::READ) { + ret |= Self::NR; + } + if f.contains(MappingFlags::WRITE) { + ret |= Self::W; + } + if !f.contains(MappingFlags::EXECUTE) { + ret |= Self::NX; + } + if f.contains(MappingFlags::USER) { + ret |= Self::PLVH | Self::PLVL; + } + if !f.contains(MappingFlags::DEVICE) { + if f.contains(MappingFlags::UNCACHED) { + // MAT = 2 (WUC) + ret |= Self::MATH + } else { + // MAT = 1 (Coherent Cached) + ret |= Self::MATL + } + } // else, MAT = 0 (SUC) + ret + } +} + +/// page table entry for LoongArch64 systems. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct LA64PTE(u64); + +impl LA64PTE { + const PHYS_ADDR_MASK: u64 = 0x0000_ffff_ffff_f000; // bits 12..48 +} + +impl GenericPTE for LA64PTE { + fn new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) -> Self { + let mut flags = PTEFlags::from(flags); + if is_huge { + flags |= PTEFlags::GH; + } + Self(flags.bits() as u64 | ((paddr.as_usize()) as u64 & Self::PHYS_ADDR_MASK)) + } + fn new_table(paddr: PhysAddr) -> Self { + Self(PTEFlags::V.bits() as u64 | ((paddr.as_usize()) as u64 & Self::PHYS_ADDR_MASK)) + } + fn paddr(&self) -> PhysAddr { + PhysAddr::from((self.0 & Self::PHYS_ADDR_MASK) as usize) + } + fn flags(&self) -> MappingFlags { + PTEFlags::from_bits_truncate(self.0 as usize).into() + } + + fn set_paddr(&mut self, paddr: PhysAddr) { + self.0 = (self.0 & !Self::PHYS_ADDR_MASK) | (paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) + } + + fn set_flags(&mut self, flags: MappingFlags, is_huge: bool) { + let mut flags = PTEFlags::from(flags); + if is_huge { + flags |= PTEFlags::GH; + } + self.0 = (self.0 & Self::PHYS_ADDR_MASK) | flags.bits() as u64; + } + + fn is_unused(&self) -> bool { + self.0 == 0 + } + fn is_present(&self) -> bool { + PTEFlags::from_bits_truncate(self.0 as usize).contains(PTEFlags::V) + } + fn is_huge(&self) -> bool { + PTEFlags::from_bits_truncate(self.0 as usize).contains(PTEFlags::GH) + } + fn clear(&mut self) { + self.0 = 0 + } +} + +impl fmt::Debug for LA64PTE { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_struct("LA64PTE"); + f.field("raw", &self.0) + .field("paddr", &self.paddr()) + .field("flags", &self.flags()) + .field("is_unused", &self.is_unused()) + .field("is_present", &self.is_present()) + .field("is_huge", &self.is_huge()) + .finish() + } +} diff --git a/crates/page_table_entry/src/arch/mod.rs b/crates/page_table_entry/src/arch/mod.rs index 9491f95395..93353d8330 100644 --- a/crates/page_table_entry/src/arch/mod.rs +++ b/crates/page_table_entry/src/arch/mod.rs @@ -7,3 +7,6 @@ pub mod riscv; // TODO: `#[cfg(any(target_arch = "aarch64", doc))]` does not work. #[doc(cfg(target_arch = "aarch64"))] pub mod aarch64; + +#[doc(cfg(target_arch = "loongarch64"))] +pub mod loongarch64; diff --git a/crates/percpu/src/imp.rs b/crates/percpu/src/imp.rs index 322867b717..28a130cbc7 100644 --- a/crates/percpu/src/imp.rs +++ b/crates/percpu/src/imp.rs @@ -74,6 +74,8 @@ pub fn get_local_thread_pointer() -> usize { core::arch::asm!("mv {}, gp", out(reg) tp) } else if #[cfg(target_arch = "aarch64")] { core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) tp) + } else if #[cfg(target_arch = "loongarch64")] { + core::arch::asm!("move {}, $r21", out(reg) tp) } } } @@ -108,6 +110,8 @@ pub fn set_local_thread_pointer(cpu_id: usize) { core::arch::asm!("mv gp, {}", in(reg) tp) } else if #[cfg(target_arch = "aarch64")] { core::arch::asm!("msr TPIDR_EL1, {}", in(reg) tp) + } else if #[cfg(target_arch = "loongarch64")] { + core::arch::asm!("move $r21, {}", in(reg) tp) } } } diff --git a/crates/percpu_macros/src/arch.rs b/crates/percpu_macros/src/arch.rs index 6d77eb6812..cd0e79e6c6 100644 --- a/crates/percpu_macros/src/arch.rs +++ b/crates/percpu_macros/src/arch.rs @@ -33,6 +33,12 @@ pub fn gen_offset(symbol: &Ident) -> proc_macro2::TokenStream { out(reg) value, VAR = sym #symbol, ); + #[cfg(any(target_arch = "loongarch64"))] + ::core::arch::asm!( + "la.abs {0}, {VAR}", + out(reg) value, + VAR = sym #symbol, + ); } value } @@ -58,6 +64,8 @@ pub fn gen_current_ptr(symbol: &Ident, ty: &Type) -> proc_macro2::TokenStream { ::core::arch::asm!("mrs {}, TPIDR_EL1", out(reg) base); #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] ::core::arch::asm!("mv {}, gp", out(reg) base); + #[cfg(any(target_arch = "loongarch64"))] + ::core::arch::asm!("move {}, $r21", out(reg) base); (base + self.offset()) as *const #ty } }) diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index f29ecde07d..f1d9051ecd 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -54,5 +54,8 @@ arm_gic = { path = "../../crates/arm_gic" } arm_pl011 = { path = "../../crates/arm_pl011" } dw_apb_uart = { path = "../../crates/dw_apb_uart" } +[target.'cfg(target_arch = "loongarch64")'.dependencies] +loongarch64 = { git = "https://github.com/Godones/loongArch64" } + [build-dependencies] axconfig = { path = "../axconfig" } diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index 73b7a7884d..97bb9fb0fc 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -64,7 +64,7 @@ SECTIONS . = ALIGN(4K); _edata = .; - .bss : ALIGN(4K) { + .bss : AT(.) ALIGN(4K) { boot_stack = .; *(.bss.stack) . = ALIGN(4K); diff --git a/modules/axhal/src/arch/loongarch64/context.rs b/modules/axhal/src/arch/loongarch64/context.rs new file mode 100644 index 0000000000..04117930a2 --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/context.rs @@ -0,0 +1,100 @@ +use core::arch::asm; +use memory_addr::VirtAddr; + +/// Saved registers when a trap (interrupt or exception) occurs. +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct TrapFrame { + /// All general registers. + pub regs: [usize; 32], + /// Pre-exception Mode Information + pub prmd: usize, + /// Exception Return Address + pub era: usize, +} + +/// Saved hardware states of a task. +/// +/// The context usually includes: +/// +/// - Callee-saved registers +/// - Stack pointer register +/// - Thread pointer register (for thread-local storage, currently unsupported) +/// - FP/SIMD registers +/// +/// On context switch, current task saves its context from CPU to memory, +/// and the next task restores its context from memory to CPU. +#[allow(missing_docs)] +#[repr(C)] +#[derive(Debug, Default)] +pub struct TaskContext { + pub ra: usize, // return address + pub sp: usize, // stack pointer + pub s: [usize; 10], // loongArch need to save 10 static registers from $r22 to $r31 + pub tp: usize, +} + +impl TaskContext { + /// Creates a new default context for a new task. + pub const fn new() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// Initializes the context for a new task, with the given entry point and + /// kernel stack. + pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { + self.sp = kstack_top.as_usize(); + self.ra = entry; + self.tp = tls_area.as_usize(); + } + + /// Switches to another task. + /// + /// It first saves the current task's context from CPU to this place, and then + /// restores the next task's context from `next_ctx` to CPU. + pub fn switch_to(&mut self, next_ctx: &Self) { + #[cfg(feature = "tls")] + { + self.tp = super::read_thread_pointer(); + unsafe { super::write_thread_pointer(next_ctx.tp) }; + } + unsafe { context_switch(self, next_ctx) } + } +} + +#[naked] +unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { + asm!( + " + // save old context (callee-saved registers) + st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + + // restore new context + ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + + ret", + options(noreturn), + ) +} diff --git a/modules/axhal/src/arch/loongarch64/mod.rs b/modules/axhal/src/arch/loongarch64/mod.rs new file mode 100644 index 0000000000..a0d4594799 --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/mod.rs @@ -0,0 +1,99 @@ +#[macro_use] +mod context; +mod trap; + +use core::arch::asm; +use loongarch64::register::{crmd, ecfg, eentry, pgd}; +use memory_addr::{PhysAddr, VirtAddr}; + +pub use self::context::{TaskContext, TrapFrame}; + +/// Allows the current CPU to respond to interrupts. +#[inline] +pub fn enable_irqs() { + crmd::set_ie(true) +} + +/// Makes the current CPU to ignore interrupts. +#[inline] +pub fn disable_irqs() { + crmd::set_ie(false) +} + +/// Returns whether the current CPU is allowed to respond to interrupts. +#[inline] +pub fn irqs_enabled() -> bool { + crmd::read().ie() +} + +/// Relaxes the current CPU and waits for interrupts. +/// +/// It must be called with interrupts enabled, otherwise it will never return. +#[inline] +pub fn wait_for_irqs() { + unsafe { loongarch64::asm::idle() } +} + +/// Halt the current CPU. +#[inline] +pub fn halt() { + disable_irqs(); + unsafe { loongarch64::asm::idle() } // should never return +} + +/// Reads the register that stores the current page table root. +/// +/// Returns the physical address of the page table root. +#[inline] +pub fn read_page_table_root() -> PhysAddr { + PhysAddr::from(pgd::read().base()) +} + +/// Writes the register to update the current page table root. +/// +/// # Safety +/// +/// This function is unsafe as it changes the virtual memory address space. +pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { + let old_root = read_page_table_root(); + trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + trace!("loongarch64 don't need to set page table"); +} + +/// Flushes the TLB. +/// +/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB +/// entry that maps the given virtual address. +#[inline] +pub fn flush_tlb(_vaddr: Option) { + trace!("loongarch64 don't need to flush tlb now"); +} + +/// Writes Exception Entry Base Address Register (`eentry`). +#[inline] +pub fn set_trap_vector_base(eentry: usize) { + ecfg::set_vs(0); + eentry::set_eentry(eentry); +} + +/// Reads the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +#[inline] +pub fn read_thread_pointer() -> usize { + let tp; + unsafe { asm!("move {}, $tp", out(reg) tp) }; + tp +} + +/// Writes the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +/// +/// # Safety +/// +/// This function is unsafe as it changes the CPU states. +#[inline] +pub unsafe fn write_thread_pointer(tp: usize) { + asm!("move $tp, {}", in(reg) tp) +} diff --git a/modules/axhal/src/arch/loongarch64/trap.S b/modules/axhal/src/arch/loongarch64/trap.S new file mode 100644 index 0000000000..f474c62e9a --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/trap.S @@ -0,0 +1,89 @@ +.section .text +.balign 4096 +.global trap_vector_base +trap_vector_base: + addi.d $sp, $sp, -{trapframe_size} // allocate space + // save the registers. + st.d $ra, $sp, 8 + st.d $tp, $sp, 16 + st.d $sp, $sp, 24 + st.d $a0, $sp, 32 + st.d $a1, $sp, 40 + st.d $a2, $sp, 48 + st.d $a3, $sp, 56 + st.d $a4, $sp, 64 + st.d $a5, $sp, 72 + st.d $a6, $sp, 80 + st.d $a7, $sp, 88 + st.d $t0, $sp, 96 + st.d $t1, $sp, 104 + st.d $t2, $sp, 112 + st.d $t3, $sp, 120 + st.d $t4, $sp, 128 + st.d $t5, $sp, 136 + st.d $t6, $sp, 144 + st.d $t7, $sp, 152 + st.d $t8, $sp, 160 + st.d $r21, $sp,168 + st.d $fp, $sp, 176 + st.d $s0, $sp, 184 + st.d $s1, $sp, 192 + st.d $s2, $sp, 200 + st.d $s3, $sp, 208 + st.d $s4, $sp, 216 + st.d $s5, $sp, 224 + st.d $s6, $sp, 232 + st.d $s7, $sp, 240 + st.d $s8, $sp, 248 + + csrrd $t2, 0x1 + st.d $t2, $sp, 8*32 // prmd + csrrd $t1, 0x6 // era + st.d $t1, $sp, 8*33 // era + + move $a0, $sp + bl loongarch64_trap_handler + + + // restore the registers. + ld.d $t1, $sp, 8*33 // era + csrwr $t1, 0x6 + ld.d $t2, $sp, 8*32 // prmd + csrwr $t2, 0x1 + + ld.d $ra, $sp, 8 + ld.d $tp, $sp, 16 + ld.d $sp, $sp, 24 + ld.d $a0, $sp, 32 + ld.d $a1, $sp, 40 + ld.d $a2, $sp, 48 + ld.d $a3, $sp, 56 + ld.d $a4, $sp, 64 + ld.d $a5, $sp, 72 + ld.d $a6, $sp, 80 + ld.d $a7, $sp, 88 + ld.d $t0, $sp, 96 + ld.d $t1, $sp, 104 + ld.d $t2, $sp, 112 + ld.d $t3, $sp, 120 + ld.d $t4, $sp, 128 + ld.d $t5, $sp, 136 + ld.d $t6, $sp, 144 + ld.d $t7, $sp, 152 + ld.d $t8, $sp, 160 + ld.d $r21, $sp,168 + ld.d $fp, $sp, 176 + ld.d $s0, $sp, 184 + ld.d $s1, $sp, 192 + ld.d $s2, $sp, 200 + ld.d $s3, $sp, 208 + ld.d $s4, $sp, 216 + ld.d $s5, $sp, 224 + ld.d $s6, $sp, 232 + ld.d $s7, $sp, 240 + ld.d $s8, $sp, 248 + + addi.d $sp, $sp, {trapframe_size} + ertn + + diff --git a/modules/axhal/src/arch/loongarch64/trap.rs b/modules/axhal/src/arch/loongarch64/trap.rs new file mode 100644 index 0000000000..70a4677bce --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/trap.rs @@ -0,0 +1,32 @@ +use super::context::TrapFrame; +use loongarch64::register::estat::{self, Exception, Trap}; + +core::arch::global_asm!( + include_str!("trap.S"), + trapframe_size = const core::mem::size_of::(), +); + +fn handle_breakpoint(era: &mut usize) { + debug!("Exception(Breakpoint) @ {:#x} ", era); + *era += 4; +} + +#[no_mangle] +fn loongarch64_trap_handler(tf: &mut TrapFrame) { + let estat = estat::read(); + match estat.cause() { + Trap::Exception(Exception::Breakpoint) => handle_breakpoint(&mut tf.era), + Trap::Interrupt(_) => { + let irq_num: usize = estat.is().trailing_zeros() as usize; + crate::trap::handle_irq_extern(irq_num) + } + _ => { + panic!( + "Unhandled trap {:?} @ {:#x}:\n{:#x?}", + estat.cause(), + tf.era, + tf + ); + } + } +} diff --git a/modules/axhal/src/arch/mod.rs b/modules/axhal/src/arch/mod.rs index b8bc0af3d0..7220281628 100644 --- a/modules/axhal/src/arch/mod.rs +++ b/modules/axhal/src/arch/mod.rs @@ -10,5 +10,8 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")]{ mod aarch64; pub use self::aarch64::*; + } else if #[cfg(target_arch = "loongarch64")]{ + mod loongarch64; + pub use self::loongarch64::*; } } diff --git a/modules/axhal/src/cpu.rs b/modules/axhal/src/cpu.rs index d0ee27a341..111005bcc1 100644 --- a/modules/axhal/src/cpu.rs +++ b/modules/axhal/src/cpu.rs @@ -45,6 +45,11 @@ pub fn current_task_ptr() -> *const T { use tock_registers::interfaces::Readable; aarch64_cpu::registers::SP_EL0.get() as _ } + #[cfg(target_arch = "loongarch64")] + unsafe { + let _guard = kernel_guard::IrqSave::new(); + CURRENT_TASK_PTR.read_current_raw() as _ + } } /// Sets the pointer to the current task with preemption-safety. @@ -71,6 +76,11 @@ pub unsafe fn set_current_task_ptr(ptr: *const T) { use tock_registers::interfaces::Writeable; aarch64_cpu::registers::SP_EL0.set(ptr as u64) } + #[cfg(target_arch = "loongarch64")] + unsafe { + let _guard = kernel_guard::IrqSave::new(); + CURRENT_TASK_PTR.write_current_raw(ptr as usize) + } } #[allow(dead_code)] diff --git a/modules/axhal/src/paging.rs b/modules/axhal/src/paging.rs index 2d035e5c62..9f32b49bfa 100644 --- a/modules/axhal/src/paging.rs +++ b/modules/axhal/src/paging.rs @@ -62,5 +62,8 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")]{ /// The architecture-specific page table. pub type PageTable = page_table::aarch64::A64PageTable; + }else if #[cfg(target_arch = "loongarch64")]{ + /// The architecture-specific page table. + pub type PageTable = page_table::loongarch64::LA64PageTable; } } diff --git a/modules/axhal/src/platform/mod.rs b/modules/axhal/src/platform/mod.rs index a39151c47f..8437dcf623 100644 --- a/modules/axhal/src/platform/mod.rs +++ b/modules/axhal/src/platform/mod.rs @@ -22,7 +22,10 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_arch = "aarch64", platform_family = "aarch64-bsta1000b"))] { mod aarch64_bsta1000b; pub use self::aarch64_bsta1000b::*; - } else { + } else if #[cfg(all(target_arch = "loongarch64", platform_family = "loongarch64-qemu-virt"))] { + mod qemu_virt_loongarch64; + pub use self::qemu_virt_loongarch64::*; + }else { mod dummy; pub use self::dummy::*; } diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/boot.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/boot.rs new file mode 100644 index 0000000000..b8cfe51e4f --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/boot.rs @@ -0,0 +1,111 @@ +use axconfig::TASK_STACK_SIZE; + +#[link_section = ".bss.stack"] +static mut BOOT_STACK: [u8; TASK_STACK_SIZE] = [0; TASK_STACK_SIZE]; +#[cfg(feature = "smp")] +pub static mut SMP_BOOT_STACK_TOP: usize = 0; + +/// The earliest entry point for the primary CPU. +/// +/// We can't use bl to jump to higher address, so we use jirl to jump to higher address. +#[naked] +#[no_mangle] +#[link_section = ".text.boot"] +unsafe extern "C" fn _start() -> ! { + core::arch::asm!(" + csrrd $t0, 0x20 # cpuid + andi $t0, $t0, 0x3ff # cpuid & 0x3ff + li.d $a0, 0 # boot core + bne $t0, $a0, 1f # if cpuid != 0, goto slave_main + + ori $t0, $zero, 0x1 # CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -2048 # UC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr $t0, 0x180 # LOONGARCH_CSR_DMWIN0 + ori $t0, $zero, 0x11 # CSR_DMW1_MAT | CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -1792 # CA, PLV0, 0x9000 xxxx xxxx xxxx + csrwr $t0, 0x181 # LOONGARCH_CSR_DMWIN1 + + # Enable PG + li.w $t0, 0xb0 # PLV=0, IE=0, PG=1 + csrwr $t0, 0x0 # LOONGARCH_CSR_CRMD + li.w $t0, 0x00 # PLV=0, PIE=0, PWE=0 + csrwr $t0, 0x1 # LOONGARCH_CSR_PRMD + li.w $t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 + csrwr $t0, 0x2 # LOONGARCH_CSR_EUEN + + la.global $sp, {boot_stack} + li.d $t0, {boot_stack_size} + add.d $sp, $sp, $t0 # setup boot stack + csrrd $a0, 0x20 # cpuid + la.global $t0, {entry} + jirl $zero,$t0,0 + + + 1: + li.d $t1, {mail_buf0} + iocsrwr.d $zero, $t1 # clear mail box 0 + + li.d $t0, (1<<12) + csrxchg $t0, $t0, 0x4 # set ECFG to enable IPI interrupt + + addi.d $t0, $zero, -1 + li.d $t1, {ipi_en} + iocsrwr.w $t0, $t1 # enable IPI interrupt + + li.d $t1, {mail_buf0} + + 2: + idle 0 + nop + iocsrrd.w $t0, $t1 # read mail box 0 + beqz $t0, 2b # if mail box 0 is empty, goto 1b + + li.d $t1, {ipi_status} # read and clear ipi interrupt + iocsrrd.w $t0, $t1 + li.d $t1, {ipi_clear} + iocsrwr.w $t0, $t1 + + li.d $t1, (1<<12) + csrxchg $t0, $t0, 0x4 # disable ipi interrupt + + li.d $t1, {mail_buf0} + iocsrrd.d $t0, $t1 # read mail box 0 + or $ra, $t0, $zero # set boot core flag + jirl $zero, $ra, 0 # jump to rust_entry + ", + boot_stack_size = const TASK_STACK_SIZE, + boot_stack = sym BOOT_STACK, + entry = sym super::rust_entry, + mail_buf0 = const loongarch64::consts::LOONGARCH_CSR_MAIL_BUF0, + ipi_en = const loongarch64::consts::LOONGARCH_IOCSR_IPI_EN, + ipi_status = const loongarch64::consts::LOONGARCH_IOCSR_IPI_STATUS, + ipi_clear = const loongarch64::consts::LOONGARCH_IOCSR_IPI_CLEAR, + options(noreturn), + ) +} + +/// The earliest entry point for secondary CPUs. +#[cfg(feature = "smp")] +#[naked] +#[no_mangle] +#[link_section = ".text.boot"] +unsafe extern "C" fn _start_secondary() -> ! { + core::arch::asm!(" + ori $t0, $zero, 0x1 # CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -2048 # UC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr $t0, 0x180 # LOONGARCH_CSR_DMWIN0 + ori $t0, $zero, 0x11 # CSR_DMW1_MAT | CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -1792 # CA, PLV0, 0x9000 xxxx xxxx xxxx + csrwr $t0, 0x181 # LOONGARCH_CSR_DMWIN1 + la.abs $t0, {sm_boot_stack_top} + ld.d $sp, $t0,0 # read boot stack top + + csrrd $a0, 0x20 # cpuid + la.global $t0, {entry} + jirl $zero,$t0,0 + ", + sm_boot_stack_top = sym SMP_BOOT_STACK_TOP, + entry = sym super::rust_entry_secondary, + options(noreturn), + ) +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/console.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/console.rs new file mode 100644 index 0000000000..b78c849cc8 --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/console.rs @@ -0,0 +1,75 @@ +//! Uart 16550. + +use core::fmt::Write; +use spinlock::SpinNoIrq; + +const UART_ADDR: usize = 0x900000001FE001E0; + +static COM1: SpinNoIrq = SpinNoIrq::new(Uart::new(UART_ADDR)); + +pub struct Uart { + base_address: usize, +} + +impl Uart { + pub const fn new(base_address: usize) -> Self { + Uart { base_address } + } + + pub fn putchar(&mut self, c: u8) { + let ptr = self.base_address as *mut u8; + loop { + unsafe { + let c = ptr.add(5).read_volatile(); + if c & (1 << 5) != 0 { + break; + } + } + } + unsafe { + ptr.add(0).write_volatile(c); + } + } + + pub fn getchar(&mut self) -> Option { + let ptr = self.base_address as *mut u8; + unsafe { + if ptr.add(5).read_volatile() & 1 == 0 { + // The DR bit is 0, meaning no data + None + } else { + // The DR bit is 1, meaning data! + Some(ptr.add(0).read_volatile()) + } + } + } +} +impl Write for Uart { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for c in s.bytes() { + self.putchar(c); + } + Ok(()) + } +} + +/// Writes a byte to the console. +pub fn putchar(c: u8) { + let mut uart = COM1.lock(); + match c { + b'\n' => { + uart.putchar(b'\r'); + uart.putchar(b'\n'); + } + c => uart.putchar(c), + } +} + +pub fn write_fmt(args: core::fmt::Arguments) { + COM1.lock().write_fmt(args).unwrap(); +} + +/// Reads a byte from the console, or returns [`None`] if no input is available. +pub fn getchar() -> Option { + COM1.lock().getchar() +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/irq.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/irq.rs new file mode 100644 index 0000000000..7703bafdaa --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/irq.rs @@ -0,0 +1,71 @@ +use crate::irq::IrqHandler; +use lazy_init::LazyInit; +use loongarch64::register::ecfg; +use loongarch64::register::ecfg::LineBasedInterrupt; +use loongarch64::register::ticlr; +/// The maximum number of IRQs. +pub const MAX_IRQ_COUNT: usize = 1024; + +/// The timer IRQ number +pub const TIMER_IRQ_NUM: usize = 11; + +/// HW0 +pub const EXT_IRQ_NUM: usize = 2; + +static TIMER_HANDLER: LazyInit = LazyInit::new(); + +macro_rules! with_cause { + ($cause: expr, @TIMER => $timer_op: expr, @EXT => $ext_op: expr $(,)?) => { + match $cause { + TIMER_IRQ_NUM => $timer_op, + EXT_IRQ_NUM => $ext_op, + _ => panic!("invalid trap cause: {:#x}", $cause), + } + }; +} +/// Enables or disables the given IRQ. +pub fn set_enable(vector: usize, _enabled: bool) { + if vector == 11 {} +} + +/// Registers an IRQ handler for the given IRQ. +/// +/// It also enables the IRQ if the registration succeeds. It returns `false` if +/// the registration failed. +pub fn register_handler(vector: usize, handler: IrqHandler) -> bool { + with_cause!( + vector, + @TIMER => if !TIMER_HANDLER.is_init() { + TIMER_HANDLER.init_by(handler); + true + } else { + false + }, + @EXT => crate::irq::register_handler_common(vector, handler), + ) +} + +/// Dispatches the IRQ. +/// +/// This function is called by the common interrupt handler. It looks +/// up in the IRQ handler table and calls the corresponding handler. If +/// necessary, it also acknowledges the interrupt controller after handling. +pub fn dispatch_irq(vector: usize) { + with_cause!( + vector, + @TIMER => { + ticlr::clear_timer_interrupt(); + TIMER_HANDLER(); + }, + @EXT => crate::irq::dispatch_irq_common(0), + ); +} + +pub(super) fn init_percpu() { + // enable soft interrupts, timer interrupts, and external interrupts + let inter = LineBasedInterrupt::TIMER + | LineBasedInterrupt::SWI0 + | LineBasedInterrupt::SWI1 + | LineBasedInterrupt::HWI0; + ecfg::set_lie(inter); +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/mem.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/mem.rs new file mode 100644 index 0000000000..bad6113c42 --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/mem.rs @@ -0,0 +1,6 @@ +use crate::mem::MemRegion; + +/// Returns platform-specific memory regions. +pub(crate) fn platform_regions() -> impl Iterator { + crate::mem::default_free_regions().chain(crate::mem::default_mmio_regions()) +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/misc.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/misc.rs new file mode 100644 index 0000000000..37871a4888 --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/misc.rs @@ -0,0 +1,9 @@ +/// Shutdown the whole system, including all CPUs. +pub fn terminate() -> ! { + info!("Shutting down..."); + crate::arch::halt(); + warn!("It should shutdown!"); + loop { + crate::arch::halt(); + } +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/mod.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/mod.rs new file mode 100644 index 0000000000..b3c605e315 --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/mod.rs @@ -0,0 +1,52 @@ +mod boot; +pub mod console; +pub mod mem; +pub mod misc; +pub mod time; + +#[cfg(feature = "irq")] +pub mod irq; + +#[cfg(feature = "smp")] +pub mod mp; + +extern "C" { + fn trap_vector_base(); + fn rust_main(cpu_id: usize, dtb: usize); + fn _sbss(); + fn _ebss(); + #[cfg(feature = "smp")] + fn rust_main_secondary(cpu_id: usize); +} + +#[no_mangle] +unsafe extern "C" fn rust_entry(cpu_id: usize, _dtb: usize) { + crate::mem::clear_bss(); + crate::cpu::init_primary(cpu_id); + crate::arch::set_trap_vector_base(trap_vector_base as usize); + rust_main(cpu_id, 0); +} + +#[cfg(feature = "smp")] +unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { + crate::arch::set_trap_vector_base(trap_vector_base as usize); + crate::cpu::init_secondary(cpu_id); + rust_main_secondary(cpu_id); +} + +/// Initializes the platform devices for the primary CPU. +/// +/// For example, the interrupt controller and external interrupts. +pub fn platform_init() { + #[cfg(feature = "irq")] + self::irq::init_percpu(); + self::time::init_percpu(); +} + +/// Initializes the platform devices for secondary CPUs. +#[cfg(feature = "smp")] +pub fn platform_init_secondary() { + #[cfg(feature = "irq")] + self::irq::init_percpu(); + self::time::init_percpu(); +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/mp.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/mp.rs new file mode 100644 index 0000000000..c48fb4a49d --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/mp.rs @@ -0,0 +1,15 @@ +use crate::mem::{phys_to_virt, PhysAddr}; +use loongarch64::ipi::{csr_mail_send, send_ipi_single}; + +/// Starts the given secondary CPU with its boot stack. +pub fn start_secondary_cpu(hartid: usize, stack_top: PhysAddr) { + extern "C" { + fn _start_secondary(); + } + let stack_top_virt_addr = phys_to_virt(stack_top).as_usize(); + unsafe { + super::boot::SMP_BOOT_STACK_TOP = stack_top_virt_addr; + } + csr_mail_send(_start_secondary as u64, hartid, 0); + send_ipi_single(hartid, 1); +} diff --git a/modules/axhal/src/platform/qemu_virt_loongarch64/time.rs b/modules/axhal/src/platform/qemu_virt_loongarch64/time.rs new file mode 100644 index 0000000000..506a341230 --- /dev/null +++ b/modules/axhal/src/platform/qemu_virt_loongarch64/time.rs @@ -0,0 +1,49 @@ +use loongarch64::time::Time; +const NANOS_PER_TICK: u64 = crate::time::NANOS_PER_SEC / axconfig::TIMER_FREQUENCY as u64; + +/// Returns the current clock time in hardware ticks. +#[inline] +pub fn current_ticks() -> u64 { + Time::read() as u64 +} + +/// Converts hardware ticks to nanoseconds. +#[inline] +pub const fn ticks_to_nanos(ticks: u64) -> u64 { + ticks * NANOS_PER_TICK +} + +/// Converts nanoseconds to hardware ticks. +#[inline] +pub const fn nanos_to_ticks(nanos: u64) -> u64 { + nanos / NANOS_PER_TICK +} + +/// Set a one-shot timer. +/// +/// A timer interrupt will be triggered at the given deadline (in nanoseconds). +#[cfg(feature = "irq")] +pub fn set_oneshot_timer(deadline_ns: u64) { + use loongarch64::register::tcfg; + let old_ticks = current_ticks(); + let mut ticks = nanos_to_ticks(deadline_ns) as usize; + // Normally, deadline_ns should be increasing. + // These two lines of code are mainly used by the init_percpu function. + // We need to trigger a clock interrupt before the interrupt is turned on. + if ticks <= old_ticks as usize { + ticks = old_ticks as usize + 16; + } + let ticks = ticks - old_ticks as usize; + // align to 4 + let ticks = (ticks + 3) & !3; + tcfg::set_periodic(false); // set timer to one-shot mode + tcfg::set_init_val(ticks); // set timer initial value + tcfg::set_en(true); // enable timer +} + +pub(super) fn init_percpu() { + #[cfg(feature = "irq")] + { + set_oneshot_timer(0); + } +} diff --git a/modules/axhal/src/tls.rs b/modules/axhal/src/tls.rs index 849ef0e38a..b82b51b3f7 100644 --- a/modules/axhal/src/tls.rs +++ b/modules/axhal/src/tls.rs @@ -68,6 +68,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "riscv64")] { const TCB_SIZE: usize = 0; const GAP_ABOVE_TP: usize = 0; + }else if #[cfg(target_arch = "loongarch64")] { + const TCB_SIZE: usize = 0; + const GAP_ABOVE_TP: usize = 0; } } @@ -131,7 +134,11 @@ fn static_tls_size() -> usize { fn static_tls_offset() -> usize { if cfg!(target_arch = "x86_64") { 0 - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE + GAP_ABOVE_TP } else { unreachable!() @@ -141,7 +148,11 @@ fn static_tls_offset() -> usize { fn tp_offset() -> usize { if cfg!(target_arch = "x86_64") { static_tls_size() - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE } else { unreachable!() @@ -151,7 +162,11 @@ fn tp_offset() -> usize { fn tls_area_size() -> usize { if cfg!(target_arch = "x86_64") { static_tls_size() + TCB_SIZE - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE + GAP_ABOVE_TP + static_tls_size() } else { unreachable!() diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index a62c3055cb..4812609bc6 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -140,7 +140,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { #[cfg(feature = "alloc")] init_allocator(); - #[cfg(feature = "paging")] + #[cfg(all(feature = "paging", not(target_arch = "loongarch64")))] { info!("Initialize kernel page table..."); remap_kernel_memory().expect("remap kernel memoy failed"); @@ -229,7 +229,7 @@ fn init_allocator() { } } -#[cfg(feature = "paging")] +#[cfg(all(feature = "paging", not(target_arch = "loongarch64")))] fn remap_kernel_memory() -> Result<(), axhal::paging::PagingError> { use axhal::mem::{memory_regions, phys_to_virt}; use axhal::paging::PageTable; diff --git a/modules/axruntime/src/mp.rs b/modules/axruntime/src/mp.rs index 768ef5188a..adaa846069 100644 --- a/modules/axruntime/src/mp.rs +++ b/modules/axruntime/src/mp.rs @@ -34,7 +34,7 @@ pub extern "C" fn rust_main_secondary(cpu_id: usize) -> ! { ENTERED_CPUS.fetch_add(1, Ordering::Relaxed); info!("Secondary CPU {:x} started.", cpu_id); - #[cfg(feature = "paging")] + #[cfg(all(feature = "paging", not(target_arch = "loongarch64")))] super::remap_kernel_memory().unwrap(); axhal::platform_init_secondary(); diff --git a/platforms/aarch64-qemu-virt.toml b/platforms/aarch64-qemu-virt.toml index 723bba7eb1..409fe2da8c 100644 --- a/platforms/aarch64-qemu-virt.toml +++ b/platforms/aarch64-qemu-virt.toml @@ -67,7 +67,7 @@ pci-bus-end = "0xff" pci-ranges = [ ["0x3ef_f0000", "0x1_0000"], # PIO space ["0x1000_0000", "0x2eff_0000"], # 32-bit MMIO space - ["0x80_0000_0000", "0x80_0000_0000"], # 64-but MMIO space + ["0x80_0000_0000", "0x80_0000_0000"], # 64-bit MMIO space ] # UART Address uart-paddr = "0x0900_0000" diff --git a/platforms/loongarch64-qemu-virt.toml b/platforms/loongarch64-qemu-virt.toml new file mode 100644 index 0000000000..42bf636366 --- /dev/null +++ b/platforms/loongarch64-qemu-virt.toml @@ -0,0 +1,41 @@ +# Architecture identifier. +arch = "loongarch64" +# Platform identifier. +platform = "loongarch64-qemu-virt" +# Platform family. +family = "loongarch64-qemu-virt" + +# Base address of the whole physical memory. +#phys-memory-base = "0x0" +phys-memory-base = "0x9000_0000" +# Size of the whole physical memory. +phys-memory-size = "0x800_0000" # 128M +# Base physical address of the kernel image. +#kernel-base-paddr = "0x000_1000" +kernel-base-paddr = "0x9000_0000" +# Base virtual address of the kernel image. +kernel-base-vaddr = "0x9000_0000_9000_0000" +# Linear mapping offset, for quick conversions between physical and virtual +# addresses. +phys-virt-offset = "0x9000_0000_0000_0000" +# MMIO regions with format (`base_paddr`, `size`). +mmio-regions = [ + ["0x2000_0000", "0x07ff_ffff"], # PCI config space + ["0x4000_0000", "0x3fff_ffff"] # PCI memory ranges +] +# VirtIO MMIO regions with format (`base_paddr`, `size`). +virtio-mmio-regions = [ +] +# Base physical address of the PCIe ECAM space. +pci-ecam-base = "0x2000_0000" +# End PCI bus number (`bus-range` property in device tree). +pci-bus-end = "0x7f" +# PCI device memory ranges (`ranges` property in device tree). +pci-ranges = [ + ["0x1800_4000", "0xc000"], # PIO space + ["0x4000_0000", "0x40000000"], # 64-bit MMIO space +] +# Timer interrupt frequency in Hz. +timer-frequency = "100_000_000" # 100MHz + + diff --git a/platforms/riscv64-qemu-virt.toml b/platforms/riscv64-qemu-virt.toml index 8c1051c89b..288ecd65aa 100644 --- a/platforms/riscv64-qemu-virt.toml +++ b/platforms/riscv64-qemu-virt.toml @@ -43,7 +43,7 @@ pci-bus-end = "0xff" pci-ranges = [ ["0x0300_0000", "0x1_0000"], # PIO space ["0x4000_0000", "0x4000_0000"], # 32-bit MMIO space - ["0x4_0000_0000", "0x4_0000_0000"], # 64-but MMIO space + ["0x4_0000_0000", "0x4_0000_0000"], # 64-bit MMIO space ] # Timer interrupt frequency in Hz. diff --git a/scripts/make/cargo.mk b/scripts/make/cargo.mk index fc07ea2dae..e4aff599d4 100644 --- a/scripts/make/cargo.mk +++ b/scripts/make/cargo.mk @@ -23,6 +23,10 @@ ifeq ($(ARCH), x86_64) RUSTFLAGS += -C link-arg=--no-relax endif +ifeq ($(ARCH),loongarch64) + build_args += -Z build-std +endif + ifeq ($(MAKECMDGOALS), doc_check_missing) RUSTDOCFLAGS += -D missing-docs endif diff --git a/scripts/make/qemu.mk b/scripts/make/qemu.mk index 3ed4326140..748add0e38 100644 --- a/scripts/make/qemu.mk +++ b/scripts/make/qemu.mk @@ -24,7 +24,14 @@ qemu_args-aarch64 := \ -machine virt \ -kernel $(OUT_BIN) +qemu_args-loongarch64 := \ + -kernel $(OUT_ELF) + +ifeq ($(ARCH), loongarch64) +qemu_args-y := -m 1G -smp $(SMP) $(qemu_args-$(ARCH)) +else qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH)) +endif qemu_args-$(BLK) += \ -device virtio-blk-$(vdev-suffix),drive=disk0 \ @@ -50,7 +57,10 @@ qemu_args-$(GRAPHIC) += \ -serial mon:stdio ifeq ($(GRAPHIC), n) - qemu_args-y += -nographic + qemu_args-y += -nographic + ifeq ($(ARCH), loongarch64) + qemu_args-y += -vga none + endif endif ifeq ($(QEMU_LOG), y) diff --git a/tools/la64/README.md b/tools/la64/README.md new file mode 100644 index 0000000000..b86cd7b174 --- /dev/null +++ b/tools/la64/README.md @@ -0,0 +1,16 @@ +# Run Arceos on loongarch64 + +1. install `gcc ` + + ``` + wget https://github.com/foxsen/qemu-loongarch-runenv/releases/download/toolchain/loongarch64-clfs-2021-12-18-cross-tools-gcc-full.tar.xz + tar zxf loongarch64-clfs-2021-12-18-cross-tools-gcc-full.tar.xz + # exec below command in bash OR add below info in ~/.bashrc + ``` + +2. install `gdb` + + ``` + copy la-gdb.sh to your dir and run it + ``` + diff --git a/tools/la64/la-gdb.sh b/tools/la64/la-gdb.sh new file mode 100755 index 0000000000..48b551354c --- /dev/null +++ b/tools/la64/la-gdb.sh @@ -0,0 +1,9 @@ +#!/bin/bash +git clone https://github.com/foxsen/binutils-gdb +git checkout loongarch-v2022-03-10 +cd binutils-gdb +mkdir "build" +cd "build" +../configure --target=loongarch64-unknown-linux-gnu --prefix=/opt/gdb +make -j$(nproc) +sudo make install \ No newline at end of file diff --git a/ulib/axlibc/include/float.h b/ulib/axlibc/include/float.h index 32ac40abfb..6d53fd0ab5 100644 --- a/ulib/axlibc/include/float.h +++ b/ulib/axlibc/include/float.h @@ -95,5 +95,19 @@ #define DECIMAL_DIG 21 #endif +#elif defined(__loonarch__) +#define FLT_EVAL_METHOD 0 +#define LDBL_TRUE_MIN 6.47517511943802511092443895822764655e-4966L +#define LDBL_MIN 3.36210314311209350626267781732175260e-4932L +#define LDBL_MAX 1.18973149535723176508575932662800702e+4932L +#define LDBL_EPSILON 1.92592994438723585305597794258492732e-34L + +#define LDBL_MANT_DIG 113 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MAX_EXP 16384 + +#define LDBL_DIG 33 +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_10_EXP 4932 #endif diff --git a/ulib/axlibc/include/setjmp.h b/ulib/axlibc/include/setjmp.h index 70981dfb45..459e7333ee 100644 --- a/ulib/axlibc/include/setjmp.h +++ b/ulib/axlibc/include/setjmp.h @@ -8,6 +8,8 @@ typedef unsigned long __jmp_buf[22]; #elif defined(__riscv__) || defined(__riscv) typedef unsigned long __jmp_buf[26]; #elif defined(__x86_64__) +typedef unsigned long __jmp_buf[21]; +#elif defined(__loongarch__) typedef unsigned long __jmp_buf[8]; #endif diff --git a/ulib/axlibc/include/stdint.h b/ulib/axlibc/include/stdint.h index 3081bf0116..89d87f2458 100644 --- a/ulib/axlibc/include/stdint.h +++ b/ulib/axlibc/include/stdint.h @@ -38,7 +38,7 @@ typedef int64_t intmax_t; * We use pointer types to represent addresses, * uintptr_t to represent the numerical values of addresses. * */ -#if __riscv_xlen == 64 || defined(__x86_64__) || defined(__aarch64__) +#if __riscv_xlen == 64 || defined(__x86_64__) || defined(__aarch64__) || defined(__loongarch__) typedef int64_t intptr_t; typedef uint64_t uintptr_t; #elif __riscv_xlen == 32 || defined(__i386__) diff --git a/ulib/axlibc/src/setjmp.rs b/ulib/axlibc/src/setjmp.rs index aed4441ffc..2f7693b0f4 100644 --- a/ulib/axlibc/src/setjmp.rs +++ b/ulib/axlibc/src/setjmp.rs @@ -56,6 +56,58 @@ pub unsafe extern "C" fn setjmp(_buf: *mut ctypes::__jmp_buf_tag) { ret", options(noreturn), ); + #[cfg(all(target_arch = "loongarch64", feature = "fp_simd"))] + core::arch::asm!( + "st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + st.d $r1, $a0, 12 * 8 # r21 like gp + + fst.d $f24, $a0, 13 * 8 + fst.d $f25, $a0, 14 * 8 + fst.d $f26, $a0, 15 * 8 + fst.d $f27, $a0, 16 * 8 + fst.d $f28, $a0, 17 * 8 + fst.d $f29, $a0, 18 * 8 + fst.d $f30, $a0, 19 * 8 + fst.d $f31, $a0, 20 * 8 + + + li.w $a0, 0 + ret + ", + options(noreturn), + ); + #[cfg(all(target_arch = "loongarch64", not(feature = "fp_simd")))] + core::arch::asm!( + "st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + st.d $r1, $a0, 12 * 8 # r21 like gp + + li.w $a0, 0 + ret + ", + options(noreturn), + ); #[cfg(all(target_arch = "riscv64", feature = "fp_simd"))] core::arch::asm!( "sd s0, 0(a0) @@ -114,7 +166,8 @@ pub unsafe extern "C" fn setjmp(_buf: *mut ctypes::__jmp_buf_tag) { #[cfg(not(any( target_arch = "aarch64", target_arch = "x86_64", - target_arch = "riscv64" + target_arch = "riscv64", + target_arch = "loongarch64" )))] core::arch::asm!("ret", options(noreturn)) } @@ -178,6 +231,63 @@ pub unsafe extern "C" fn longjmp(_buf: *mut ctypes::__jmp_buf_tag, _val: c_int) jmp rdx", options(noreturn), ); + + #[cfg(all(target_arch = "loongarch64", feature = "fp_simd"))] + core::arch::asm!( + "ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + ld.d $r21, $a1, 12 * 8 # r21 like gp + + + fld.d $f24, $a0, 13 * 8 + fld.d $f25, $a0, 14 * 8 + fld.d $f26, $a0, 15 * 8 + fld.d $f27, $a0, 16 * 8 + fld.d $f28, $a0, 17 * 8 + fld.d $f29, $a0, 18 * 8 + fld.d $f30, $a0, 19 * 8 + fld.d $f31, $a0, 20 * 8 + + + sltui $a0, $a1, 1 + add.d $a0, $a0, $a1 # a0 = (a1 == 0) ? 1 : a1 + jirl $zero,$ra, 0 + ", + options(noreturn), + ); + #[cfg(all(target_arch = "loongarch64", not(feature = "fp_simd")))] + core::arch::asm!( + "ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + ld.d $r21, $a1, 12 * 8 # r21 like gp + + + sltui $a0, $a1, 1 + add.d $a0, $a0, $a1 # a0 = (a1 == 0) ? 1 : a1 + jirl $zero,$ra, 0 + ", + options(noreturn), + ); #[cfg(all(target_arch = "riscv64", feature = "fp_simd"))] core::arch::asm!( "ld s0, 0(a0)