From 4fa0e6334bd1f0aa50829c2f71852e897caa7202 Mon Sep 17 00:00:00 2001 From: YanWQ-monad Date: Tue, 24 Sep 2024 22:14:30 +0800 Subject: [PATCH] Add RISC-V base support --- Cargo.lock | 57 ++++ OSDK.toml | 23 ++ kernel/Cargo.toml | 3 + kernel/comps/time/Cargo.toml | 3 + kernel/comps/time/src/rtc/goldfish.rs | 46 +++ kernel/comps/time/src/rtc/mod.rs | 1 + kernel/src/arch/mod.rs | 5 + kernel/src/arch/riscv/cpu.rs | 148 ++++++++++ kernel/src/arch/riscv/mod.rs | 4 + kernel/src/arch/riscv/signal.rs | 19 ++ kernel/src/lib.rs | 2 + .../process/program_loader/elf/elf_file.rs | 15 +- kernel/src/syscall/arch/mod.rs | 5 + kernel/src/syscall/arch/riscv.rs | 273 ++++++++++++++++++ kernel/src/util/random.rs | 8 + osdk/src/base_crate/mod.rs | 2 +- osdk/src/base_crate/riscv64.ld.template | 64 ++++ ostd/Cargo.toml | 5 + ostd/src/arch/mod.rs | 4 + ostd/src/arch/riscv/boot/boot.S | 62 ++++ ostd/src/arch/riscv/boot/mod.rs | 132 +++++++++ ostd/src/arch/riscv/boot/smp.rs | 11 + ostd/src/arch/riscv/cpu/local.rs | 23 ++ ostd/src/arch/riscv/cpu/mod.rs | 230 +++++++++++++++ ostd/src/arch/riscv/device/io_port.rs | 84 ++++++ ostd/src/arch/riscv/device/mod.rs | 6 + ostd/src/arch/riscv/iommu/mod.rs | 33 +++ ostd/src/arch/riscv/irq.rs | 150 ++++++++++ ostd/src/arch/riscv/mm/mod.rs | 229 +++++++++++++++ ostd/src/arch/riscv/mod.rs | 69 +++++ ostd/src/arch/riscv/pci.rs | 8 + ostd/src/arch/riscv/qemu.rs | 22 ++ ostd/src/arch/riscv/serial.rs | 39 +++ ostd/src/arch/riscv/task/mod.rs | 110 +++++++ ostd/src/arch/riscv/task/switch.S | 37 +++ ostd/src/arch/riscv/timer/mod.rs | 47 +++ ostd/src/arch/riscv/trap/mod.rs | 45 +++ ostd/src/arch/riscv/trap/trap.S | 176 +++++++++++ ostd/src/arch/riscv/trap/trap.rs | 221 ++++++++++++++ ostd/src/arch/x86/mod.rs | 2 +- ostd/src/bus/mmio/mod.rs | 7 +- ostd/src/bus/mod.rs | 2 + ostd/src/cpu/mod.rs | 4 +- ostd/src/lib.rs | 2 +- ostd/src/mm/io.rs | 7 + ostd/src/mm/kspace.rs | 3 + 46 files changed, 2436 insertions(+), 12 deletions(-) create mode 100644 kernel/comps/time/src/rtc/goldfish.rs create mode 100644 kernel/src/arch/riscv/cpu.rs create mode 100644 kernel/src/arch/riscv/mod.rs create mode 100644 kernel/src/arch/riscv/signal.rs create mode 100644 kernel/src/syscall/arch/riscv.rs create mode 100644 osdk/src/base_crate/riscv64.ld.template create mode 100644 ostd/src/arch/riscv/boot/boot.S create mode 100644 ostd/src/arch/riscv/boot/mod.rs create mode 100644 ostd/src/arch/riscv/boot/smp.rs create mode 100644 ostd/src/arch/riscv/cpu/local.rs create mode 100644 ostd/src/arch/riscv/cpu/mod.rs create mode 100644 ostd/src/arch/riscv/device/io_port.rs create mode 100644 ostd/src/arch/riscv/device/mod.rs create mode 100644 ostd/src/arch/riscv/iommu/mod.rs create mode 100644 ostd/src/arch/riscv/irq.rs create mode 100644 ostd/src/arch/riscv/mm/mod.rs create mode 100644 ostd/src/arch/riscv/mod.rs create mode 100644 ostd/src/arch/riscv/pci.rs create mode 100644 ostd/src/arch/riscv/qemu.rs create mode 100644 ostd/src/arch/riscv/serial.rs create mode 100644 ostd/src/arch/riscv/task/mod.rs create mode 100644 ostd/src/arch/riscv/task/switch.S create mode 100644 ostd/src/arch/riscv/timer/mod.rs create mode 100644 ostd/src/arch/riscv/trap/mod.rs create mode 100644 ostd/src/arch/riscv/trap/trap.S create mode 100644 ostd/src/arch/riscv/trap/trap.rs diff --git a/Cargo.lock b/Cargo.lock index 9539767e..f9de43cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,7 @@ dependencies = [ "ostd", "paste", "rand", + "riscv", "spin 0.9.8", "static_assertions", "takeable", @@ -222,6 +223,7 @@ name = "aster-time" version = "0.1.0" dependencies = [ "aster-util", + "chrono", "component", "log", "ostd", @@ -352,6 +354,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + [[package]] name = "component" version = "0.1.0" @@ -416,6 +427,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" + [[package]] name = "ctor" version = "0.1.25" @@ -531,6 +548,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + [[package]] name = "equivalent" version = "1.0.1" @@ -566,6 +589,12 @@ dependencies = [ "ext-trait", ] +[[package]] +name = "fdt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" + [[package]] name = "fnv" version = "1.0.7" @@ -1064,6 +1093,7 @@ dependencies = [ "buddy_system_allocator", "cfg-if", "const-assert", + "fdt", "gimli 0.28.0", "iced-x86", "id-alloc", @@ -1081,7 +1111,9 @@ dependencies = [ "ostd-pod", "ostd-test", "owo-colors 3.5.0", + "riscv", "rsdp", + "sbi-rt", "spin 0.9.8", "static_assertions", "tdx-guest", @@ -1261,6 +1293,16 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal", +] + [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -1282,6 +1324,21 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "sbi-rt" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0" +dependencies = [ + "sbi-spec", +] + +[[package]] +name = "sbi-spec" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" + [[package]] name = "scopeguard" version = "1.2.0" diff --git a/OSDK.toml b/OSDK.toml index cf803d53..3d9ce1a7 100644 --- a/OSDK.toml +++ b/OSDK.toml @@ -71,3 +71,26 @@ qemu.args = """\ -monitor chardev:mux \ -serial chardev:mux \ """ + +[scheme."riscv"] +boot.method = "qemu-direct" +build.strip_elf = false + +qemu.path = "./qemu-system-riscv64" +qemu.args = """\ + -cpu rv64,zba=true,zbb=true \ + -machine virt \ + -m 8G \ + --no-reboot \ + -nographic \ + -display none \ + -serial chardev:mux \ + -monitor chardev:mux \ + -chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log \ + -drive if=none,format=raw,id=x0,file=./test/build/ext2.img \ + -drive if=none,format=raw,id=x1,file=./test/build/exfat.img \ + -device virtio-blk-device,drive=x0 \ + -device virtio-keyboard-device \ + -device virtio-serial-device \ + -device virtconsole,chardev=mux \ +""" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 18c08fd2..819400b6 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -58,6 +58,9 @@ getset = "0.1.2" takeable = "0.2.2" cfg-if = "1.0" +[target.riscv64gc-unknown-none-elf.dependencies] +riscv = { version = "0.11.1", features = ["s-mode"] } + [dependencies.lazy_static] version = "1.0" features = ["spin_no_std"] diff --git a/kernel/comps/time/Cargo.toml b/kernel/comps/time/Cargo.toml index 2b58beec..7c9f2d07 100644 --- a/kernel/comps/time/Cargo.toml +++ b/kernel/comps/time/Cargo.toml @@ -12,4 +12,7 @@ component = { path = "../../libs/comp-sys/component" } log = "0.4" spin = "0.9.4" +[target.riscv64gc-unknown-none-elf.dependencies] +chrono = { version = "0.4.38", default-features = false } + [features] diff --git a/kernel/comps/time/src/rtc/goldfish.rs b/kernel/comps/time/src/rtc/goldfish.rs new file mode 100644 index 00000000..9ba6561b --- /dev/null +++ b/kernel/comps/time/src/rtc/goldfish.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 + +use ostd::{arch::riscv::timer::GOLDFISH_IO_MEM, mm::VmIoOnce}; +use chrono::{DateTime, Datelike, Timelike}; + +use crate::{SystemTime, rtc::Driver}; + +pub struct RtcGoldfish; + +impl Driver for RtcGoldfish { + fn try_new() -> Option { + GOLDFISH_IO_MEM.get()?; + Some(RtcGoldfish) + } + + fn read_rtc(&self) -> SystemTime { + const TIME_LOW: usize = 0; + const TIME_HIGH: usize = 4; + + let io_mem = GOLDFISH_IO_MEM.get().unwrap(); + + let mut last_time_high = io_mem.read_once(TIME_HIGH).unwrap(); + let timestamp = loop { + let time_low: u32 = io_mem.read_once(TIME_LOW).unwrap(); + let time_high: u32 = io_mem.read_once(TIME_HIGH).unwrap(); + if last_time_high == time_high { + break ((time_high as u64) << 32) | time_low as u64; + } + last_time_high = time_high; + }; + + let time = DateTime::from_timestamp_nanos(timestamp as i64).naive_utc(); + let (is_ad, year) = time.year_ce(); + debug_assert!(is_ad, "non-negative timestamp should always be AD"); + + SystemTime { + year: year as u16, + month: time.month() as u8, + day: time.day() as u8, + hour: time.hour() as u8, + minute: time.minute() as u8, + second: time.second() as u8, + nanos: time.nanosecond() as u64, + } + } +} diff --git a/kernel/comps/time/src/rtc/mod.rs b/kernel/comps/time/src/rtc/mod.rs index 53550fac..0cf2099b 100644 --- a/kernel/comps/time/src/rtc/mod.rs +++ b/kernel/comps/time/src/rtc/mod.rs @@ -39,4 +39,5 @@ macro_rules! declare_rtc_drivers { declare_rtc_drivers! { #[cfg(target_arch = "x86_64")] cmos::RtcCmos, + #[cfg(target_arch = "riscv64")] goldfish::RtcGoldfish, } diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs index 695b5e60..f91e63e3 100644 --- a/kernel/src/arch/mod.rs +++ b/kernel/src/arch/mod.rs @@ -1,6 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 +#[cfg(target_arch = "riscv64")] +mod riscv; #[cfg(target_arch = "x86_64")] mod x86; + +#[cfg(target_arch = "riscv64")] +pub use riscv::*; #[cfg(target_arch = "x86_64")] pub use x86::*; diff --git a/kernel/src/arch/riscv/cpu.rs b/kernel/src/arch/riscv/cpu.rs new file mode 100644 index 00000000..9bb2254f --- /dev/null +++ b/kernel/src/arch/riscv/cpu.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MPL-2.0 + +use ostd::{ + cpu::{CpuExceptionInfo, RawGeneralRegs, UserContext}, + Pod, +}; + +use crate::{cpu::LinuxAbi, thread::exception::PageFaultInfo, vm::perms::VmPerms}; + +impl LinuxAbi for UserContext { + fn syscall_num(&self) -> usize { + self.a7() + } + + fn syscall_ret(&self) -> usize { + self.a0() + } + + fn set_syscall_ret(&mut self, ret: usize) { + self.set_a0(ret) + } + + fn syscall_args(&self) -> [usize; 6] { + [ + self.a0(), + self.a1(), + self.a2(), + self.a3(), + self.a4(), + self.a5(), + ] + } + + fn set_tls_pointer(&mut self, tls: usize) { + self.set_tp(tls); + } + + fn tls_pointer(&self) -> usize { + self.tp() + } +} + +/// General-purpose registers. +#[derive(Debug, Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct GpRegs { + pub zero: usize, + pub ra: usize, + pub sp: usize, + pub gp: usize, + pub tp: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub s0: usize, + pub s1: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, + pub s9: usize, + pub s10: usize, + pub s11: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, +} + +macro_rules! copy_gp_regs { + ($src: ident, $dst: ident) => { + $dst.zero = $src.zero; + $dst.ra = $src.ra; + $dst.sp = $src.sp; + $dst.gp = $src.gp; + $dst.tp = $src.tp; + $dst.t0 = $src.t0; + $dst.t1 = $src.t1; + $dst.t2 = $src.t2; + $dst.s0 = $src.s0; + $dst.s1 = $src.s1; + $dst.a0 = $src.a0; + $dst.a1 = $src.a1; + $dst.a2 = $src.a2; + $dst.a3 = $src.a3; + $dst.a4 = $src.a4; + $dst.a5 = $src.a5; + $dst.a6 = $src.a6; + $dst.a7 = $src.a7; + $dst.s2 = $src.s2; + $dst.s3 = $src.s3; + $dst.s4 = $src.s4; + $dst.s5 = $src.s5; + $dst.s6 = $src.s6; + $dst.s7 = $src.s7; + $dst.s8 = $src.s8; + $dst.s9 = $src.s9; + $dst.s10 = $src.s10; + $dst.s11 = $src.s11; + $dst.t3 = $src.t3; + $dst.t4 = $src.t4; + $dst.t5 = $src.t5; + $dst.t6 = $src.t6; + }; +} + +impl GpRegs { + pub fn copy_to_raw(&self, dst: &mut RawGeneralRegs) { + copy_gp_regs!(self, dst); + } + + pub fn copy_from_raw(&mut self, src: &RawGeneralRegs) { + copy_gp_regs!(src, self); + } +} + +impl TryFrom<&CpuExceptionInfo> for PageFaultInfo { + // [`Err`] indicates that the [`CpuExceptionInfo`] is not a page fault, + // with no additional error information. + type Error = (); + + fn try_from(value: &CpuExceptionInfo) -> Result { + use riscv::register::scause::Exception; + + let required_perms = match value.cpu_exception() { + Exception::InstructionPageFault => VmPerms::EXEC, + Exception::LoadPageFault => VmPerms::READ, + Exception::StorePageFault => VmPerms::WRITE, + _ => return Err(()), + }; + + Ok(PageFaultInfo { + address: value.page_fault_addr, + required_perms, + }) + } +} diff --git a/kernel/src/arch/riscv/mod.rs b/kernel/src/arch/riscv/mod.rs new file mode 100644 index 00000000..e2a78f3c --- /dev/null +++ b/kernel/src/arch/riscv/mod.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub mod cpu; +pub mod signal; diff --git a/kernel/src/arch/riscv/signal.rs b/kernel/src/arch/riscv/signal.rs new file mode 100644 index 00000000..4abaa375 --- /dev/null +++ b/kernel/src/arch/riscv/signal.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 + +use ostd::cpu::{CpuExceptionInfo, UserContext}; + +use crate::process::signal::{sig_num::SigNum, signals::fault::FaultSignal, SignalContext}; + +impl SignalContext for UserContext { + fn set_arguments(&mut self, sig_num: SigNum, siginfo_addr: usize, ucontext_addr: usize) { + self.set_a0(sig_num.as_u8() as usize); + self.set_a1(siginfo_addr); + self.set_a2(ucontext_addr); + } +} + +impl From<&CpuExceptionInfo> for FaultSignal { + fn from(trap_info: &CpuExceptionInfo) -> Self { + unimplemented!() + } +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index be063ac6..22895eee 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -104,6 +104,7 @@ pub fn init() { util::random::init(); driver::init(); time::init(); + #[cfg(target_arch = "x86_64")] net::init(); sched::init(); fs::rootfs::init(boot::initramfs()).unwrap(); @@ -141,6 +142,7 @@ fn init_thread() { // Work queue should be initialized before interrupt is enabled, // in case any irq handler uses work queue as bottom half thread::work_queue::init(); + #[cfg(target_arch = "x86_64")] net::lazy_init(); fs::lazy_init(); ipc::init(); diff --git a/kernel/src/process/program_loader/elf/elf_file.rs b/kernel/src/process/program_loader/elf/elf_file.rs index c4f81993..a3afcdb7 100644 --- a/kernel/src/process/program_loader/elf/elf_file.rs +++ b/kernel/src/process/program_loader/elf/elf_file.rs @@ -186,6 +186,11 @@ pub struct HeaderPt2_64 { } fn check_elf_header(elf_header: &ElfHeader) -> Result<()> { + #[cfg(target_arch = "riscv64")] + const EXPECTED_ELF_MACHINE: header::Machine = header::Machine::RISC_V; + #[cfg(target_arch = "x86_64")] + const EXPECTED_ELF_MACHINE: header::Machine = header::Machine::X86_64; + // 64bit debug_assert_eq!(elf_header.pt1.class(), header::Class::SixtyFour); if elf_header.pt1.class() != header::Class::SixtyFour { @@ -201,14 +206,14 @@ fn check_elf_header(elf_header: &ElfHeader) -> Result<()> { // if elf_header.pt1.os_abi() != header::OsAbi::SystemV { // return Error::new(Errno::ENOEXEC); // } - // x86_64 architecture - debug_assert_eq!(elf_header.pt2.machine.as_machine(), header::Machine::X86_64); - if elf_header.pt2.machine.as_machine() != header::Machine::X86_64 { - return_errno_with_message!(Errno::ENOEXEC, "Not x86_64 executable"); + if elf_header.pt2.machine.as_machine() != EXPECTED_ELF_MACHINE { + return_errno_with_message!( + Errno::ENOEXEC, + "Executable could not be run on this architecture" + ); } // Executable file or shared object let elf_type = elf_header.pt2.type_.as_type(); - debug_assert!(elf_type == header::Type::Executable || elf_type == header::Type::SharedObject); if elf_type != header::Type::Executable && elf_type != header::Type::SharedObject { return_errno_with_message!(Errno::ENOEXEC, "Not executable file"); } diff --git a/kernel/src/syscall/arch/mod.rs b/kernel/src/syscall/arch/mod.rs index e9056b8f..2853e159 100644 --- a/kernel/src/syscall/arch/mod.rs +++ b/kernel/src/syscall/arch/mod.rs @@ -2,7 +2,12 @@ //! Implement the `syscall_dispatch` function and the const values of system call number such as `SYS_READ`. +#[cfg(target_arch = "riscv64")] +pub mod riscv; #[cfg(target_arch = "x86_64")] pub mod x86; + +#[cfg(target_arch = "riscv64")] +pub use self::riscv::*; #[cfg(target_arch = "x86_64")] pub use self::x86::*; diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs new file mode 100644 index 00000000..2b1d79ff --- /dev/null +++ b/kernel/src/syscall/arch/riscv.rs @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::syscall::{ + accept::{sys_accept, sys_accept4}, + access::sys_faccessat, + bind::sys_bind, + brk::sys_brk, + capget::sys_capget, + capset::sys_capset, + chdir::{sys_chdir, sys_fchdir}, + chmod::{sys_fchmod, sys_fchmodat}, + chown::{sys_fchown, sys_fchownat}, + chroot::sys_chroot, + clock_gettime::sys_clock_gettime, + clone::{sys_clone, sys_clone3}, + close::sys_close, + connect::sys_connect, + dup::{sys_dup, sys_dup3}, + epoll::{sys_epoll_create1, sys_epoll_ctl, sys_epoll_pwait}, + eventfd::sys_eventfd2, + execve::{sys_execve, sys_execveat}, + exit::sys_exit, + exit_group::sys_exit_group, + fallocate::sys_fallocate, + fcntl::sys_fcntl, + flock::sys_flock, + fsync::{sys_fdatasync, sys_fsync}, + futex::sys_futex, + getcwd::sys_getcwd, + getdents64::sys_getdents64, + getegid::sys_getegid, + geteuid::sys_geteuid, + getgid::sys_getgid, + getgroups::sys_getgroups, + getpeername::sys_getpeername, + getpgrp::sys_getpgrp, + getpid::sys_getpid, + getppid::sys_getppid, + getrandom::sys_getrandom, + getresgid::sys_getresgid, + getresuid::sys_getresuid, + getrusage::sys_getrusage, + getsid::sys_getsid, + getsockname::sys_getsockname, + getsockopt::sys_getsockopt, + gettid::sys_gettid, + gettimeofday::sys_gettimeofday, + getuid::sys_getuid, + impl_syscall_nums_and_dispatch_fn, + ioctl::sys_ioctl, + kill::sys_kill, + link::sys_linkat, + listen::sys_listen, + lseek::sys_lseek, + madvise::sys_madvise, + mkdir::sys_mkdirat, + mknod::sys_mknodat, + mmap::sys_mmap, + mount::sys_mount, + mprotect::sys_mprotect, + msync::sys_msync, + munmap::sys_munmap, + nanosleep::{sys_clock_nanosleep, sys_nanosleep}, + open::sys_openat, + pipe::sys_pipe2, + prctl::sys_prctl, + pread64::sys_pread64, + preadv::{sys_preadv, sys_preadv2, sys_readv}, + prlimit64::sys_prlimit64, + pselect6::sys_pselect6, + pwrite64::sys_pwrite64, + pwritev::{sys_pwritev, sys_pwritev2, sys_writev}, + read::sys_read, + readlink::sys_readlinkat, + recvfrom::sys_recvfrom, + recvmsg::sys_recvmsg, + rename::sys_renameat, + rt_sigaction::sys_rt_sigaction, + rt_sigpending::sys_rt_sigpending, + rt_sigprocmask::sys_rt_sigprocmask, + rt_sigsuspend::sys_rt_sigsuspend, + sched_getaffinity::sys_sched_getaffinity, + sched_yield::sys_sched_yield, + semctl::sys_semctl, + semget::sys_semget, + semop::{sys_semop, sys_semtimedop}, + sendfile::sys_sendfile, + sendmsg::sys_sendmsg, + sendto::sys_sendto, + set_get_priority::{sys_get_priority, sys_set_priority}, + set_robust_list::sys_set_robust_list, + set_tid_address::sys_set_tid_address, + setfsgid::sys_setfsgid, + setfsuid::sys_setfsuid, + setgid::sys_setgid, + setgroups::sys_setgroups, + setitimer::{sys_getitimer, sys_setitimer}, + setpgid::sys_setpgid, + setregid::sys_setregid, + setresgid::sys_setresgid, + setresuid::sys_setresuid, + setreuid::sys_setreuid, + setsid::sys_setsid, + setsockopt::sys_setsockopt, + setuid::sys_setuid, + shutdown::sys_shutdown, + sigaltstack::sys_sigaltstack, + socket::sys_socket, + socketpair::sys_socketpair, + stat::{sys_fstat, sys_fstatat}, + statfs::{sys_fstatfs, sys_statfs}, + symlink::sys_symlinkat, + sync::sys_sync, + tgkill::sys_tgkill, + timer_create::{sys_timer_create, sys_timer_delete}, + timer_settime::{sys_timer_gettime, sys_timer_settime}, + truncate::{sys_ftruncate, sys_truncate}, + umask::sys_umask, + umount::sys_umount, + uname::sys_uname, + unlink::sys_unlinkat, + utimens::sys_utimensat, + wait4::sys_wait4, + waitid::sys_waitid, + write::sys_write, +}; + +impl_syscall_nums_and_dispatch_fn! { + SYS_GETCWD = 17 => sys_getcwd(args[..2]); + SYS_EVENTFD2 = 19 => sys_eventfd2(args[..2]); + SYS_EPOLL_CREATE1 = 20 => sys_epoll_create1(args[..1]); + SYS_EPOLL_CTL = 21 => sys_epoll_ctl(args[..4]); + SYS_EPOLL_PWAIT = 22 => sys_epoll_pwait(args[..6]); + SYS_DUP = 23 => sys_dup(args[..1]); + SYS_DUP3 = 24 => sys_dup3(args[..3]); + SYS_FCNTL = 25 => sys_fcntl(args[..3]); + SYS_IOCTL = 29 => sys_ioctl(args[..3]); + SYS_FLOCK = 32 => sys_flock(args[..2]); + SYS_MKNODAT = 33 => sys_mknodat(args[..4]); + SYS_MKDIRAT = 34 => sys_mkdirat(args[..3]); + SYS_UNLINKAT = 35 => sys_unlinkat(args[..3]); + SYS_SYMLINKAT = 36 => sys_symlinkat(args[..3]); + SYS_LINKAT = 37 => sys_linkat(args[..5]); + SYS_RENAMEAT = 38 => sys_renameat(args[..4]); + SYS_UMOUNT = 39 => sys_umount(args[..2]); + SYS_MOUNT = 40 => sys_mount(args[..5]); + SYS_STATFS = 43 => sys_statfs(args[..2]); + SYS_FSTATFS = 44 => sys_fstatfs(args[..2]); + SYS_TRUNCATE = 45 => sys_truncate(args[..2]); + SYS_FTRUNCATE = 46 => sys_ftruncate(args[..2]); + SYS_FALLOCATE = 47 => sys_fallocate(args[..4]); + SYS_FACCESSAT = 48 => sys_faccessat(args[..3]); + SYS_CHDIR = 49 => sys_chdir(args[..1]); + SYS_FCHDIR = 50 => sys_fchdir(args[..1]); + SYS_CHROOT = 51 => sys_chroot(args[..1]); + SYS_FCHMOD = 52 => sys_fchmod(args[..2]); + SYS_FCHMODAT = 53 => sys_fchmodat(args[..3]); + SYS_FCHOWNAT = 54 => sys_fchownat(args[..5]); + SYS_FCHOWN = 55 => sys_fchown(args[..3]); + SYS_OPENAT = 56 => sys_openat(args[..4]); + SYS_CLOSE = 57 => sys_close(args[..1]); + SYS_PIPE2 = 59 => sys_pipe2(args[..2]); + SYS_GETDENTS64 = 61 => sys_getdents64(args[..3]); + SYS_LSEEK = 62 => sys_lseek(args[..3]); + SYS_READ = 63 => sys_read(args[..3]); + SYS_WRITE = 64 => sys_write(args[..3]); + SYS_READV = 65 => sys_readv(args[..3]); + SYS_WRITEV = 66 => sys_writev(args[..3]); + SYS_PREAD64 = 67 => sys_pread64(args[..4]); + SYS_PWRITE64 = 68 => sys_pwrite64(args[..4]); + SYS_PREADV = 69 => sys_preadv(args[..4]); + SYS_PWRITEV = 70 => sys_pwritev(args[..4]); + SYS_SENDFILE64 = 71 => sys_sendfile(args[..4]); + SYS_PSELECT6 = 72 => sys_pselect6(args[..6]); + SYS_READLINKAT = 78 => sys_readlinkat(args[..4]); + SYS_NEWFSTATAT = 79 => sys_fstatat(args[..4]); + SYS_NEWFSTAT = 80 => sys_fstat(args[..2]); + SYS_SYNC = 81 => sys_sync(args[..0]); + SYS_FSYNC = 82 => sys_fsync(args[..1]); + SYS_FDATASYNC = 83 => sys_fdatasync(args[..1]); + SYS_CAPGET = 90 => sys_capget(args[..2]); + SYS_CAPSET = 91 => sys_capset(args[..2]); + SYS_EXIT = 93 => sys_exit(args[..1]); + SYS_EXIT_GROUP = 94 => sys_exit_group(args[..1]); + SYS_WAITID = 95 => sys_waitid(args[..5]); + SYS_SET_TID_ADDRESS = 96 => sys_set_tid_address(args[..1]); + SYS_FUTEX = 98 => sys_futex(args[..6]); + SYS_SET_ROBUST_LIST = 99 => sys_set_robust_list(args[..2]); + SYS_NANOSLEEP = 101 => sys_nanosleep(args[..2]); + SYS_GETITIMER = 102 => sys_getitimer(args[..2]); + SYS_SETITIMER = 103 => sys_setitimer(args[..3]); + SYS_TIMER_CREATE = 107 => sys_timer_create(args[..3]); + SYS_TIMER_DELETE = 111 => sys_timer_delete(args[..1]); + SYS_SCHED_GETAFFINITY = 123 => sys_sched_getaffinity(args[..3]); + SYS_SCHED_YIELD = 124 => sys_sched_yield(args[..0]); + SYS_KILL = 129 => sys_kill(args[..2]); + SYS_TGKILL = 131 => sys_tgkill(args[..3]); + SYS_SIGALTSTACK = 132 => sys_sigaltstack(args[..2]); + SYS_RT_SIGSUSPEND = 133 => sys_rt_sigsuspend(args[..2]); + SYS_RT_SIGACTION = 134 => sys_rt_sigaction(args[..4]); + SYS_RT_SIGPROCMASK = 135 => sys_rt_sigprocmask(args[..4]); + SYS_RT_SIGPENDING = 136 => sys_rt_sigpending(args[..2]); + SYS_SET_PRIORITY = 140 => sys_set_priority(args[..3]); + SYS_GET_PRIORITY = 141 => sys_get_priority(args[..2]); + SYS_SETREGID = 143 => sys_setregid(args[..2]); + SYS_SETGID = 144 => sys_setgid(args[..1]); + SYS_SETREUID = 145 => sys_setreuid(args[..2]); + SYS_SETUID = 146 => sys_setuid(args[..1]); + SYS_SETRESUID = 147 => sys_setresuid(args[..3]); + SYS_GETRESUID = 148 => sys_getresuid(args[..3]); + SYS_SETRESGID = 149 => sys_setresgid(args[..3]); + SYS_GETRESGID = 150 => sys_getresgid(args[..3]); + SYS_SETFSUID = 151 => sys_setfsuid(args[..1]); + SYS_SETFSGID = 152 => sys_setfsgid(args[..1]); + SYS_SETPGID = 154 => sys_setpgid(args[..2]); + SYS_GETPGRP = 155 => sys_getpgrp(args[..0]); + SYS_GETSID = 156 => sys_getsid(args[..1]); + SYS_SETSID = 157 => sys_setsid(args[..0]); + SYS_GETGROUPS = 158 => sys_getgroups(args[..2]); + SYS_SETGROUPS = 159 => sys_setgroups(args[..2]); + SYS_NEWUNAME = 160 => sys_uname(args[..1]); + SYS_GETRUSAGE = 165 => sys_getrusage(args[..2]); + SYS_UMASK = 166 => sys_umask(args[..1]); + SYS_PRCTL = 167 => sys_prctl(args[..5]); + SYS_GETTIMEOFDAY = 169 => sys_gettimeofday(args[..1]); + SYS_GETPID = 172 => sys_getpid(args[..0]); + SYS_GETPPID = 173 => sys_getppid(args[..0]); + SYS_GETUID = 174 => sys_getuid(args[..0]); + SYS_GETEUID = 175 => sys_geteuid(args[..0]); + SYS_GETGID = 176 => sys_getgid(args[..0]); + SYS_GETEGID = 177 => sys_getegid(args[..0]); + SYS_GETTID = 178 => sys_gettid(args[..0]); + SYS_SEMGET = 190 => sys_semget(args[..3]); + SYS_SEMCTL = 191 => sys_semctl(args[..4]); + SYS_SEMOP = 193 => sys_semop(args[..3]); + SYS_SOCKET = 198 => sys_socket(args[..3]); + SYS_SOCKETPAIR = 199 => sys_socketpair(args[..4]); + SYS_BIND = 200 => sys_bind(args[..3]); + SYS_LISTEN = 201 => sys_listen(args[..2]); + SYS_ACCEPT = 202 => sys_accept(args[..3]); + SYS_CONNECT = 203 => sys_connect(args[..3]); + SYS_GETSOCKNAME = 204 => sys_getsockname(args[..3]); + SYS_GETPEERNAME = 205 => sys_getpeername(args[..3]); + SYS_SENDTO = 206 => sys_sendto(args[..6]); + SYS_RECVFROM = 207 => sys_recvfrom(args[..6]); + SYS_SETSOCKOPT = 208 => sys_setsockopt(args[..5]); + SYS_GETSOCKOPT = 209 => sys_getsockopt(args[..5]); + SYS_SHUTDOWN = 210 => sys_shutdown(args[..2]); + SYS_SENDMSG = 211 => sys_sendmsg(args[..3]); + SYS_RECVMSG = 212 => sys_recvmsg(args[..3]); + SYS_BRK = 214 => sys_brk(args[..1]); + SYS_MUNMAP = 215 => sys_munmap(args[..2]); + SYS_CLONE = 220 => sys_clone(args[..5], &user_ctx); + SYS_EXECVE = 221 => sys_execve(args[..3], &mut user_ctx); + SYS_MMAP = 222 => sys_mmap(args[..6]); + SYS_MPROTECT = 226 => sys_mprotect(args[..3]); + SYS_MSYNC = 227 => sys_msync(args[..3]); + SYS_MADVISE = 233 => sys_madvise(args[..3]); + SYS_ACCEPT4 = 242 => sys_accept4(args[..4]); + SYS_WAIT4 = 260 => sys_wait4(args[..4]); + SYS_PRLIMIT64 = 261 => sys_prlimit64(args[..4]); + SYS_GETRANDOM = 278 => sys_getrandom(args[..3]); + SYS_EXECVEAT = 281 => sys_execveat(args[..5], &mut user_ctx); + SYS_PREADV2 = 286 => sys_preadv2(args[..5]); + SYS_PWRITEV2 = 287 => sys_pwritev2(args[..5]); + SYS_CLOCK_GETTIME = 403 => sys_clock_gettime(args[..2]); + SYS_CLOCK_NANOSLEEP = 407 => sys_clock_nanosleep(args[..4]); + SYS_TIMER_GETTIME = 408 => sys_timer_gettime(args[..2]); + SYS_TIMER_SETTIME = 409 => sys_timer_settime(args[..4]); + SYS_UTIMENSAT = 412 => sys_utimensat(args[..4]); + SYS_SEMTIMEDOP = 420 => sys_semtimedop(args[..4]); + SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx); +} diff --git a/kernel/src/util/random.rs b/kernel/src/util/random.rs index 97300582..349a5862 100644 --- a/kernel/src/util/random.rs +++ b/kernel/src/util/random.rs @@ -37,6 +37,14 @@ pub fn init() { tail.copy_from_slice(&src[..n]); } + RNG.call_once(|| SpinLock::new(StdRng::from_seed(seed))); + } else if #[cfg(target_arch = "riscv64")] { + use rand::SeedableRng; + use ostd::arch::boot::DEVICE_TREE; + + let chosen = DEVICE_TREE.get().unwrap().find_node("/chosen").unwrap(); + let seed = chosen.property("rng-seed").unwrap().value.try_into().unwrap(); + RNG.call_once(|| SpinLock::new(StdRng::from_seed(seed))); } else { compile_error!("unsupported target"); diff --git a/osdk/src/base_crate/mod.rs b/osdk/src/base_crate/mod.rs index f27cecfe..32fe2ddc 100644 --- a/osdk/src/base_crate/mod.rs +++ b/osdk/src/base_crate/mod.rs @@ -78,7 +78,7 @@ pub fn new_base_crate( } // TODO: currently just x86_64 works; add support for other architectures // here when OSTD is ready - include_linker_script!(["x86_64.ld"]); + include_linker_script!(["x86_64.ld", "riscv64.ld"]); // Overwrite the main.rs file let main_rs = include_str!("main.rs.template"); diff --git a/osdk/src/base_crate/riscv64.ld.template b/osdk/src/base_crate/riscv64.ld.template new file mode 100644 index 00000000..d701fd36 --- /dev/null +++ b/osdk/src/base_crate/riscv64.ld.template @@ -0,0 +1,64 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) +KERNEL_LMA = 0x80200000; +KERNEL_VMA = 0xffffffff80200000; +KERNEL_VMA_OFFSET = KERNEL_VMA - KERNEL_LMA; + +SECTIONS +{ + . = KERNEL_VMA; + + PROVIDE(__executable_start = .); + __kernel_start = .; + + .text : AT(ADDR(.text) - KERNEL_VMA_OFFSET) { + *(.text.entry) + *(.text .text.*) + PROVIDE(__etext = .); + } + + .rodata : AT(ADDR(.rodata) - KERNEL_VMA_OFFSET) { *(.rodata .rodata.*) } + + .eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA_OFFSET) { + PROVIDE(__GNU_EH_FRAME_HDR = .); + KEEP(*(.eh_frame_hdr .eh_frame_hdr.*)) + } + . = ALIGN(8); + .eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA_OFFSET) { + PROVIDE(__eh_frame = .); + KEEP(*(.eh_frame .eh_frame.*)) + } + + .init_array : AT(ADDR(.init_array) - KERNEL_VMA_OFFSET) { + __sinit_array = .; + KEEP(*(SORT(.init_array .init_array.*))) + __einit_array = .; + } + + . = DATA_SEGMENT_RELRO_END(0, .); + + .data : AT(ADDR(.data) - KERNEL_VMA_OFFSET) { *(.data .data.*) } + + # The CPU local data storage. It is readable and writable for the bootstrap + # processor, while it would be copied to other dynamically allocated memory + # areas for the application processors. + .cpu_local : AT(ADDR(.cpu_local) - KERNEL_VMA_OFFSET) { + __cpu_local_start = .; + KEEP(*(SORT(.cpu_local))) + __cpu_local_end = .; + } + + /* boot stack (in entry.S) */ + .stack : AT(ADDR(.stack) - KERNEL_VMA_OFFSET) { + *(.bss.stack) + } + + .bss : AT(ADDR(.bss) - KERNEL_VMA_OFFSET) { + __bss = .; + *(.bss .bss.*) + __bss_end = .; + } + + . = DATA_SEGMENT_END(.); + __kernel_end = .; +} diff --git a/ostd/Cargo.toml b/ostd/Cargo.toml index 49ce01f1..f6a78d17 100644 --- a/ostd/Cargo.toml +++ b/ostd/Cargo.toml @@ -57,6 +57,11 @@ iced-x86 = { version = "1.21.0", default-features = false, features = [ ], optional = true } tdx-guest = { version = "0.1.7", optional = true } +[target.riscv64gc-unknown-none-elf.dependencies] +riscv = { version = "0.11.1", features = ["s-mode"] } +sbi-rt = "0.0.3" +fdt = { version = "0.1.5", features = ["pretty-printing"] } + [features] default = ["cvm_guest", "log_color"] log_color = ["dep:owo-colors"] diff --git a/ostd/src/arch/mod.rs b/ostd/src/arch/mod.rs index 123b08be..cdedf39d 100644 --- a/ostd/src/arch/mod.rs +++ b/ostd/src/arch/mod.rs @@ -4,8 +4,12 @@ //! //! Each architecture that Asterinas supports may contain a submodule here. +#[cfg(target_arch = "riscv64")] +pub mod riscv; #[cfg(target_arch = "x86_64")] pub mod x86; +#[cfg(target_arch = "riscv64")] +pub use self::riscv::*; #[cfg(target_arch = "x86_64")] pub use self::x86::*; diff --git a/ostd/src/arch/riscv/boot/boot.S b/ostd/src/arch/riscv/boot/boot.S new file mode 100644 index 00000000..23a40133 --- /dev/null +++ b/ostd/src/arch/riscv/boot/boot.S @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MPL-2.0 */ + +.section .text.entry +.globl _start +_start: + # Arguments passed from SBI: + # a0 = hart id + # a1 = device tree paddr (not touched) + + # 1. enable paging + # setting up 1st pagetable + # entry = (PPN(boot_pagetable_2nd) << 10) | 0x01 # V + la t1, boot_pagetable + li t0, 8 * 511 + add t1, t1, t0 + la t0, boot_pagetable_2nd + srli t0, t0, 2 + ori t0, t0, 0x01 + sd t0, 0(t1) + + la t0, boot_pagetable + li t1, 9 << 60 + srli t0, t0, 12 + or t0, t0, t1 + csrw satp, t0 + sfence.vma + + # 2. set sp (BSP only) + lga sp, boot_stack_top + + # 3. jump to rust riscv_boot + lga t0, riscv_boot + jr t0 + + +.section .bss.stack + +.globl boot_stack_bottom +boot_stack_bottom: + .space 0x40000 # 64 KiB + +.globl boot_stack_top +boot_stack_top: + + +.section .data + +.align 12 +boot_pagetable: + .quad (0x00000 << 10) | 0xcf # VRWXAD + .zero 8 * 255 + .quad (0x00000 << 10) | 0xcf # VRWXAD + .zero 8 * 254 + .quad 0 # To-Be-Assign + +boot_pagetable_2nd: + # 0x0000_00ff_8000_0000 -> 0x0000_0000_8000_0000 + .zero 8 * 508 + .quad (0x00000 << 10) | 0xcf # VRWXAD + .quad (0x40000 << 10) | 0xcf # VRWXAD + .quad (0x80000 << 10) | 0xcf # VRWXAD + .quad 0 diff --git a/ostd/src/arch/riscv/boot/mod.rs b/ostd/src/arch/riscv/boot/mod.rs new file mode 100644 index 00000000..30b0d83f --- /dev/null +++ b/ostd/src/arch/riscv/boot/mod.rs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The RISC-V boot module defines the entrypoints of Asterinas. + +pub mod smp; + +use alloc::{string::String, vec::Vec}; +use core::arch::global_asm; + +use fdt::Fdt; +use spin::Once; + +use crate::{ + boot::{ + kcmdline::KCmdlineArg, + memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType}, + BootloaderAcpiArg, BootloaderFramebufferArg, + }, + early_println, + mm::paddr_to_vaddr, +}; + +global_asm!(include_str!("boot.S")); + +/// The Flattened Device Tree of the platform. +pub static DEVICE_TREE: Once = Once::new(); + +fn init_bootloader_name(bootloader_name: &'static Once) { + bootloader_name.call_once(|| "Unknown".into()); +} + +fn init_kernel_commandline(kernel_cmdline: &'static Once) { + let bootargs = DEVICE_TREE.get().unwrap().chosen().bootargs().unwrap_or(""); + kernel_cmdline.call_once(|| bootargs.into()); +} + +fn init_initramfs(initramfs: &'static Once<&'static [u8]>) { + let chosen = DEVICE_TREE.get().unwrap().find_node("/chosen").unwrap(); + let initrd_start = chosen + .property("linux,initrd-start") + .unwrap() + .as_usize() + .unwrap(); + let initrd_end = chosen + .property("linux,initrd-end") + .unwrap() + .as_usize() + .unwrap(); + + let base_va = paddr_to_vaddr(initrd_start); + let length = initrd_end - initrd_start; + initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) }); +} + +fn init_acpi_arg(acpi: &'static Once) { + acpi.call_once(|| BootloaderAcpiArg::NotProvided); +} + +fn init_framebuffer_info(_framebuffer_arg: &'static Once) {} + +fn init_memory_regions(memory_regions: &'static Once>) { + let mut regions = Vec::::new(); + + for region in DEVICE_TREE.get().unwrap().memory().regions() { + if region.size.unwrap_or(0) > 0 { + regions.push(MemoryRegion::new( + region.starting_address as usize, + region.size.unwrap(), + MemoryRegionType::Usable, + )); + } + } + + if let Some(node) = DEVICE_TREE.get().unwrap().find_node("/reserved-memory") { + for child in node.children() { + if let Some(reg_iter) = child.reg() { + for region in reg_iter { + regions.push(MemoryRegion::new( + region.starting_address as usize, + region.size.unwrap(), + MemoryRegionType::Reserved, + )); + } + } + } + } + + // Add the kernel region. + regions.push(MemoryRegion::kernel()); + + // Add the initramfs region. + let chosen = DEVICE_TREE.get().unwrap().find_node("/chosen").unwrap(); + let initrd_start = chosen + .property("linux,initrd-start") + .unwrap() + .as_usize() + .unwrap(); + let initrd_end = chosen + .property("linux,initrd-end") + .unwrap() + .as_usize() + .unwrap(); + let length = initrd_end - initrd_start; + regions.push(MemoryRegion::new( + initrd_start, + length, + MemoryRegionType::Module, + )); + + memory_regions.call_once(|| non_overlapping_regions_from(regions.as_ref())); +} + +/// The entry point of the Rust code portion of Asterinas. +#[no_mangle] +pub extern "C" fn riscv_boot(_hart_id: usize, device_tree_paddr: usize) -> ! { + early_println!("Enter riscv_boot"); + + let device_tree_ptr = paddr_to_vaddr(device_tree_paddr) as *const u8; + let fdt = unsafe { fdt::Fdt::from_ptr(device_tree_ptr).unwrap() }; + DEVICE_TREE.call_once(|| fdt); + + crate::boot::register_boot_init_callbacks( + init_bootloader_name, + init_kernel_commandline, + init_initramfs, + init_acpi_arg, + init_framebuffer_info, + init_memory_regions, + ); + + crate::boot::call_ostd_main(); +} diff --git a/ostd/src/arch/riscv/boot/smp.rs b/ostd/src/arch/riscv/boot/smp.rs new file mode 100644 index 00000000..7b9375c7 --- /dev/null +++ b/ostd/src/arch/riscv/boot/smp.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Multiprocessor Boot Support + +pub(crate) fn get_num_processors() -> Option { + Some(1) +} + +pub(crate) fn bringup_all_aps() { + // TODO +} diff --git a/ostd/src/arch/riscv/cpu/local.rs b/ostd/src/arch/riscv/cpu/local.rs new file mode 100644 index 00000000..6a172a95 --- /dev/null +++ b/ostd/src/arch/riscv/cpu/local.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Architecture dependent CPU-local information utilities. + +pub(crate) unsafe fn set_base(addr: u64) { + core::arch::asm!( + "mv gp, {addr}", + addr = in(reg) addr, + options(preserves_flags, nostack) + ); +} + +pub(crate) fn get_base() -> u64 { + let mut gp; + unsafe { + core::arch::asm!( + "mv {gp}, gp", + gp = out(reg) gp, + options(preserves_flags, nostack) + ); + } + gp +} diff --git a/ostd/src/arch/riscv/cpu/mod.rs b/ostd/src/arch/riscv/cpu/mod.rs new file mode 100644 index 00000000..3bdc4d42 --- /dev/null +++ b/ostd/src/arch/riscv/cpu/mod.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! CPU + +pub mod local; + +use core::fmt::Debug; + +use riscv::register::scause::{Exception, Trap}; + +pub use super::trap::GeneralRegs as RawGeneralRegs; +use super::trap::{TrapFrame, UserContext as RawUserContext}; +use crate::user::{ReturnReason, UserContextApi, UserContextApiInternal}; + +/// Cpu context, including both general-purpose registers and floating-point registers. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct UserContext { + user_context: RawUserContext, + trap: Trap, + fp_regs: (), // TODO + cpu_exception_info: CpuExceptionInfo, +} + +/// CPU exception information. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct CpuExceptionInfo { + /// The type of the exception. + pub code: Exception, + /// The error code associated with the exception. + pub page_fault_addr: usize, + pub error_code: usize, // TODO +} + +impl Default for UserContext { + fn default() -> Self { + UserContext { + user_context: RawUserContext::default(), + trap: Trap::Exception(Exception::Unknown), + fp_regs: (), + cpu_exception_info: CpuExceptionInfo::default(), + } + } +} + +impl Default for CpuExceptionInfo { + fn default() -> Self { + CpuExceptionInfo { + code: Exception::Unknown, + page_fault_addr: 0, + error_code: 0, + } + } +} + +impl CpuExceptionInfo { + /// Get corresponding CPU exception + pub fn cpu_exception(&self) -> CpuException { + self.code + } +} + +impl UserContext { + /// Returns a reference to the general registers. + pub fn general_regs(&self) -> &RawGeneralRegs { + &self.user_context.general + } + + /// Returns a mutable reference to the general registers + pub fn general_regs_mut(&mut self) -> &mut RawGeneralRegs { + &mut self.user_context.general + } + + /// Returns the trap information. + pub fn trap_information(&self) -> &CpuExceptionInfo { + &self.cpu_exception_info + } + + /// Returns a reference to the floating point registers + pub fn fp_regs(&self) -> &() { + &self.fp_regs + } + + /// Returns a mutable reference to the floating point registers + pub fn fp_regs_mut(&mut self) -> &mut () { + &mut self.fp_regs + } + + /// Sets thread-local storage pointer. + pub fn set_tls_pointer(&mut self, tls: usize) { + self.set_tp(tls) + } + + /// Gets thread-local storage pointer. + pub fn tls_pointer(&self) -> usize { + self.tp() + } + + /// Activates thread-local storage pointer on the current CPU. + pub fn activate_tls_pointer(&self) { + // No-op + } +} + +impl UserContextApiInternal for UserContext { + fn execute(&mut self, mut has_kernel_event: F) -> ReturnReason + where + F: FnMut() -> bool, + { + let ret = loop { + self.user_context.run(); + match riscv::register::scause::read().cause() { + Trap::Interrupt(_) => todo!(), + Trap::Exception(Exception::UserEnvCall) => { + self.user_context.sepc += 4; + break ReturnReason::UserSyscall; + } + Trap::Exception(e) => { + let stval = riscv::register::stval::read(); + log::trace!("Exception, scause: {e:?}, stval: {stval:#x?}"); + self.cpu_exception_info = CpuExceptionInfo { + code: e, + page_fault_addr: stval, + error_code: 0, + }; + break ReturnReason::UserException; + } + } + + if has_kernel_event() { + break ReturnReason::KernelEvent; + } + }; + + crate::arch::irq::enable_local(); + ret + } + + fn as_trap_frame(&self) -> TrapFrame { + TrapFrame { + general: self.user_context.general, + sstatus: self.user_context.sstatus, + sepc: self.user_context.sepc, + } + } +} + +impl UserContextApi for UserContext { + fn trap_number(&self) -> usize { + todo!() + } + + fn trap_error_code(&self) -> usize { + todo!() + } + + fn instruction_pointer(&self) -> usize { + self.user_context.sepc + } + + fn set_instruction_pointer(&mut self, ip: usize) { + self.user_context.set_ip(ip); + } + + fn stack_pointer(&self) -> usize { + self.user_context.get_sp() + } + + fn set_stack_pointer(&mut self, sp: usize) { + self.user_context.set_sp(sp); + } +} + +macro_rules! cpu_context_impl_getter_setter { + ( $( [ $field: ident, $setter_name: ident] ),*) => { + impl UserContext { + $( + #[doc = concat!("Gets the value of ", stringify!($field))] + #[inline(always)] + pub fn $field(&self) -> usize { + self.user_context.general.$field + } + + #[doc = concat!("Sets the value of ", stringify!($field))] + #[inline(always)] + pub fn $setter_name(&mut self, $field: usize) { + self.user_context.general.$field = $field; + } + )* + } + }; +} + +cpu_context_impl_getter_setter!( + [ra, set_ra], + [sp, set_sp], + [gp, set_gp], + [tp, set_tp], + [t0, set_t0], + [t1, set_t1], + [t2, set_t2], + [s0, set_s0], + [s1, set_s1], + [a0, set_a0], + [a1, set_a1], + [a2, set_a2], + [a3, set_a3], + [a4, set_a4], + [a5, set_a5], + [a6, set_a6], + [a7, set_a7], + [s2, set_s2], + [s3, set_s3], + [s4, set_s4], + [s5, set_s5], + [s6, set_s6], + [s7, set_s7], + [s8, set_s8], + [s9, set_s9], + [s10, set_s10], + [s11, set_s11], + [t3, set_t3], + [t4, set_t4], + [t5, set_t5], + [t6, set_t6] +); + +/// CPU exception. +pub type CpuException = Exception; diff --git a/ostd/src/arch/riscv/device/io_port.rs b/ostd/src/arch/riscv/device/io_port.rs new file mode 100644 index 00000000..ee70bde7 --- /dev/null +++ b/ostd/src/arch/riscv/device/io_port.rs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! I/O port access. + +use core::marker::PhantomData; + +pub struct WriteOnlyAccess; +pub struct ReadWriteAccess; + +pub trait IoPortWriteAccess {} +pub trait IoPortReadAccess {} + +impl IoPortWriteAccess for WriteOnlyAccess {} +impl IoPortWriteAccess for ReadWriteAccess {} +impl IoPortReadAccess for ReadWriteAccess {} + +pub trait PortRead: Sized { + unsafe fn read_from_port(_port: u16) -> Self { + unimplemented!() + } +} + +pub trait PortWrite: Sized { + unsafe fn write_to_port(_port: u16, _value: Self) { + unimplemented!() + } +} + +impl PortRead for u8 {} +impl PortWrite for u8 {} +impl PortRead for u16 {} +impl PortWrite for u16 {} +impl PortRead for u32 {} +impl PortWrite for u32 {} + +/// An I/O port, representing a specific address in the I/O address of x86. +/// +/// The following code shows and example to read and write u32 value to an I/O port: +/// +/// ```rust +/// static PORT: IoPort = unsafe { IoPort::new(0x12) }; +/// +/// fn port_value_increase(){ +/// PORT.write(PORT.read() + 1) +/// } +/// ``` +/// +pub struct IoPort { + port: u16, + value_marker: PhantomData, + access_marker: PhantomData, +} + +impl IoPort { + /// Create an I/O port. + /// + /// # Safety + /// + /// This function is marked unsafe as creating an I/O port is considered + /// a privileged operation. + pub const unsafe fn new(port: u16) -> Self { + Self { + port, + value_marker: PhantomData, + access_marker: PhantomData, + } + } +} + +impl IoPort { + /// Reads from the I/O port + #[inline] + pub fn read(&self) -> T { + unsafe { PortRead::read_from_port(self.port) } + } +} + +impl IoPort { + /// Writes to the I/O port + #[inline] + pub fn write(&self, value: T) { + unsafe { PortWrite::write_to_port(self.port, value) } + } +} diff --git a/ostd/src/arch/riscv/device/mod.rs b/ostd/src/arch/riscv/device/mod.rs new file mode 100644 index 00000000..22252681 --- /dev/null +++ b/ostd/src/arch/riscv/device/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Device-related APIs. +//! This module mainly contains the APIs that should exposed to the device driver like PCI, RTC + +pub mod io_port; diff --git a/ostd/src/arch/riscv/iommu/mod.rs b/ostd/src/arch/riscv/iommu/mod.rs new file mode 100644 index 00000000..a8b2972c --- /dev/null +++ b/ostd/src/arch/riscv/iommu/mod.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The IOMMU support. + +use crate::mm::{dma::Daddr, Paddr}; + +/// An enumeration representing possible errors related to IOMMU. +#[derive(Debug)] +pub enum IommuError { + /// No IOMMU is available. + NoIommu, +} + +/// +/// # Safety +/// +/// Mapping an incorrect address may lead to a kernel data leak. +pub(crate) unsafe fn map(_daddr: Daddr, _paddr: Paddr) -> Result<(), IommuError> { + Err(IommuError::NoIommu) +} + +pub(crate) fn unmap(_daddr: Daddr) -> Result<(), IommuError> { + Err(IommuError::NoIommu) +} + +pub(crate) fn init() -> Result<(), IommuError> { + // TODO: We will support IOMMU on RISC-V + Err(IommuError::NoIommu) +} + +pub(crate) fn has_dma_remapping() -> bool { + false +} diff --git a/ostd/src/arch/riscv/irq.rs b/ostd/src/arch/riscv/irq.rs new file mode 100644 index 00000000..117257bb --- /dev/null +++ b/ostd/src/arch/riscv/irq.rs @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupts. + +use alloc::{boxed::Box, fmt::Debug, sync::Arc, vec::Vec}; + +use id_alloc::IdAlloc; +use spin::Once; + +use crate::{ + sync::{Mutex, PreemptDisabled, SpinLock, SpinLockGuard}, + trap::TrapFrame, +}; + +/// The global allocator for software defined IRQ lines. +pub(crate) static IRQ_ALLOCATOR: Once> = Once::new(); + +pub(crate) static IRQ_LIST: Once> = Once::new(); + +pub(crate) fn init() { + let mut list: Vec = Vec::new(); + for i in 0..256 { + list.push(IrqLine { + irq_num: i as u8, + callback_list: SpinLock::new(Vec::new()), + }); + } + IRQ_LIST.call_once(|| list); + CALLBACK_ID_ALLOCATOR.call_once(|| Mutex::new(IdAlloc::with_capacity(256))); + IRQ_ALLOCATOR.call_once(|| SpinLock::new(IdAlloc::with_capacity(256))); +} + +pub(crate) fn enable_local() { + unsafe { riscv::interrupt::enable() } +} + +pub(crate) fn disable_local() { + riscv::interrupt::disable(); +} + +pub(crate) fn is_local_enabled() -> bool { + riscv::register::sstatus::read().sie() +} + +static CALLBACK_ID_ALLOCATOR: Once> = Once::new(); + +pub struct CallbackElement { + function: Box, + id: usize, +} + +impl CallbackElement { + pub fn call(&self, element: &TrapFrame) { + self.function.call((element,)); + } +} + +impl Debug for CallbackElement { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CallbackElement") + .field("id", &self.id) + .finish() + } +} + +/// An interrupt request (IRQ) line. +#[derive(Debug)] +pub(crate) struct IrqLine { + pub(crate) irq_num: u8, + pub(crate) callback_list: SpinLock>, +} + +impl IrqLine { + /// Acquire an interrupt request line. + /// + /// # Safety + /// + /// This function is marked unsafe as manipulating interrupt lines is + /// considered a dangerous operation. + #[allow(clippy::redundant_allocation)] + pub unsafe fn acquire(irq_num: u8) -> Arc<&'static Self> { + Arc::new(IRQ_LIST.get().unwrap().get(irq_num as usize).unwrap()) + } + + /// Get the IRQ number. + pub fn num(&self) -> u8 { + self.irq_num + } + + pub fn callback_list( + &self, + ) -> SpinLockGuard, PreemptDisabled> { + self.callback_list.lock() + } + + /// Register a callback that will be invoked when the IRQ is active. + /// + /// A handle to the callback is returned. Dropping the handle + /// automatically unregisters the callback. + /// + /// For each IRQ line, multiple callbacks may be registered. + pub fn on_active(&self, callback: F) -> IrqCallbackHandle + where + F: Fn(&TrapFrame) + Sync + Send + 'static, + { + let allocate_id = CALLBACK_ID_ALLOCATOR.get().unwrap().lock().alloc().unwrap(); + self.callback_list.lock().push(CallbackElement { + function: Box::new(callback), + id: allocate_id, + }); + IrqCallbackHandle { + irq_num: self.irq_num, + id: allocate_id, + } + } +} + +/// The handle to a registered callback for a IRQ line. +/// +/// When the handle is dropped, the callback will be unregistered automatically. +#[must_use] +#[derive(Debug)] +pub struct IrqCallbackHandle { + irq_num: u8, + id: usize, +} + +impl Drop for IrqCallbackHandle { + fn drop(&mut self) { + let mut a = IRQ_LIST + .get() + .unwrap() + .get(self.irq_num as usize) + .unwrap() + .callback_list + .lock(); + a.retain(|item| item.id != self.id); + CALLBACK_ID_ALLOCATOR.get().unwrap().lock().free(self.id); + } +} + +/// Sends a general inter-processor interrupt (IPI) to the specified CPU. +/// +/// # Safety +/// +/// The caller must ensure that the CPU ID and the interrupt number corresponds +/// to a safe function to call. +pub(crate) unsafe fn send_ipi(cpu_id: u32, irq_num: u8) { + unimplemented!() +} diff --git a/ostd/src/arch/riscv/mm/mod.rs b/ostd/src/arch/riscv/mm/mod.rs new file mode 100644 index 00000000..72dfc322 --- /dev/null +++ b/ostd/src/arch/riscv/mm/mod.rs @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::fmt; +use core::ops::Range; + +use crate::{ + mm::{ + page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags}, + page_table::PageTableEntryTrait, + Paddr, PagingConstsTrait, PagingLevel, Vaddr, PAGE_SIZE, + }, + Pod, +}; + +pub(crate) const NR_ENTRIES_PER_PAGE: usize = 512; + +#[derive(Clone, Debug, Default)] +pub struct PagingConsts {} + +impl PagingConstsTrait for PagingConsts { + const BASE_PAGE_SIZE: usize = 4096; + const NR_LEVELS: PagingLevel = 4; + const ADDRESS_WIDTH: usize = 48; + const HIGHEST_TRANSLATION_LEVEL: PagingLevel = 4; + const PTE_SIZE: usize = core::mem::size_of::(); +} + +bitflags::bitflags! { + #[derive(Pod)] + #[repr(C)] + /// Possible flags for a page table entry. + pub struct PageTableFlags: usize { + /// Specifies whether the mapped frame or page table is valid. + const VALID = 1 << 0; + /// Controls whether reads to the mapped frames are allowed. + const READABLE = 1 << 1; + /// Controls whether writes to the mapped frames are allowed. + const WRITABLE = 1 << 2; + /// Controls whether execution code in the mapped frames are allowed. + const EXECUTABLE = 1 << 3; + /// Controls whether accesses from userspace (i.e. U-mode) are permitted. + const USER = 1 << 4; + /// Indicates that the mapping is present in all address spaces, so it isn't flushed from + /// the TLB on an address space switch. + const GLOBAL = 1 << 5; + /// Whether the memory area represented by this entry is accessed. + const ACCESSED = 1 << 6; + /// Whether the memory area represented by this entry is modified. + const DIRTY = 1 << 7; + // PBMT: Non-cacheable, idempotent, weakly-ordered (RVWMO), main memory + const PBMT_NC = 1 << 61; + // PBMT: Non-cacheable, non-idempotent, strongly-ordered (I/O ordering), I/O + const PBMT_IO = 1 << 62; + /// Naturally aligned power-of-2 + const NAPOT = 1 << 63; + } +} + +pub(crate) fn tlb_flush_addr(vaddr: Vaddr) { + unsafe { + riscv::asm::sfence_vma(0, vaddr); + } +} + +pub(crate) fn tlb_flush_addr_range(range: &Range) { + for vaddr in range.clone().step_by(PAGE_SIZE) { + tlb_flush_addr(vaddr); + } +} + +pub(crate) fn tlb_flush_all_excluding_global() { + // TODO: excluding global? + riscv::asm::sfence_vma_all() +} + +pub(crate) fn tlb_flush_all_including_global() { + riscv::asm::sfence_vma_all() +} + +#[derive(Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct PageTableEntry(usize); + +/// Activate the given level 4 page table. +/// +/// "satp" register doesn't have a field that encodes the cache policy, +/// so `_root_pt_cache` is ignored. +/// +/// # Safety +/// +/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by +/// changing the page mapping. +pub unsafe fn activate_page_table(root_paddr: Paddr, _root_pt_cache: CachePolicy) { + assert!(root_paddr % PagingConsts::BASE_PAGE_SIZE == 0); + let ppn = root_paddr >> 12; + riscv::register::satp::set(riscv::register::satp::Mode::Sv48, 0, ppn); +} + +pub fn current_page_table_paddr() -> Paddr { + riscv::register::satp::read().ppn() << 12 +} + +impl PageTableEntry { + const PHYS_ADDR_MASK: usize = 0x003F_FFFF_FFFF_FC00; + + fn new_paddr(paddr: Paddr) -> Self { + let ppn = paddr >> 12; + Self(ppn << 10) + } +} + +/// Parse a bit-flag bits `val` in the representation of `from` to `to` in bits. +macro_rules! parse_flags { + ($val:expr, $from:expr, $to:expr) => { + ($val as usize & $from.bits() as usize) >> $from.bits().ilog2() << $to.bits().ilog2() + }; +} + +impl PageTableEntryTrait for PageTableEntry { + fn is_present(&self) -> bool { + self.0 & PageTableFlags::VALID.bits() != 0 + } + + fn new_page(paddr: Paddr, _level: PagingLevel, prop: PageProperty) -> Self { + let mut pte = Self::new_paddr(paddr); + pte.set_prop(prop); + pte + } + + fn new_pt(paddr: Paddr) -> Self { + // In RISC-V, non-leaf PTE should have RWX = 000, + // and D, A, and U are reserved for future standard use. + let pte = Self::new_paddr(paddr); + PageTableEntry(pte.0 | PageTableFlags::VALID.bits()) + } + + fn paddr(&self) -> Paddr { + let ppn = (self.0 & Self::PHYS_ADDR_MASK) >> 10; + ppn << 12 + } + + fn prop(&self) -> PageProperty { + let flags = parse_flags!(self.0, PageTableFlags::READABLE, PageFlags::R) + | parse_flags!(self.0, PageTableFlags::WRITABLE, PageFlags::W) + | parse_flags!(self.0, PageTableFlags::EXECUTABLE, PageFlags::X) + | parse_flags!(self.0, PageTableFlags::ACCESSED, PageFlags::ACCESSED) + | parse_flags!(self.0, PageTableFlags::DIRTY, PageFlags::DIRTY); + let priv_flags = parse_flags!(self.0, PageTableFlags::USER, PrivFlags::USER) + | parse_flags!(self.0, PageTableFlags::GLOBAL, PrivFlags::GLOBAL); + + let cache = if self.0 & PageTableFlags::PBMT_IO.bits() != 0 { + CachePolicy::Uncacheable + } else { + CachePolicy::Writeback + }; + + PageProperty { + flags: PageFlags::from_bits(flags as u8).unwrap(), + cache, + priv_flags: PrivFlags::from_bits(priv_flags as u8).unwrap(), + } + } + + fn set_prop(&mut self, prop: PageProperty) { + let mut flags = PageTableFlags::VALID.bits() + | parse_flags!(prop.flags.bits(), PageFlags::R, PageTableFlags::READABLE) + | parse_flags!(prop.flags.bits(), PageFlags::W, PageTableFlags::WRITABLE) + | parse_flags!(prop.flags.bits(), PageFlags::X, PageTableFlags::EXECUTABLE) + | parse_flags!( + prop.priv_flags.bits(), + PrivFlags::USER, + PageTableFlags::USER + ) + | parse_flags!( + prop.priv_flags.bits(), + PrivFlags::GLOBAL, + PageTableFlags::GLOBAL + ); + + match prop.cache { + CachePolicy::Writeback => (), + CachePolicy::Uncacheable => { + // Currently, Asterinas uses `Uncacheable` for I/O memory. + flags |= PageTableFlags::PBMT_IO.bits() + } + _ => panic!("unsupported cache policy"), + } + + self.0 = (self.0 & Self::PHYS_ADDR_MASK) | flags; + } + + fn is_last(&self, level: PagingLevel) -> bool { + let rwx = PageTableFlags::READABLE | PageTableFlags::WRITABLE | PageTableFlags::EXECUTABLE; + level == 1 || (self.0 & rwx.bits()) != 0 + } +} + +impl fmt::Debug for PageTableEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_struct("PageTableEntry"); + f.field("raw", &format_args!("{:#x}", self.0)) + .field("paddr", &format_args!("{:#x}", self.paddr())) + .field("present", &self.is_present()) + .field( + "flags", + &PageTableFlags::from_bits_truncate(self.0 & !Self::PHYS_ADDR_MASK), + ) + .field("prop", &self.prop()) + .finish() + } +} + +pub(crate) fn __memcpy_fallible(dst: *mut u8, src: *const u8, size: usize) -> usize { + // TODO: implement fallible + unsafe { + riscv::register::sstatus::set_sum(); + } + unsafe { core::ptr::copy(src, dst, size) }; + 0 +} + +pub(crate) fn __memset_fallible(dst: *mut u8, value: u8, size: usize) -> usize { + // TODO: implement fallible + unsafe { + riscv::register::sstatus::set_sum(); + } + unsafe { core::ptr::write_bytes(dst, value, size) }; + 0 +} diff --git a/ostd/src/arch/riscv/mod.rs b/ostd/src/arch/riscv/mod.rs new file mode 100644 index 00000000..89030a1a --- /dev/null +++ b/ostd/src/arch/riscv/mod.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Platform-specific code for the RISC-V platform. + +pub mod boot; +pub(crate) mod cpu; +pub mod device; +pub mod iommu; +pub(crate) mod irq; +pub(crate) mod mm; +pub(crate) mod pci; +pub mod qemu; +pub mod serial; +pub mod task; +pub mod timer; +pub mod trap; + +use core::sync::atomic::Ordering; + +#[cfg(feature = "cvm_guest")] +pub(crate) fn init_cvm_guest() { + // Unimplemented, no-op +} + +pub(crate) fn init_on_bsp() { + // SAFETY: this function is only called once on BSP. + unsafe { + trap::init(true); + } + irq::init(); + + // SAFETY: they are only called once on BSP and ACPI has been initialized. + unsafe { + crate::cpu::init_num_cpus(); + crate::cpu::set_this_cpu_id(0); + } + + // SAFETY: no CPU local objects have been accessed by this far. And + // we are on the BSP. + unsafe { crate::cpu::local::init_on_bsp() }; + + crate::boot::smp::boot_all_aps(); + + timer::init(); +} + +pub(crate) unsafe fn init_on_ap() { + unimplemented!() +} + +pub(crate) fn interrupts_ack(irq_number: usize) { + unimplemented!() +} + +/// Return the frequency of TSC. The unit is Hz. +pub fn tsc_freq() -> u64 { + timer::TIMEBASE_FREQ.load(Ordering::Relaxed) +} + +/// Reads the current value of the processor’s time-stamp counter (TSC). +pub fn read_tsc() -> u64 { + riscv::register::time::read64() +} + +pub(crate) fn enable_cpu_features() { + unsafe { + riscv::register::sstatus::set_fs(riscv::register::sstatus::FS::Clean); + } +} diff --git a/ostd/src/arch/riscv/pci.rs b/ostd/src/arch/riscv/pci.rs new file mode 100644 index 00000000..025e32ae --- /dev/null +++ b/ostd/src/arch/riscv/pci.rs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! PCI bus io port + +use super::device::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess}; + +pub static PCI_ADDRESS_PORT: IoPort = unsafe { IoPort::new(0x0) }; +pub static PCI_DATA_PORT: IoPort = unsafe { IoPort::new(0x0) }; diff --git a/ostd/src/arch/riscv/qemu.rs b/ostd/src/arch/riscv/qemu.rs new file mode 100644 index 00000000..8071777d --- /dev/null +++ b/ostd/src/arch/riscv/qemu.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Providing the ability to exit QEMU and return a value as debug result. + +/// The exit code of QEMU. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum QemuExitCode { + /// The code that indicates a successful exit. + Success, + /// The code that indicates a failed exit. + Failed, +} + +/// Exit QEMU with the given exit code. +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + log::debug!("exit qemu with exit code {exit_code:?}"); + match exit_code { + QemuExitCode::Success => sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::NoReason), + QemuExitCode::Failed => sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure), + }; + unreachable!("qemu does not exit"); +} diff --git a/ostd/src/arch/riscv/serial.rs b/ostd/src/arch/riscv/serial.rs new file mode 100644 index 00000000..6448dc0a --- /dev/null +++ b/ostd/src/arch/riscv/serial.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The console I/O. + +use alloc::fmt; +use core::fmt::Write; + +/// Prints the formatted arguments to the standard output using the serial port. +#[inline] +pub fn print(args: fmt::Arguments) { + Stdout.write_fmt(args).unwrap(); +} + +/// The callback function for console input. +pub type InputCallback = dyn Fn(u8) + Send + Sync + 'static; + +/// Registers a callback function to be called when there is console input. +pub fn register_console_input_callback(_f: &'static InputCallback) { + todo!() +} + +struct Stdout; + +impl Write for Stdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + for &c in s.as_bytes() { + send(c); + } + Ok(()) + } +} + +/// Initializes the serial port. +pub(crate) fn init() {} + +/// Sends a byte on the serial port. +pub fn send(data: u8) { + sbi_rt::console_write_byte(data); +} diff --git a/ostd/src/arch/riscv/task/mod.rs b/ostd/src/arch/riscv/task/mod.rs new file mode 100644 index 00000000..ae741d28 --- /dev/null +++ b/ostd/src/arch/riscv/task/mod.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The architecture support of context switch. + +use crate::task::TaskContextApi; + +core::arch::global_asm!(include_str!("switch.S")); + +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub(crate) struct TaskContext { + pub regs: CalleeRegs, + pub pc: usize, + pub fsbase: usize, +} + +/// Callee-saved registers. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct CalleeRegs { + /// sp + pub sp: u64, + /// s0 + pub s0: u64, + /// s1 + pub s1: u64, + /// s2 + pub s2: u64, + /// s3 + pub s3: u64, + /// s4 + pub s4: u64, + /// s5 + pub s5: u64, + /// s6 + pub s6: u64, + /// s7 + pub s7: u64, + /// s8 + pub s8: u64, + /// s9 + pub s9: u64, + /// s10 + pub s10: u64, + /// s11 + pub s11: u64, +} + +impl CalleeRegs { + /// Creates new `CalleeRegs` + pub const fn new() -> Self { + CalleeRegs { + sp: 0, + s0: 0, + s1: 0, + s2: 0, + s3: 0, + s4: 0, + s5: 0, + s6: 0, + s7: 0, + s8: 0, + s9: 0, + s10: 0, + s11: 0, + } + } +} + +impl TaskContext { + pub const fn new() -> Self { + TaskContext { + regs: CalleeRegs::new(), + pc: 0, + fsbase: 0, + } + } + + /// Sets thread-local storage pointer. + pub fn set_tls_pointer(&mut self, tls: usize) { + self.fsbase = tls; + } + + /// Gets thread-local storage pointer. + pub fn tls_pointer(&self) -> usize { + self.fsbase + } +} + +impl TaskContextApi for TaskContext { + fn set_instruction_pointer(&mut self, ip: usize) { + self.pc = ip; + } + + fn instruction_pointer(&self) -> usize { + self.pc + } + + fn set_stack_pointer(&mut self, sp: usize) { + self.regs.sp = sp as u64; + } + + fn stack_pointer(&self) -> usize { + self.regs.sp as usize + } +} + +extern "C" { + pub(crate) fn context_switch(cur: *mut TaskContext, nxt: *const TaskContext); +} diff --git a/ostd/src/arch/riscv/task/switch.S b/ostd/src/arch/riscv/task/switch.S new file mode 100644 index 00000000..69adab78 --- /dev/null +++ b/ostd/src/arch/riscv/task/switch.S @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: MPL-2.0 */ + +.text +.global context_switch +context_switch: # (cur: *mut TaskContext, nxt: *TaskContext) + # Save cur's register + sd ra, 0x68(a0) # return address + sd sp, 0x0(a0) + sd s0, 0x8(a0) + sd s1, 0x10(a0) + sd s2, 0x18(a0) + sd s3, 0x20(a0) + sd s4, 0x28(a0) + sd s5, 0x30(a0) + sd s6, 0x38(a0) + sd s7, 0x40(a0) + sd s8, 0x48(a0) + sd s9, 0x50(a0) + sd s10, 0x58(a0) + sd s11, 0x60(a0) + + # Restore nxt's registers + ld ra, 0x68(a1) # return address + ld sp, 0x0(a1) + ld s0, 0x8(a1) + ld s1, 0x10(a1) + ld s2, 0x18(a1) + ld s3, 0x20(a1) + ld s4, 0x28(a1) + ld s5, 0x30(a1) + ld s6, 0x38(a1) + ld s7, 0x40(a1) + ld s8, 0x48(a1) + ld s9, 0x50(a1) + ld s10, 0x58(a1) + ld s11, 0x60(a1) + ret diff --git a/ostd/src/arch/riscv/timer/mod.rs b/ostd/src/arch/riscv/timer/mod.rs new file mode 100644 index 00000000..d6c4c75e --- /dev/null +++ b/ostd/src/arch/riscv/timer/mod.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The timer support. + +use core::sync::atomic::{AtomicU64, Ordering}; + +use spin::Once; + +use crate::{arch::boot::DEVICE_TREE, io_mem::IoMem}; + +/// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for unit conversion and +/// convenient for timer. What's more, the frequency cannot be set too high or too low, 1000Hz is +/// a modest choice. +/// +/// For system performance reasons, this rate cannot be set too high, otherwise most of the time +/// is spent executing timer code. +pub const TIMER_FREQ: u64 = 1000; + +pub(crate) static TIMEBASE_FREQ: AtomicU64 = AtomicU64::new(1); + +/// [`IoMem`] of goldfish RTC, which will be used by `aster-time`. +pub static GOLDFISH_IO_MEM: Once = Once::new(); + +pub(super) fn init() { + let timer_freq = DEVICE_TREE + .get() + .unwrap() + .cpus() + .next() + .unwrap() + .timebase_frequency() as u64; + TIMEBASE_FREQ.store(timer_freq, Ordering::Relaxed); + + let chosen = DEVICE_TREE.get().unwrap().find_node("/soc/rtc").unwrap(); + if let Some(compatible) = chosen.compatible() + && compatible.all().any(|c| c == "google,goldfish-rtc") + { + let region = chosen.reg().unwrap().next().unwrap(); + let io_mem = unsafe { + IoMem::new( + (region.starting_address as usize) + ..(region.starting_address as usize) + region.size.unwrap(), + ) + }; + GOLDFISH_IO_MEM.call_once(|| io_mem); + } +} diff --git a/ostd/src/arch/riscv/trap/mod.rs b/ostd/src/arch/riscv/trap/mod.rs new file mode 100644 index 00000000..7cdb9478 --- /dev/null +++ b/ostd/src/arch/riscv/trap/mod.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Handles trap. + +mod trap; + +pub use trap::{GeneralRegs, TrapFrame, UserContext}; + +use crate::cpu_local_cell; + +cpu_local_cell! { + static IS_KERNEL_INTERRUPTED: bool = false; +} + +/// Initialize interrupt handling on RISC-V. +pub unsafe fn init(on_bsp: bool) { + self::trap::init(); +} + +/// Returns true if this function is called within the context of an IRQ handler +/// and the IRQ occurs while the CPU is executing in the kernel mode. +/// Otherwise, it returns false. +pub fn is_kernel_interrupted() -> bool { + IS_KERNEL_INTERRUPTED.load() +} + +/// Handle traps (only from kernel). +#[no_mangle] +extern "C" fn trap_handler(f: &mut TrapFrame) { + use riscv::register::scause::Trap; + + match riscv::register::scause::read().cause() { + Trap::Interrupt(_) => { + IS_KERNEL_INTERRUPTED.store(true); + todo!(); + IS_KERNEL_INTERRUPTED.store(false); + } + Trap::Exception(e) => { + let stval = riscv::register::stval::read(); + panic!( + "Cannot handle kernel cpu exception: {e:?}. stval: {stval:#x}, trapframe: {f:#x?}.", + ); + } + } +} diff --git a/ostd/src/arch/riscv/trap/trap.S b/ostd/src/arch/riscv/trap/trap.S new file mode 100644 index 00000000..575bad3c --- /dev/null +++ b/ostd/src/arch/riscv/trap/trap.S @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: MPL-2.0 OR MIT + * + * The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs), + * which is released under the following license: + * + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2020 - 2024 Runji Wang + * + * We make the following new changes: + * * Add the `trap_handler_table`. + * + * These changes are released under the following license: + * + * SPDX-License-Identifier: MPL-2.0 + */ + +# Constants / Macros defined in Rust code: +# XLENB +# LOAD +# STORE + + .section .text + .global trap_entry + .balign 4 +trap_entry: + # If coming from userspace, preserve the user stack pointer and load + # the kernel stack pointer. If we came from the kernel, sscratch + # will contain 0, and we should continue on the current stack. + csrrw sp, sscratch, sp + bnez sp, trap_from_user +trap_from_kernel: + csrr sp, sscratch + addi sp, sp, -34 * XLENB + # sscratch = previous-sp, sp = kernel-sp +trap_from_user: + # save general registers except sp(x2) + STORE_SP x1, 1 + STORE_SP x3, 3 + STORE_SP x4, 4 + STORE_SP x5, 5 + STORE_SP x6, 6 + STORE_SP x7, 7 + STORE_SP x8, 8 + STORE_SP x9, 9 + STORE_SP x10, 10 + STORE_SP x11, 11 + STORE_SP x12, 12 + STORE_SP x13, 13 + STORE_SP x14, 14 + STORE_SP x15, 15 + STORE_SP x16, 16 + STORE_SP x17, 17 + STORE_SP x18, 18 + STORE_SP x19, 19 + STORE_SP x20, 20 + STORE_SP x21, 21 + STORE_SP x22, 22 + STORE_SP x23, 23 + STORE_SP x24, 24 + STORE_SP x25, 25 + STORE_SP x26, 26 + STORE_SP x27, 27 + STORE_SP x28, 28 + STORE_SP x29, 29 + STORE_SP x30, 30 + STORE_SP x31, 31 + + # save sp, sstatus, sepc + csrrw t0, sscratch, x0 # sscratch = 0 (kernel) + csrr t1, sstatus + csrr t2, sepc + STORE_SP t0, 2 # save sp + STORE_SP t1, 32 # save sstatus + STORE_SP t2, 33 # save sepc + + li t0, 3 << 13 + or t1, t1, t0 # sstatus.FS = Dirty (3) + csrw sstatus, t1 + + andi t1, t1, 1 << 8 # sstatus.SPP == 1 + beqz t1, end_trap_from_user +end_trap_from_kernel: + mv a0, sp # first arg is TrapFrame + la ra, trap_return # set return address + j trap_handler + +end_trap_from_user: + # load callee-saved registers + LOAD_SP sp, 0 + LOAD_SP s0, 0 + LOAD_SP s1, 1 + LOAD_SP s2, 2 + LOAD_SP s3, 3 + LOAD_SP s4, 4 + LOAD_SP s5, 5 + LOAD_SP s6, 6 + LOAD_SP s7, 7 + LOAD_SP s8, 8 + LOAD_SP s9, 9 + LOAD_SP s10, 10 + LOAD_SP s11, 11 + LOAD_SP ra, 12 + # not callee-saved, but is used to store mhartid + LOAD_SP gp, 13 + addi sp, sp, 14 * XLENB + + ret + +.global run_user +run_user: + # save callee-saved registers + addi sp, sp, -14 * XLENB + STORE_SP s0, 0 + STORE_SP s1, 1 + STORE_SP s2, 2 + STORE_SP s3, 3 + STORE_SP s4, 4 + STORE_SP s5, 5 + STORE_SP s6, 6 + STORE_SP s7, 7 + STORE_SP s8, 8 + STORE_SP s9, 9 + STORE_SP s10, 10 + STORE_SP s11, 11 + STORE_SP ra, 12 + # not callee-saved, but is used to store mhartid + STORE_SP gp, 13 + + mv t0, sp + mv sp, a0 + STORE_SP t0, 0 # save kernel-sp + csrw sscratch, sp # sscratch = bottom of trap frame + +trap_return: + LOAD_SP t0, 32 # t0 = sstatus + LOAD_SP t1, 33 # t1 = sepc + csrw sstatus, t0 # load sstatus + csrw sepc, t1 # load sepc + + # restore general registers except sp(x2) + LOAD_SP x1, 1 + LOAD_SP x3, 3 + LOAD_SP x4, 4 + LOAD_SP x5, 5 + LOAD_SP x6, 6 + LOAD_SP x7, 7 + LOAD_SP x8, 8 + LOAD_SP x9, 9 + LOAD_SP x10, 10 + LOAD_SP x11, 11 + LOAD_SP x12, 12 + LOAD_SP x13, 13 + LOAD_SP x14, 14 + LOAD_SP x15, 15 + LOAD_SP x16, 16 + LOAD_SP x17, 17 + LOAD_SP x18, 18 + LOAD_SP x19, 19 + LOAD_SP x20, 20 + LOAD_SP x21, 21 + LOAD_SP x22, 22 + LOAD_SP x23, 23 + LOAD_SP x24, 24 + LOAD_SP x25, 25 + LOAD_SP x26, 26 + LOAD_SP x27, 27 + LOAD_SP x28, 28 + LOAD_SP x29, 29 + LOAD_SP x30, 30 + LOAD_SP x31, 31 + # restore sp last + LOAD_SP x2, 2 + + # return from supervisor call + sret diff --git a/ostd/src/arch/riscv/trap/trap.rs b/ostd/src/arch/riscv/trap/trap.rs new file mode 100644 index 00000000..6520997c --- /dev/null +++ b/ostd/src/arch/riscv/trap/trap.rs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MPL-2.0 OR MIT +// +// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs), +// which is released under the following license: +// +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2020 - 2024 Runji Wang +// +// We make the following new changes: +// * Implement the `trap_handler` of Asterinas. +// +// These changes are released under the following license: +// +// SPDX-License-Identifier: MPL-2.0 + +use core::arch::{asm, global_asm}; + +use crate::Pod; + +#[cfg(target_arch = "riscv32")] +global_asm!( + r" + .equ XLENB, 4 + .macro LOAD_SP a1, a2 + lw \a1, \a2*XLENB(sp) + .endm + .macro STORE_SP a1, a2 + sw \a1, \a2*XLENB(sp) + .endm +" +); +#[cfg(target_arch = "riscv64")] +global_asm!( + r" + .equ XLENB, 8 + .macro LOAD_SP a1, a2 + ld \a1, \a2*XLENB(sp) + .endm + .macro STORE_SP a1, a2 + sd \a1, \a2*XLENB(sp) + .endm +" +); + +global_asm!(include_str!("trap.S")); + +/// Initialize interrupt handling for the current HART. +/// +/// # Safety +/// +/// This function will: +/// - Set `sscratch` to 0. +/// - Set `stvec` to internal exception vector. +/// +/// You **MUST NOT** modify these registers later. +pub unsafe fn init() { + // Set sscratch register to 0, indicating to exception vector that we are + // presently executing in the kernel + asm!("csrw sscratch, zero"); + // Set the exception vector address + asm!("csrw stvec, {}", in(reg) trap_entry as usize); +} + +/// Trap frame of kernel interrupt +/// +/// # Trap handler +/// +/// You need to define a handler function like this: +/// +/// ```no_run +/// #[no_mangle] +/// pub extern "C" fn trap_handler(tf: &mut TrapFrame) { +/// println!("TRAP! tf: {:#x?}", tf); +/// } +/// ``` +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct TrapFrame { + /// General registers + pub general: GeneralRegs, + /// Supervisor Status + pub sstatus: usize, + /// Supervisor Exception Program Counter + pub sepc: usize, +} + +/// Saved registers on a trap. +#[derive(Debug, Default, Clone, Copy, Pod)] +#[repr(C)] +pub struct UserContext { + /// General registers + pub general: GeneralRegs, + /// Supervisor Status + pub sstatus: usize, + /// Supervisor Exception Program Counter + pub sepc: usize, +} + +impl UserContext { + /// Go to user space with the context, and come back when a trap occurs. + /// + /// On return, the context will be reset to the status before the trap. + /// Trap reason and error code will be returned. + /// + /// # Example + /// ```no_run + /// use trapframe::{UserContext, GeneralRegs}; + /// + /// // init user space context + /// let mut context = UserContext { + /// general: GeneralRegs { + /// sp: 0x10000, + /// ..Default::default() + /// }, + /// sepc: 0x1000, + /// ..Default::default() + /// }; + /// // go to user + /// context.run(); + /// // back from user + /// println!("back from user: {:#x?}", context); + /// ``` + pub fn run(&mut self) { + unsafe { run_user(self) } + } +} + +/// General registers +#[derive(Debug, Default, Clone, Copy, Pod)] +#[repr(C)] +#[allow(missing_docs)] +pub struct GeneralRegs { + pub zero: usize, + pub ra: usize, + pub sp: usize, + pub gp: usize, + pub tp: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub s0: usize, + pub s1: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, + pub s9: usize, + pub s10: usize, + pub s11: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, +} + +impl UserContext { + /// Get number of syscall + pub fn get_syscall_num(&self) -> usize { + self.general.a7 + } + + /// Get return value of syscall + pub fn get_syscall_ret(&self) -> usize { + self.general.a0 + } + + /// Set return value of syscall + pub fn set_syscall_ret(&mut self, ret: usize) { + self.general.a0 = ret; + } + + /// Get syscall args + pub fn get_syscall_args(&self) -> [usize; 6] { + [ + self.general.a0, + self.general.a1, + self.general.a2, + self.general.a3, + self.general.a4, + self.general.a5, + ] + } + + /// Set instruction pointer + pub fn set_ip(&mut self, ip: usize) { + self.sepc = ip; + } + + /// Set stack pointer + pub fn set_sp(&mut self, sp: usize) { + self.general.sp = sp; + } + + /// Get stack pointer + pub fn get_sp(&self) -> usize { + self.general.sp + } + + /// Set tls pointer + pub fn set_tls(&mut self, tls: usize) { + self.general.gp = tls; + } +} + +#[allow(improper_ctypes)] +extern "C" { + fn trap_entry(); + fn run_user(regs: &mut UserContext); +} diff --git a/ostd/src/arch/x86/mod.rs b/ostd/src/arch/x86/mod.rs index a929bb90..336ac0e0 100644 --- a/ostd/src/arch/x86/mod.rs +++ b/ostd/src/arch/x86/mod.rs @@ -39,7 +39,7 @@ use kernel::apic::ioapic; use log::{info, warn}; #[cfg(feature = "cvm_guest")] -pub(crate) fn check_tdx_init() { +pub(crate) fn init_cvm_guest() { match init_tdx() { Ok(td_info) => { early_println!( diff --git a/ostd/src/bus/mmio/mod.rs b/ostd/src/bus/mmio/mod.rs index 80c4c57b..3aa385bc 100644 --- a/ostd/src/bus/mmio/mod.rs +++ b/ostd/src/bus/mmio/mod.rs @@ -15,8 +15,7 @@ use log::debug; use self::bus::MmioBus; use crate::{ - arch::kernel::IO_APIC, bus::mmio::common_device::MmioCommonDevice, mm::paddr_to_vaddr, - sync::SpinLock, trap::IrqLine, + bus::mmio::common_device::MmioCommonDevice, mm::paddr_to_vaddr, sync::SpinLock, trap::IrqLine, }; cfg_if! { @@ -45,14 +44,16 @@ pub(crate) fn init() { } } // FIXME: The address 0xFEB0_0000 is obtained from an instance of microvm, and it may not work in other architecture. + #[cfg(target_arch = "x86_64")] iter_range(0xFEB0_0000..0xFEB0_4000); } +#[cfg(target_arch = "x86_64")] fn iter_range(range: Range) { debug!("[Virtio]: Iter MMIO range:{:x?}", range); let mut current = range.end; let mut lock = MMIO_BUS.lock(); - let io_apics = IO_APIC.get().unwrap(); + let io_apics = crate::arch::kernel::IO_APIC.get().unwrap(); let is_ioapic2 = io_apics.len() == 2; let mut io_apic = if is_ioapic2 { io_apics.get(1).unwrap().lock() diff --git a/ostd/src/bus/mod.rs b/ostd/src/bus/mod.rs index f22a5836..58d237ee 100644 --- a/ostd/src/bus/mod.rs +++ b/ostd/src/bus/mod.rs @@ -16,6 +16,8 @@ pub enum BusProbeError { /// Initializes the bus pub(crate) fn init() { + #[cfg(target_arch = "x86_64")] pci::init(); + mmio::init(); } diff --git a/ostd/src/cpu/mod.rs b/ostd/src/cpu/mod.rs index 309ec68d..00da818a 100644 --- a/ostd/src/cpu/mod.rs +++ b/ostd/src/cpu/mod.rs @@ -5,8 +5,10 @@ pub mod local; cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")]{ + if #[cfg(target_arch = "x86_64")] { pub use crate::arch::x86::cpu::*; + } else if #[cfg(target_arch = "riscv64")] { + pub use crate::arch::riscv::cpu::*; } } diff --git a/ostd/src/lib.rs b/ostd/src/lib.rs index eef6f7b7..b9b73b1f 100644 --- a/ostd/src/lib.rs +++ b/ostd/src/lib.rs @@ -76,7 +76,7 @@ pub unsafe fn init() { arch::serial::init(); #[cfg(feature = "cvm_guest")] - arch::check_tdx_init(); + arch::init_cvm_guest(); // SAFETY: This function is called only once and only on the BSP. unsafe { cpu::local::early_init_bsp_local_base() }; diff --git a/ostd/src/mm/io.rs b/ostd/src/mm/io.rs index 67717db9..6a56971d 100644 --- a/ostd/src/mm/io.rs +++ b/ostd/src/mm/io.rs @@ -943,3 +943,10 @@ const fn is_pod_once() -> bool { size == 1 || size == 2 || size == 4 || size == 8 } + +#[cfg(target_arch = "riscv64")] +const fn is_pod_once() -> bool { + let size = size_of::(); + + size == 1 || size == 2 || size == 4 || size == 8 +} diff --git a/ostd/src/mm/kspace.rs b/ostd/src/mm/kspace.rs index a0e6104c..805dc253 100644 --- a/ostd/src/mm/kspace.rs +++ b/ostd/src/mm/kspace.rs @@ -80,7 +80,10 @@ pub fn kernel_loaded_offset() -> usize { KERNEL_CODE_BASE_VADDR } +#[cfg(target_arch = "x86_64")] const KERNEL_CODE_BASE_VADDR: usize = 0xffff_ffff_8000_0000 << ADDR_WIDTH_SHIFT; +#[cfg(target_arch = "riscv64")] +const KERNEL_CODE_BASE_VADDR: usize = 0xffff_ffff_0000_0000 << ADDR_WIDTH_SHIFT; const FRAME_METADATA_CAP_VADDR: Vaddr = 0xffff_ff00_0000_0000 << ADDR_WIDTH_SHIFT; const FRAME_METADATA_BASE_VADDR: Vaddr = 0xffff_fe00_0000_0000 << ADDR_WIDTH_SHIFT;