Add RISC-V base support

This commit is contained in:
YanWQ-monad 2024-09-24 22:14:30 +08:00 committed by Tate, Hongliang Tian
parent 839c2a6689
commit 4fa0e6334b
46 changed files with 2436 additions and 12 deletions

57
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

@ -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<RtcGoldfish> {
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,
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,4 @@
// SPDX-License-Identifier: MPL-2.0
pub mod cpu;
pub mod signal;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Fdt> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| "Unknown".into());
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
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<BootloaderAcpiArg>) {
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
}
fn init_framebuffer_info(_framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::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();
}

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
//! Multiprocessor Boot Support
pub(crate) fn get_num_processors() -> Option<u32> {
Some(1)
}
pub(crate) fn bringup_all_aps() {
// TODO
}

View File

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

View File

@ -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<F>(&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;

View File

@ -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<u32, ReadWriteAccess> = unsafe { IoPort::new(0x12) };
///
/// fn port_value_increase(){
/// PORT.write(PORT.read() + 1)
/// }
/// ```
///
pub struct IoPort<T, A> {
port: u16,
value_marker: PhantomData<T>,
access_marker: PhantomData<A>,
}
impl<T, A> IoPort<T, A> {
/// 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<T: PortRead, A: IoPortReadAccess> IoPort<T, A> {
/// Reads from the I/O port
#[inline]
pub fn read(&self) -> T {
unsafe { PortRead::read_from_port(self.port) }
}
}
impl<T: PortWrite, A: IoPortWriteAccess> IoPort<T, A> {
/// Writes to the I/O port
#[inline]
pub fn write(&self, value: T) {
unsafe { PortWrite::write_to_port(self.port, value) }
}
}

View File

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

View File

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

150
ostd/src/arch/riscv/irq.rs Normal file
View File

@ -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<SpinLock<IdAlloc>> = Once::new();
pub(crate) static IRQ_LIST: Once<Vec<IrqLine>> = Once::new();
pub(crate) fn init() {
let mut list: Vec<IrqLine> = 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<Mutex<IdAlloc>> = Once::new();
pub struct CallbackElement {
function: Box<dyn Fn(&TrapFrame) + Send + Sync + 'static>,
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<Vec<CallbackElement>>,
}
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<alloc::vec::Vec<CallbackElement>, 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<F>(&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!()
}

View File

@ -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::<PageTableEntry>();
}
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<Vaddr>) {
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
}

View File

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

View File

@ -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<u32, WriteOnlyAccess> = unsafe { IoPort::new(0x0) };
pub static PCI_DATA_PORT: IoPort<u32, ReadWriteAccess> = unsafe { IoPort::new(0x0) };

View File

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

View File

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

View File

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

View File

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

View File

@ -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<IoMem> = 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);
}
}

View File

@ -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?}.",
);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -16,6 +16,8 @@ pub enum BusProbeError {
/// Initializes the bus
pub(crate) fn init() {
#[cfg(target_arch = "x86_64")]
pci::init();
mmio::init();
}

View File

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

View File

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

View File

@ -943,3 +943,10 @@ const fn is_pod_once<T: Pod>() -> bool {
size == 1 || size == 2 || size == 4 || size == 8
}
#[cfg(target_arch = "riscv64")]
const fn is_pod_once<T: Pod>() -> bool {
let size = size_of::<T>();
size == 1 || size == 2 || size == 4 || size == 8
}

View File

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