diff --git a/arceos/Cargo.toml b/arceos/Cargo.toml index 55757f52c..144e5a8aa 100644 --- a/arceos/Cargo.toml +++ b/arceos/Cargo.toml @@ -55,6 +55,9 @@ members = [ "exercises/sys_map", "exercises/simple_hv", "exercises/ramfs_rename", + + "examples/helloworld", + "examples/httpserver", ] [workspace.package] diff --git a/arceos/Dockerfile b/arceos/Dockerfile new file mode 100644 index 000000000..9aec632e5 --- /dev/null +++ b/arceos/Dockerfile @@ -0,0 +1,39 @@ +FROM rust:slim + +RUN echo /etc/apt/sources.list << deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main +RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + +RUN apt-get update \ + && apt-get install -y --no-install-recommends wget libclang-16-dev make python3 \ + xz-utils python3-venv ninja-build bzip2 meson dosfstools xxd\ + pkg-config libglib2.0-dev git libslirp-dev vim-common sudo\ + && rm -rf /var/lib/apt/lists/* + +RUN cargo install cargo-binutils axconfig-gen + +COPY rust-toolchain.toml /rust-toolchain.toml + +RUN rustc --version + +RUN wget https://musl.cc/aarch64-linux-musl-cross.tgz \ + && wget https://musl.cc/riscv64-linux-musl-cross.tgz \ + && wget https://musl.cc/x86_64-linux-musl-cross.tgz \ + && wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-linux-musl-cross-gcc-13.2.0/loongarch64-linux-musl-cross.tgz \ + && tar zxf aarch64-linux-musl-cross.tgz \ + && tar zxf riscv64-linux-musl-cross.tgz \ + && tar zxf x86_64-linux-musl-cross.tgz \ + && tar zxf loongarch64-linux-musl-cross.tgz \ + && rm -f *.tgz + +RUN wget https://download.qemu.org/qemu-9.2.1.tar.xz \ + && tar xf qemu-9.2.1.tar.xz \ + && cd qemu-9.2.1 \ + && ./configure --prefix=/qemu-bin-9.2.1 \ + --target-list=loongarch64-softmmu,riscv64-softmmu,aarch64-softmmu,x86_64-softmmu \ + --enable-gcov --enable-debug --enable-slirp \ + && make -j$(nproc) \ + && make install +RUN rm -rf qemu-9.2.1 qemu-9.2.1.tar.xz + +ENV PATH="/x86_64-linux-musl-cross/bin:/aarch64-linux-musl-cross/bin:/riscv64-linux-musl-cross/bin:/loongarch64-linux-musl-cross/bin:$PATH" +ENV PATH="/qemu-bin-9.2.1/bin:$PATH" diff --git a/arceos/Makefile b/arceos/Makefile index ac86cb55d..7dad6ae78 100644 --- a/arceos/Makefile +++ b/arceos/Makefile @@ -181,7 +181,7 @@ debug: build sleep 1 $(GDB) $(OUT_ELF) \ -ex 'target remote localhost:1234' \ - -ex 'b rust_entry' \ + -ex 'b rust_main' \ -ex 'continue' \ -ex 'disp /16i $$pc' @@ -223,7 +223,7 @@ pflash_img: $(call mk_pflash,$(PFLASH_IMG)) payload: - @make -C ./payload + @make ARCH=$(ARCH) -C ./payload clean: clean_c rm -rf $(APP)/*.bin $(APP)/*.elf diff --git a/arceos/README.md b/arceos/README.md index b652b0c42..72dfc46dc 100644 --- a/arceos/README.md +++ b/arceos/README.md @@ -43,6 +43,8 @@ Install `libclang-dev`: sudo apt install libclang-dev ``` +Currently, the aarch64 architecture supports u_* and m_* under arceos/tour, but m_3_* requires libclang-*-dev version less than or equal to 16. You can use conda to generate a specified clangdev version for successful compilation. Or use dockerfile directly. + Download & install [musl](https://musl.cc) toolchains: ```bash @@ -72,6 +74,8 @@ brew install qemu Other systems and arch please refer to [Qemu Download](https://www.qemu.org/download/#linux) +For qemu version, it is recommended to use version 9.2.0 or later, otherwise unexpected problems may occur. + ### 2. Build & Run ```bash @@ -80,8 +84,8 @@ make A=path/to/app ARCH= LOG= # e.g. run app in arceos directory make run A=path/to/app ARCH= LOG= # e.g. build&run oscamp tour app tour/u_1_0 -make pflash_img -make disk_img +make pflash_img ARCH= +make disk_img ARCH= make run A=tour/u_1_0 # e.g. try to build&run oscamp tour apps ./test_tour.sh @@ -93,6 +97,8 @@ Where `path/to/app` is the relative path to the application. Examples applicatio `` should be one of `off`, `error`, `warn`, `info`, `debug`, `trace`. +If the above command does not specify ARCH, it defaults to risc-V64. + More arguments and targets can be found in [Makefile](Makefile). For example, to run the [httpserver](examples/httpserver/) on `qemu-system-aarch64` with 4 cores and log level `info`: @@ -103,6 +109,52 @@ make A=examples/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y Note that the `NET=y` argument is required to enable the network device in QEMU. These arguments (`BLK`, `GRAPHIC`, etc.) only take effect at runtime not build time. + +For example, the complete process for tour/m_1_0, aarch64 architecture: +``` bash +make +make ARCH=aarch64 +rm -f pflash.img +rm -f disk.img +make pflash_img ARCH=aarch64 +make disk_img ARCH=aarch64 +make payload ARCH=aarch64 +./update_disk.sh ./payload/origin/origin +make run A=tour/m_1_0 ARCH=aarch64 LOG=debug BLK=y +``` +For tour/m_1_0,risc-V64 architecture: +``` bash +make +rm -f pflash.img +rm -f disk.img +make pflash_img +make disk_img +make payload +./update_disk.sh ./payload/origin/origin +make run A=tour/m_1_0 LOG=debug BLK=y +``` + +## Build and Run through Docker +Install [Docker](https://www.docker.com/) in your system. + +Then build all dependencies through provided dockerfile: +```bash +docker build -t oscamp -f Dockerfile . +``` +Create a container and build/run app: +``` bash +docker run -it --privileged \ + -v ~/oscamp:/oscamp \ + -w /oscamp/arceos \ + oscamp bash +``` +"By default, Docker containers are isolated, and for security reasons, they run with a restricted set of Linux Capabilities. The `make disk_img` rule requires the `mount` command, and the `mount` command (as well as `umount`) needs the `CAP_SYS_ADMIN` Linux Capability. By default, Docker containers do not possess this capability, even if you are the `root` user inside the container. + +**Solution:** +Use the `--privileged` flag (not recommended for production environments, but convenient for development and debugging). +This flag grants the container almost all Linux Capabilities, including `CAP_SYS_ADMIN`, allowing it to perform operations typically reserved for the `root` user, such as mounting file systems." +# Now build/run app in the container +make A=examples/helloworld ARCH=aarch64 run ## How to write ArceOS apps You can write and build your custom applications outside the ArceOS source tree. diff --git a/arceos/modules/axhal/src/arch/aarch64/context.rs b/arceos/modules/axhal/src/arch/aarch64/context.rs index 67b923364..a4881c2b6 100644 --- a/arceos/modules/axhal/src/arch/aarch64/context.rs +++ b/arceos/modules/axhal/src/arch/aarch64/context.rs @@ -15,6 +15,149 @@ pub struct TrapFrame { pub spsr: u64, } +impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.r[0] as _ + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.r[1] as _ + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.r[2] as _ + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.r[3] as _ + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.r[4] as _ + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.r[5] as _ + } +} + +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub const fn empty() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// Creates a new context with the given entry point, user stack pointer, + /// and the argument. + pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self { + use aarch64_cpu::registers::SPSR_EL1; + let mut regs = [0; 31]; + regs[0] = arg0 as _; + Self(TrapFrame { + r: regs, + usp: ustack_top.as_usize() as _, + elr: entry as _, + spsr: (SPSR_EL1::M::EL0t + + SPSR_EL1::D::Masked + + SPSR_EL1::A::Masked + + SPSR_EL1::I::Unmasked + + SPSR_EL1::F::Masked) + .value, + }) + } + + /// Creates a new context from the given [`TrapFrame`]. + pub const fn from(trap_frame: &TrapFrame) -> Self { + Self(*trap_frame) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.elr as _ + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.usp as _ + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, pc: usize) { + self.0.elr = pc as _; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, sp: usize) { + self.0.usp = sp as _; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, r0: usize) { + self.0.r[0] = r0 as _; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `elr`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + #[inline(never)] + #[unsafe(no_mangle)] + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + super::disable_irqs(); + // We do not handle traps that occur at the current exception level, + // so the kstack ptr(`sp_el1`) will not change during running in user space. + // Then we don't need to save the `sp_el1` to the taskctx. + unsafe { + core::arch::asm!( + " + mov sp, x1 + ldp x30, x9, [x0, 30 * 8] + ldp x10, x11, [x0, 32 * 8] + msr sp_el0, x9 + msr elr_el1, x10 + msr spsr_el1, x11 + + ldp x28, x29, [x0, 28 * 8] + ldp x26, x27, [x0, 26 * 8] + ldp x24, x25, [x0, 24 * 8] + ldp x22, x23, [x0, 22 * 8] + ldp x20, x21, [x0, 20 * 8] + ldp x18, x19, [x0, 18 * 8] + ldp x16, x17, [x0, 16 * 8] + ldp x14, x15, [x0, 14 * 8] + ldp x12, x13, [x0, 12 * 8] + ldp x10, x11, [x0, 10 * 8] + ldp x8, x9, [x0, 8 * 8] + ldp x6, x7, [x0, 6 * 8] + ldp x4, x5, [x0, 4 * 8] + ldp x2, x3, [x0, 2 * 8] + ldp x0, x1, [x0] + eret", + in("x0") &self.0, + in("x1") kstack_top.as_usize() , + options(noreturn), + ) + } + } +} + /// FP & SIMD registers. #[repr(C, align(16))] #[derive(Debug, Default)] @@ -47,7 +190,7 @@ impl FpState { /// and the next task restores its context from memory to CPU. #[allow(missing_docs)] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct TaskContext { pub sp: u64, pub tpidr_el0: u64, @@ -63,14 +206,23 @@ pub struct TaskContext { pub r28: u64, pub r29: u64, pub lr: u64, // r30 + /// The `ttbr0_el1` register value, i.e., the page table root. + #[cfg(feature = "uspace")] + pub ttbr0_el1: memory_addr::PhysAddr, #[cfg(feature = "fp_simd")] pub fp_state: FpState, } impl TaskContext { - /// Creates a new default context for a new task. - pub const fn new() -> Self { - unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + /// Creates a dummy context for a new task. + /// + /// Note the context is not initialized, it will be filled by [`switch_to`] + /// (for initial tasks) and [`init`] (for regular tasks) methods. + /// + /// [`init`]: TaskContext::init + /// [`switch_to`]: TaskContext::switch_to + pub fn new() -> Self { + Self::default() } /// Initializes the context for a new task, with the given entry point and @@ -78,9 +230,18 @@ impl TaskContext { pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { self.sp = kstack_top.as_usize() as u64; self.lr = entry as u64; + // When under `uspace` feature, kernel will not use this register. self.tpidr_el0 = tls_area.as_usize() as u64; } + /// Changes the page table root for user space (`ttbr0_el1` register for aarch64 in el1 level). + /// + /// If not set, it means that this task is a kernel task and only `ttbr1_el1` register will be used. + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, ttbr0_el1: memory_addr::PhysAddr) { + self.ttbr0_el1 = ttbr0_el1; + } + /// Switches to another task. /// /// It first saves the current task's context from CPU to this place, and then @@ -88,6 +249,12 @@ impl TaskContext { pub fn switch_to(&mut self, next_ctx: &Self) { #[cfg(feature = "fp_simd")] self.fp_state.switch_to(&next_ctx.fp_state); + #[cfg(feature = "uspace")] + { + if self.ttbr0_el1 != next_ctx.ttbr0_el1 { + unsafe { super::write_page_table_root0(next_ctx.ttbr0_el1) }; + } + } unsafe { context_switch(self, next_ctx) } } } diff --git a/arceos/modules/axhal/src/arch/aarch64/mod.rs b/arceos/modules/axhal/src/arch/aarch64/mod.rs index 160dabdd1..2d99acad8 100644 --- a/arceos/modules/axhal/src/arch/aarch64/mod.rs +++ b/arceos/modules/axhal/src/arch/aarch64/mod.rs @@ -1,5 +1,7 @@ mod context; -pub(crate) mod trap; + +#[cfg(target_os = "none")] +mod trap; use core::arch::asm; @@ -7,6 +9,8 @@ use aarch64_cpu::registers::{DAIF, TPIDR_EL0, TTBR0_EL1, TTBR1_EL1, VBAR_EL1}; use memory_addr::{PhysAddr, VirtAddr}; use tock_registers::interfaces::{Readable, Writeable}; +#[cfg(feature = "uspace")] +pub use self::context::UspaceContext; pub use self::context::{FpState, TaskContext, TrapFrame}; /// Allows the current CPU to respond to interrupts. @@ -135,3 +139,14 @@ pub fn read_thread_pointer() -> usize { pub unsafe fn write_thread_pointer(tpidr_el0: usize) { TPIDR_EL0.set(tpidr_el0 as _) } + +/// Initializes CPU states on the current CPU. +/// +/// On AArch64, it sets the exception vector base address (`VBAR_EL1`) and `TTBR0_EL1`. +pub fn cpu_init() { + unsafe extern "C" { + fn exception_vector_base(); + } + set_exception_vector_base(exception_vector_base as usize); + unsafe { write_page_table_root0(0.into()) }; // disable low address access in EL1 +} diff --git a/arceos/modules/axhal/src/arch/aarch64/trap.S b/arceos/modules/axhal/src/arch/aarch64/trap.S index 7167610ac..662c0888d 100644 --- a/arceos/modules/axhal/src/arch/aarch64/trap.S +++ b/arceos/modules/axhal/src/arch/aarch64/trap.S @@ -21,6 +21,11 @@ mrs x11, spsr_el1 stp x30, x9, [sp, 30 * 8] stp x10, x11, [sp, 32 * 8] + + # We may have interrupted userspace, or a guest, or exit-from or + # return-to either of those. So we can't trust sp_el0, and need to + # restore it. + bl {cache_current_task_ptr} .endm .macro RESTORE_REGS diff --git a/arceos/modules/axhal/src/arch/aarch64/trap.rs b/arceos/modules/axhal/src/arch/aarch64/trap.rs index 9bee97474..de1725033 100644 --- a/arceos/modules/axhal/src/arch/aarch64/trap.rs +++ b/arceos/modules/axhal/src/arch/aarch64/trap.rs @@ -6,7 +6,7 @@ use tock_registers::interfaces::Readable; use super::TrapFrame; -global_asm!(include_str!("trap.S")); +global_asm!(include_str!("trap.S"), cache_current_task_ptr = sym crate::cpu::cache_current_task_ptr); #[repr(u8)] #[derive(Debug)] @@ -65,6 +65,7 @@ fn handle_instruction_abort(tf: &TrapFrame, iss: u64, is_user: bool) { } fn handle_data_abort(tf: &TrapFrame, iss: u64, is_user: bool) { + debug!("ISS={:#x} tf={:#x?} is_user={}", iss, tf, is_user); let wnr = (iss & (1 << 6)) != 0; // WnR: Write not Read let cm = (iss & (1 << 8)) != 0; // CM: Cache maintenance let mut access_flags = if wnr & !cm { @@ -98,13 +99,22 @@ fn handle_sync_exception(tf: &mut TrapFrame) { let esr = ESR_EL1.extract(); let iss = esr.read(ESR_EL1::ISS); match esr.read_as_enum(ESR_EL1::EC) { + #[cfg(feature = "uspace")] Some(ESR_EL1::EC::Value::SVC64) => { - warn!("No syscall is supported currently!"); + tf.r[0] = crate::trap::handle_syscall(tf, tf.r[8] as usize) as u64; } Some(ESR_EL1::EC::Value::InstrAbortLowerEL) => handle_instruction_abort(tf, iss, true), Some(ESR_EL1::EC::Value::InstrAbortCurrentEL) => handle_instruction_abort(tf, iss, false), Some(ESR_EL1::EC::Value::DataAbortLowerEL) => handle_data_abort(tf, iss, true), Some(ESR_EL1::EC::Value::DataAbortCurrentEL) => handle_data_abort(tf, iss, false), + Some(ESR_EL1::EC::Value::TrappedFP) => { + debug!("Trapped FP @ {:#x} ", tf.elr); + tf.elr += 4; + } + Some(ESR_EL1::EC::Value::Unknown) => { + debug!("Unknown exception @ {:#x} ", tf.elr); + tf.elr += 4; + } Some(ESR_EL1::EC::Value::Brk64) => { debug!("BRK #{:#x} @ {:#x} ", iss, tf.elr); tf.elr += 4; diff --git a/arceos/modules/axhal/src/arch/loongarch64/context.rs b/arceos/modules/axhal/src/arch/loongarch64/context.rs new file mode 100644 index 000000000..a4acf33c3 --- /dev/null +++ b/arceos/modules/axhal/src/arch/loongarch64/context.rs @@ -0,0 +1,290 @@ +use core::arch::naked_asm; +use memory_addr::VirtAddr; + +/// General registers of Loongarch64. +#[allow(missing_docs)] +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct GeneralRegisters { + pub zero: usize, + pub ra: usize, + pub tp: usize, + pub sp: 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 t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub t7: usize, + pub t8: usize, + pub u0: usize, + pub fp: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, +} + +/// Saved registers when a trap (interrupt or exception) occurs. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct TrapFrame { + /// All general registers. + pub regs: GeneralRegisters, + /// Pre-exception Mode Information + pub prmd: usize, + /// Exception Return Address + pub era: usize, +} + +impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.regs.a0 as _ + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.regs.a1 as _ + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.regs.a2 as _ + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.regs.a3 as _ + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.regs.a4 as _ + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.regs.a5 as _ + } +} + +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub fn empty() -> Self { + Self(Default::default()) + } + + /// Creates a new context with the given entry point, user stack pointer, + /// and the argument. + pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self { + let mut trap_frame = TrapFrame::default(); + const PPLV_UMODE: usize = 0b11; + const PIE: usize = 1 << 2; + trap_frame.regs.sp = ustack_top.as_usize(); + trap_frame.era = entry; + trap_frame.prmd = PPLV_UMODE | PIE; + trap_frame.regs.a0 = arg0; + Self(trap_frame) + } + + /// Creates a new context from the given [`TrapFrame`]. + pub const fn from(trap_frame: &TrapFrame) -> Self { + Self(*trap_frame) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.era + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.regs.sp + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, pc: usize) { + self.0.era = pc; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, sp: usize) { + self.0.regs.sp = sp; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, a0: usize) { + self.0.regs.a0 = a0; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `era`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + #[unsafe(no_mangle)] + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + use loongArch64::register::era; + + super::disable_irqs(); + era::set_pc(self.get_ip()); + + unsafe { + core::arch::asm!( + include_asm_macros!(), + " + move $sp, {tf} + csrwr $tp, KSAVE_TP + csrwr $r21, KSAVE_R21 + LDD $tp, $sp, 32 + csrwr $tp, LA_CSR_PRMD + csrwr {kstack_top}, KSAVE_KSP // save ksp into SAVE0 CSR + + POP_GENERAL_REGS + + LDD $tp, $sp, 2 + LDD $r21, $sp, 21 + LDD $sp, $sp, 3 // user sp + ertn", + tf = in (reg) &self.0, + kstack_top = in(reg) kstack_top.as_usize(), + options(noreturn), + ) + } + } +} + +/// Saved hardware states of a task. +/// +/// The context usually includes: +/// +/// - Callee-saved registers +/// - Stack pointer register +/// - Thread pointer register (for thread-local storage, currently unsupported) +/// - FP/SIMD registers +/// +/// On context switch, current task saves its context from CPU to memory, +/// and the next task restores its context from memory to CPU. +#[allow(missing_docs)] +#[repr(C)] +#[derive(Debug, Default)] +pub struct TaskContext { + /// Return Address + pub ra: usize, + /// Stack Pointer + pub sp: usize, + /// loongArch need to save 10 static registers from $r22 to $r31 + pub s: [usize; 10], + /// Thread Pointer + pub tp: usize, + #[cfg(feature = "uspace")] + /// user page table root + pub pgdl: usize, +} + +impl TaskContext { + /// Creates a new default context for a new task. + pub fn new() -> Self { + Default::default() + } + + /// Initializes the context for a new task, with the given entry point and + /// kernel stack. + pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { + self.sp = kstack_top.as_usize(); + self.ra = entry; + self.tp = tls_area.as_usize(); + } + + /// Changes the page table root (`pgdl` register for loongarch64). + /// + /// If not set, it means that this task is a kernel task and only `pgdh` register will be used. + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, pgdl: memory_addr::PhysAddr) { + self.pgdl = pgdl.as_usize(); + } + + /// Switches to another task. + /// + /// It first saves the current task's context from CPU to this place, and then + /// restores the next task's context from `next_ctx` to CPU. + pub fn switch_to(&mut self, next_ctx: &Self) { + #[cfg(feature = "tls")] + { + self.tp = super::read_thread_pointer(); + unsafe { super::write_thread_pointer(next_ctx.tp) }; + } + #[cfg(feature = "uspace")] + { + if self.pgdl != next_ctx.pgdl { + unsafe { super::write_page_table_root0(pa!(next_ctx.pgdl)) }; + } + } + unsafe { context_switch(self, next_ctx) } + } +} + +#[naked] +unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { + unsafe { + naked_asm!( + include_asm_macros!(), + " + // save old context (callee-saved registers) + STD $ra, $a0, 0 + STD $sp, $a0, 1 + STD $s0, $a0, 2 + STD $s1, $a0, 3 + STD $s2, $a0, 4 + STD $s3, $a0, 5 + STD $s4, $a0, 6 + STD $s5, $a0, 7 + STD $s6, $a0, 8 + STD $s7, $a0, 9 + STD $s8, $a0, 10 + STD $fp, $a0, 11 + + // restore new context + LDD $fp, $a1, 11 + LDD $s8, $a1, 10 + LDD $s7, $a1, 9 + LDD $s6, $a1, 8 + LDD $s5, $a1, 7 + LDD $s4, $a1, 6 + LDD $s3, $a1, 5 + LDD $s2, $a1, 4 + LDD $s1, $a1, 3 + LDD $s0, $a1, 2 + LDD $sp, $a1, 1 + LDD $ra, $a1, 0 + + ret", + ) + } +} diff --git a/arceos/modules/axhal/src/arch/loongarch64/macros.rs b/arceos/modules/axhal/src/arch/loongarch64/macros.rs new file mode 100644 index 000000000..8e91d8fef --- /dev/null +++ b/arceos/modules/axhal/src/arch/loongarch64/macros.rs @@ -0,0 +1,78 @@ +macro_rules! include_asm_macros { + () => { + r" + .ifndef REGS_MACROS_FLAG + .equ REGS_MACROS_FLAG, 1 + + // CSR list + .equ LA_CSR_PRMD, 0x1 + .equ LA_CSR_EUEN, 0x2 + .equ LA_CSR_ERA, 0x6 + .equ LA_CSR_PGDL, 0x19 // Page table base address when VA[47] = 0 + .equ LA_CSR_PGDH, 0x1a // Page table base address when VA[47] = 1 + .equ LA_CSR_PGD, 0x1b // Page table base + .equ LA_CSR_PWCL, 0x1c + .equ LA_CSR_PWCH, 0x1d + .equ LA_CSR_TLBRENTRY, 0x88 // TLB refill exception entry + .equ LA_CSR_TLBRBADV, 0x89 // TLB refill badvaddr + .equ LA_CSR_TLBRERA, 0x8a // TLB refill ERA + .equ LA_CSR_TLBRSAVE, 0x8b // KScratch for TLB refill exception + .equ LA_CSR_TLBRELO0, 0x8c // TLB refill entrylo0 + .equ LA_CSR_TLBRELO1, 0x8d // TLB refill entrylo1 + .equ LA_CSR_TLBREHI, 0x8e // TLB refill entryhi + .equ LA_CSR_DMW0, 0x180 + .equ LA_CSR_DMW1, 0x181 + + .equ KSAVE_KSP, 0x30 + .equ KSAVE_TEMP, 0x31 + .equ KSAVE_R21, 0x32 + .equ KSAVE_TP, 0x33 + + .macro STD rd, rj, off + st.d \rd, \rj, \off*8 + .endm + .macro LDD rd, rj, off + ld.d \rd, \rj, \off*8 + .endm + + .macro PUSH_POP_GENERAL_REGS, op + \op $ra, $sp, 1 + \op $a0, $sp, 4 + \op $a1, $sp, 5 + \op $a2, $sp, 6 + \op $a3, $sp, 7 + \op $a4, $sp, 8 + \op $a5, $sp, 9 + \op $a6, $sp, 10 + \op $a7, $sp, 11 + \op $t0, $sp, 12 + \op $t1, $sp, 13 + \op $t2, $sp, 14 + \op $t3, $sp, 15 + \op $t4, $sp, 16 + \op $t5, $sp, 17 + \op $t6, $sp, 18 + \op $t7, $sp, 19 + \op $t8, $sp, 20 + \op $fp, $sp, 22 + \op $s0, $sp, 23 + \op $s1, $sp, 24 + \op $s2, $sp, 25 + \op $s3, $sp, 26 + \op $s4, $sp, 27 + \op $s5, $sp, 28 + \op $s6, $sp, 29 + \op $s7, $sp, 30 + \op $s8, $sp, 31 + .endm + + .macro PUSH_GENERAL_REGS + PUSH_POP_GENERAL_REGS STD + .endm + .macro POP_GENERAL_REGS + PUSH_POP_GENERAL_REGS LDD + .endm + + .endif" + }; +} diff --git a/arceos/modules/axhal/src/arch/loongarch64/mod.rs b/arceos/modules/axhal/src/arch/loongarch64/mod.rs new file mode 100644 index 000000000..afb696cd4 --- /dev/null +++ b/arceos/modules/axhal/src/arch/loongarch64/mod.rs @@ -0,0 +1,212 @@ +#[macro_use] +mod macros; + +mod context; +mod trap; + +use core::arch::asm; +use loongArch64::register::{crmd, ecfg, eentry, pgdh, pgdl, stlbps, tlbidx, tlbrehi, tlbrentry}; +use memory_addr::{PhysAddr, VirtAddr}; +use page_table_multiarch::loongarch64::LA64MetaData; + +pub use self::context::{TaskContext, TrapFrame}; + +#[cfg(feature = "uspace")] +pub use self::context::UspaceContext; + +/// Allows the current CPU to respond to interrupts. +#[inline] +pub fn enable_irqs() { + crmd::set_ie(true) +} + +/// Makes the current CPU to ignore interrupts. +#[inline] +pub fn disable_irqs() { + crmd::set_ie(false) +} + +/// Returns whether the current CPU is allowed to respond to interrupts. +#[inline] +pub fn irqs_enabled() -> bool { + crmd::read().ie() +} + +/// Relaxes the current CPU and waits for interrupts. +/// +/// It must be called with interrupts enabled, otherwise it will never return. +#[inline] +pub fn wait_for_irqs() { + unsafe { loongArch64::asm::idle() } +} + +/// Halt the current CPU. +#[inline] +pub fn halt() { + disable_irqs(); + unsafe { loongArch64::asm::idle() } +} + +/// Reads the register that stores the current kernel page table root. +/// +/// Returns the physical address of the kernel page table root. +#[inline] +pub fn read_page_table_root() -> PhysAddr { + PhysAddr::from(pgdh::read().base()) +} + +/// Reads the register that stores the current user page table root. +/// +/// Returns the physical address of the user page table root. +#[inline] +pub fn read_page_table_root0() -> PhysAddr { + PhysAddr::from(pgdl::read().base()) +} + +/// Writes the `pgdl` register. +/// +/// # Safety +/// +/// This function is unsafe as it changes the user virtual memory address space. +pub unsafe fn write_page_table_root0(root_paddr: PhysAddr) { + let old_root = read_page_table_root0(); + trace!( + "set user page table root: {:#x} => {:#x}", + old_root, root_paddr + ); + + pgdl::set_base(root_paddr.as_usize() as _); + flush_tlb(None); +} + +/// Writes the register to update the current page table root. +/// +/// # Safety +/// +/// This function is unsafe as it changes the kernel virtual memory address space. +/// NOTE: Compiler optimize inline on release mode, kernel raise error about +/// page table. So we prohibit inline operation. +#[inline(never)] +pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { + let old_root = read_page_table_root(); + trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + + pgdh::set_base(root_paddr.as_usize()); + flush_tlb(None); +} + +/// Flushes the TLB. +/// +/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB +/// entry that maps the given virtual address. +#[inline] +pub fn flush_tlb(vaddr: Option) { + unsafe { + if let Some(vaddr) = vaddr { + // + // + // Only after all previous load/store access operations are completely + // executed, the DBAR 0 instruction can be executed; and only after the + // execution of DBAR 0 is completed, all subsequent load/store access + // operations can be executed. + // + // + // + // formats: invtlb op, asid, addr + // + // op 0x5: Clear all page table entries with G=0 and ASID equal to the + // register specified ASID, and VA equal to the register specified VA. + // + // When the operation indicated by op does not require an ASID, the + // general register rj should be set to r0. + asm!("dbar 0; invtlb 0x05, $r0, {reg}", reg = in(reg) vaddr.as_usize()); + } else { + // op 0x0: Clear all page table entries + asm!("dbar 0; invtlb 0x00, $r0, $r0"); + } + } +} + +/// Writes Exception Entry Base Address Register (`eentry`). +/// +/// - ECFG: +/// - EENTRY: +#[inline] +pub fn set_exception_entry_base(eentry: usize) { + ecfg::set_vs(0); + eentry::set_eentry(eentry); +} + +/// Sets the PWC (Page Walk Controller) registers. +/// +/// # Safety +/// +/// This function uses `unsafe` inline assembly to write values to +/// `LA_CSR_PWCL` and `LA_CSR_PWCH`. +/// +/// - `PWCL` +/// - `PWCH` +#[inline] +pub fn set_pwc(pwcl: u32, pwch: u32) { + unsafe { + asm!( + include_asm_macros!(), + "csrwr {}, LA_CSR_PWCL", + "csrwr {}, LA_CSR_PWCH", + in(reg) pwcl, + in(reg) pwch + ) + } +} + +/// Init the TLB configuration and set tlb refill handler. +/// +/// TLBRENTY: +pub fn init_tlb() { + // Page Size 4KB + const PS_4K: usize = 0x0c; + tlbidx::set_ps(PS_4K); + stlbps::set_ps(PS_4K); + tlbrehi::set_ps(PS_4K); + + set_pwc(LA64MetaData::PWCL_VALUE, LA64MetaData::PWCH_VALUE); + + unsafe extern "C" { + fn handle_tlb_refill(); + } + let paddr = crate::mem::virt_to_phys(va!(handle_tlb_refill as usize)); + tlbrentry::set_tlbrentry(paddr.as_usize()); +} + +/// Reads the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +#[inline] +pub fn read_thread_pointer() -> usize { + let tp; + unsafe { asm!("move {}, $tp", out(reg) tp) }; + tp +} + +/// Writes the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +/// +/// # Safety +/// +/// This function is unsafe as it changes the CPU states. +#[inline] +pub unsafe fn write_thread_pointer(tp: usize) { + unsafe { asm!("move $tp, {}", in(reg) tp) } +} + +/// Initializes CPU states on the current CPU. +pub fn cpu_init() { + #[cfg(feature = "fp_simd")] + loongArch64::register::euen::set_fpe(true); + + unsafe extern "C" { + fn exception_entry_base(); + } + set_exception_entry_base(exception_entry_base as usize); +} diff --git a/arceos/modules/axhal/src/arch/loongarch64/trap.S b/arceos/modules/axhal/src/arch/loongarch64/trap.S new file mode 100644 index 000000000..697f7546e --- /dev/null +++ b/arceos/modules/axhal/src/arch/loongarch64/trap.S @@ -0,0 +1,78 @@ +.macro SAVE_REGS, from_user + move $t0, $sp +.if \from_user == 1 + csrrd $sp, KSAVE_KSP // restore kernel sp + addi.d $sp, $sp, -{trapframe_size} + + STD $tp, $sp, 2 + STD $r21, $sp, 21 + csrrd $tp, KSAVE_TP + csrrd $r21, KSAVE_R21 +.else + addi.d $sp, $sp, -{trapframe_size} +.endif + + STD $t0, $sp, 3 + csrrd $t0, KSAVE_TEMP + PUSH_GENERAL_REGS + csrrd $t1, LA_CSR_PRMD + csrrd $t2, LA_CSR_ERA + STD $t1, $sp, 32 // prmd + STD $t2, $sp, 33 // era +.endm + +.macro RESTORE_REGS, from_user +.if \from_user == 1 + csrwr $tp, KSAVE_TP + csrwr $r21, KSAVE_R21 + LDD $tp, $sp, 2 + LDD $r21, $sp, 21 +.endif + + LDD $t1, $sp, 33 // era + LDD $t2, $sp, 32 // prmd + csrwr $t1, LA_CSR_ERA + csrwr $t2, LA_CSR_PRMD + POP_GENERAL_REGS + LDD $sp, $sp, 3 +.endm + +.section .text +.balign 4096 +.global exception_entry_base +exception_entry_base: + csrwr $t0, KSAVE_TEMP + csrrd $t0, LA_CSR_PRMD + andi $t0, $t0, 0x3 + bnez $t0, .Lfrom_userspace + +.Lfrom_kernel: + SAVE_REGS 0 + move $a0, $sp + addi.d $a1, $zero, 0 + bl loongarch64_trap_handler + RESTORE_REGS 0 + ertn + +.Lfrom_userspace: + SAVE_REGS 1 + move $a0, $sp + addi.d $a1, $zero, 1 + bl loongarch64_trap_handler + RESTORE_REGS 1 + ertn + +.section .text +.balign 4096 +.global handle_tlb_refill +handle_tlb_refill: + csrwr $t0, LA_CSR_TLBRSAVE + csrrd $t0, LA_CSR_PGD + lddir $t0, $t0, 3 + lddir $t0, $t0, 2 + lddir $t0, $t0, 1 + ldpte $t0, 0 + ldpte $t0, 1 + tlbfill + csrrd $t0, LA_CSR_TLBRSAVE + ertn diff --git a/arceos/modules/axhal/src/arch/loongarch64/trap.rs b/arceos/modules/axhal/src/arch/loongarch64/trap.rs new file mode 100644 index 000000000..c32cbb00e --- /dev/null +++ b/arceos/modules/axhal/src/arch/loongarch64/trap.rs @@ -0,0 +1,72 @@ +use super::context::TrapFrame; +use loongArch64::register::{ + badv, + estat::{self, Exception, Trap}, +}; +use page_table_entry::MappingFlags; + +core::arch::global_asm!( + include_asm_macros!(), + include_str!("trap.S"), + trapframe_size = const (core::mem::size_of::()), +); + +fn handle_breakpoint(era: &mut usize) { + debug!("Exception(Breakpoint) @ {:#x} ", era); + *era += 4; +} + +fn handle_page_fault(tf: &TrapFrame, mut access_flags: MappingFlags, is_user: bool) { + if is_user { + access_flags |= MappingFlags::USER; + } + let vaddr = va!(badv::read().raw()); + if !handle_trap!(PAGE_FAULT, vaddr, access_flags, is_user) { + panic!( + "Unhandled {} Page Fault @ {:#x}, fault_vaddr={:#x} ({:?}):\n{:#x?}", + if is_user { "PLV3" } else { "PLV0" }, + tf.era, + vaddr, + access_flags, + tf, + ); + } +} + +#[unsafe(no_mangle)] +fn loongarch64_trap_handler(tf: &mut TrapFrame, from_user: bool) { + let estat = estat::read(); + + match estat.cause() { + #[cfg(feature = "uspace")] + Trap::Exception(Exception::Syscall) => { + tf.regs.a0 = crate::trap::handle_syscall(tf, tf.regs.a7) as usize; + tf.era += 4; + } + Trap::Exception(Exception::LoadPageFault) + | Trap::Exception(Exception::PageNonReadableFault) => { + handle_page_fault(tf, MappingFlags::READ, from_user) + } + Trap::Exception(Exception::StorePageFault) + | Trap::Exception(Exception::PageModifyFault) => { + handle_page_fault(tf, MappingFlags::WRITE, from_user) + } + Trap::Exception(Exception::FetchPageFault) + | Trap::Exception(Exception::PageNonExecutableFault) => { + handle_page_fault(tf, MappingFlags::EXECUTE, from_user); + } + Trap::Exception(Exception::Breakpoint) => handle_breakpoint(&mut tf.era), + Trap::Interrupt(_) => { + let irq_num: usize = estat.is().trailing_zeros() as usize; + handle_trap!(IRQ, irq_num); + } + _ => { + panic!( + "Unhandled trap {:?} @ {:#x}:\n{:#x?}", + estat.cause(), + tf.era, + tf + ); + } + } +} diff --git a/arceos/modules/axhal/src/cpu.rs b/arceos/modules/axhal/src/cpu.rs index d0ee27a34..12e5b8452 100644 --- a/arceos/modules/axhal/src/cpu.rs +++ b/arceos/modules/axhal/src/cpu.rs @@ -1,5 +1,7 @@ //! CPU-related operations. +use axlog::ax_println; + #[percpu::def_percpu] static CPU_ID: usize = 0; @@ -22,6 +24,18 @@ pub fn this_cpu_is_bsp() -> bool { IS_BSP.read_current() } +/// Stores the pointer to the current task in the SP_EL0 register. +/// +/// In aarch64 architecture, we use `SP_EL0` as the read cache for +/// the current task pointer. And this function will update this cache. +#[cfg(target_arch = "aarch64")] +pub(crate) unsafe fn cache_current_task_ptr() { + use tock_registers::interfaces::Writeable; + aarch64_cpu::registers::SP_EL0.set(CURRENT_TASK_PTR.read_current_raw() as u64); + //use tock_registers::interfaces::Readable; + //ax_println!("cache_current_task_ptr: SP_EL0={:#x?}", aarch64_cpu::registers::SP_EL0.get()); +} + /// Gets the pointer to the current task with preemption-safety. /// /// Preemption may be enabled when calling this function. This function will @@ -41,9 +55,10 @@ pub fn current_task_ptr() -> *const T { } #[cfg(target_arch = "aarch64")] { - // on ARM64, we use `SP_EL0` to store the task pointer. + //on ARM64, we use `SP_EL0` to store the task pointer. use tock_registers::interfaces::Readable; aarch64_cpu::registers::SP_EL0.get() as _ + } } @@ -68,8 +83,9 @@ pub unsafe fn set_current_task_ptr(ptr: *const T) { } #[cfg(target_arch = "aarch64")] { - use tock_registers::interfaces::Writeable; - aarch64_cpu::registers::SP_EL0.set(ptr as u64) + let _guard = kernel_guard::IrqSave::new(); + CURRENT_TASK_PTR.write_current_raw(ptr as usize); + cache_current_task_ptr(); } } diff --git a/arceos/modules/axmm/Cargo.toml b/arceos/modules/axmm/Cargo.toml index af5f1909a..9d683ecb8 100644 --- a/arceos/modules/axmm/Cargo.toml +++ b/arceos/modules/axmm/Cargo.toml @@ -11,6 +11,7 @@ documentation = "https://arceos-org.github.io/arceos/axmm/index.html" [dependencies] axhal = { workspace = true, features = ["paging"] } + axconfig = { workspace = true } axalloc = { workspace = true } diff --git a/arceos/modules/axtask/Cargo.toml b/arceos/modules/axtask/Cargo.toml index 25fe7fb37..99665d753 100644 --- a/arceos/modules/axtask/Cargo.toml +++ b/arceos/modules/axtask/Cargo.toml @@ -41,6 +41,7 @@ crate_interface = { version = "0.1", optional = true } scheduler = { git = "https://github.com/arceos-org/scheduler.git", tag = "v0.1.0", optional = true } [dev-dependencies] +log = "0.4.21" rand = "0.8" axhal = { workspace = true, features = ["fp_simd"] } axtask = { workspace = true, features = ["test", "multitask"] } diff --git a/arceos/payload/Makefile b/arceos/payload/Makefile index 42ddac8ae..a29d00c5b 100644 --- a/arceos/payload/Makefile +++ b/arceos/payload/Makefile @@ -1,9 +1,10 @@ SUB_DIRS=origin hello_c fileops_c mapfile_c skernel skernel2 +ARCH ?= riscv64 all: $(SUB_DIRS) $(SUB_DIRS): FORCE - make -C $@ + make ARCH=$(ARCH) -C $@ -B FORCE: diff --git a/arceos/payload/fileops_c/Makefile b/arceos/payload/fileops_c/Makefile index 4364f919e..5fca9c8d1 100644 --- a/arceos/payload/fileops_c/Makefile +++ b/arceos/payload/fileops_c/Makefile @@ -1,7 +1,18 @@ TARGET := fileops +ARCH ?= riscv64 -CC := riscv64-linux-musl-gcc -STRIP := riscv64-linux-musl-strip +ifeq ($(ARCH),riscv64) + CC := riscv64-linux-musl-gcc + STRIP := riscv64-linux-musl-strip +else ifeq ($(ARCH),x86_64) + CC := x86_64-linux-musl-gcc + STRIP := x86_64-linux-musl-strip +else ifeq ($(ARCH),aarch64) + CC := aarch64-linux-musl-gcc + STRIP := aarch64-linux-musl-strip +else + $(error Unsupported architecture: $(ARCH)) +endif all: $(TARGET) diff --git a/arceos/payload/hello_c/Makefile b/arceos/payload/hello_c/Makefile index 9525dd3f1..8bcfd3881 100644 --- a/arceos/payload/hello_c/Makefile +++ b/arceos/payload/hello_c/Makefile @@ -1,7 +1,18 @@ TARGET := hello +ARCH ?= riscv64 -CC := riscv64-linux-musl-gcc -STRIP := riscv64-linux-musl-strip +ifeq ($(ARCH),riscv64) + CC := riscv64-linux-musl-gcc + STRIP := riscv64-linux-musl-strip +else ifeq ($(ARCH),x86_64) + CC := x86_64-linux-musl-gcc + STRIP := x86_64-linux-musl-strip +else ifeq ($(ARCH),aarch64) + CC := aarch64-linux-musl-gcc + STRIP := aarch64-linux-musl-strip +else + $(error Unsupported architecture: $(ARCH)) +endif all: $(TARGET) diff --git a/arceos/payload/mapfile_c/Makefile b/arceos/payload/mapfile_c/Makefile index 7cebfb3d0..fab046436 100644 --- a/arceos/payload/mapfile_c/Makefile +++ b/arceos/payload/mapfile_c/Makefile @@ -1,7 +1,18 @@ TARGET := mapfile +ARCH ?= riscv64 -CC := riscv64-linux-musl-gcc -STRIP := riscv64-linux-musl-strip +ifeq ($(ARCH),riscv64) + CC := riscv64-linux-musl-gcc + STRIP := riscv64-linux-musl-strip +else ifeq ($(ARCH),x86_64) + CC := x86_64-linux-musl-gcc + STRIP := x86_64-linux-musl-strip +else ifeq ($(ARCH),aarch64) + CC := aarch64-linux-musl-gcc + STRIP := aarch64-linux-musl-strip +else + $(error Unsupported architecture: $(ARCH)) +endif all: $(TARGET) diff --git a/arceos/payload/origin/Makefile b/arceos/payload/origin/Makefile index 2c6c83504..a69a0a944 100644 --- a/arceos/payload/origin/Makefile +++ b/arceos/payload/origin/Makefile @@ -1,17 +1,39 @@ TARGET := origin -TARGET_ELF := ../../target/riscv64gc-unknown-none-elf/release/$(TARGET) +ARCH ?= riscv64 + +ifeq ($(ARCH),riscv64) + TARGET_TRIPLE := riscv64gc-unknown-none-elf + BINARY_ARCH := riscv64 +else ifeq ($(ARCH),x86_64) + TARGET_TRIPLE := x86_64-unknown-none + BINARY_ARCH := x86_64 +else ifeq ($(ARCH),aarch64) + TARGET_TRIPLE := aarch64-unknown-none + BINARY_ARCH := aarch64 +else + $(error Unsupported architecture: $(ARCH)) +endif + +TARGET_ELF := ../../target/$(TARGET_TRIPLE)/release/$(TARGET) +TARGET_BIN := $(TARGET).$(ARCH) all: $(TARGET) FORCE $(TARGET): $(TARGET_ELF) - @rust-objcopy --binary-architecture=riscv64 --strip-all -O binary $< $@ + @echo "Building for $(ARCH) architecture..." + @rust-objcopy --binary-architecture=$(BINARY_ARCH) --strip-all -O binary $< $@ $(TARGET_ELF): - @cargo build -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @echo "Compiling for $(ARCH) architecture..." + @cargo build -p $(TARGET) --target $(TARGET_TRIPLE) --release + clean: - @rm -rf ./$(TARGET) - @cargo clean -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @rm -rf ./$(TARGET).* + @for arch in riscv64 x86_64 aarch64 ; do \ + echo "Cleaning $$arch build..."; \ + cargo clean -p $(TARGET) --target $$(make --no-print-directory ARCH=$$arch get-target-triple) --release 2>/dev/null || true; \ + done FORCE: diff --git a/arceos/payload/origin/src/main.rs b/arceos/payload/origin/src/main.rs index 8fb6403f2..b52bcc473 100644 --- a/arceos/payload/origin/src/main.rs +++ b/arceos/payload/origin/src/main.rs @@ -5,13 +5,29 @@ use core::panic::PanicInfo; #[no_mangle] unsafe extern "C" fn _start() -> ! { + #[cfg(target_arch = "riscv64")] core::arch::asm!( "addi sp, sp, -4", "sw a0, (sp)", "li a7, 93", "ecall", - options(noreturn) - ) + options(noreturn), + ); + #[cfg(target_arch = "x86_64")] + core::arch::asm!( + "mov rax, 60", + "xor rdi, rdi", + "syscall", + options(noreturn), + ); + #[cfg(target_arch = "aarch64")] + core::arch::asm!( + "sub sp, sp, #4", // AArch64 栈操作 + "mov sp, x0", // 设置返回值为 0 + "mov x8, #93", // AArch64 系统调用号存放在 x8 + "svc #0", // 执行系统调用 + options(noreturn), + ); } #[panic_handler] diff --git a/arceos/payload/skernel/Makefile b/arceos/payload/skernel/Makefile index aa98ac2fa..56282a716 100644 --- a/arceos/payload/skernel/Makefile +++ b/arceos/payload/skernel/Makefile @@ -1,18 +1,40 @@ TARGET := skernel -TARGET_ELF := ../../target/riscv64gc-unknown-none-elf/release/$(TARGET) +ARCH ?= riscv64 + +ifeq ($(ARCH),riscv64) + TARGET_TRIPLE := riscv64gc-unknown-none-elf + BINARY_ARCH := riscv64 +else ifeq ($(ARCH),x86_64) + TARGET_TRIPLE := x86_64-unknown-none + BINARY_ARCH := x86_64 +else ifeq ($(ARCH),aarch64) + TARGET_TRIPLE := aarch64-unknown-none + BINARY_ARCH := aarch64 +else + $(error Unsupported architecture: $(ARCH)) +endif + +TARGET_ELF := ../../target/$(TARGET_TRIPLE)/release/$(TARGET) +TARGET_BIN := $(TARGET).$(ARCH) all: $(TARGET) FORCE $(TARGET): $(TARGET_ELF) - @rust-objcopy --binary-architecture=riscv64 --strip-all -O binary $< $@ + @echo "Building for $(ARCH) architecture..." + @rust-objcopy --binary-architecture=$(BINARY_ARCH) --strip-all -O binary $< $@ $(TARGET_ELF): - @cargo build -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @echo "Compiling for $(ARCH) architecture..." + @cargo build -p $(TARGET) --target $(TARGET_TRIPLE) --release + clean: - @rm -rf ./$(TARGET) - @cargo clean -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @rm -rf ./$(TARGET).* + @for arch in riscv64 x86_64 aarch64 ; do \ + echo "Cleaning $$arch build..."; \ + cargo clean -p $(TARGET) --target $$(make --no-print-directory ARCH=$$arch get-target-triple) --release 2>/dev/null || true; \ + done FORCE: -.PHONY: FORCE +.PHONY: FORCE \ No newline at end of file diff --git a/arceos/payload/skernel/src/main.rs b/arceos/payload/skernel/src/main.rs index b0e170b2b..626648fc4 100644 --- a/arceos/payload/skernel/src/main.rs +++ b/arceos/payload/skernel/src/main.rs @@ -5,11 +5,24 @@ use core::panic::PanicInfo; #[no_mangle] unsafe extern "C" fn _start() -> ! { + #[cfg(target_arch = "riscv64")] core::arch::asm!( "li a7, 8", "ecall", - options(noreturn) - ) + options(noreturn), + ); + #[cfg(target_arch = "x86_64")] + core::arch::asm!( + "mov rax, 8", // 将系统调用号 8 放入 rax 寄存器 + "syscall", // 执行系统调用 + options(noreturn), + ); + #[cfg(target_arch = "aarch64")] + core::arch::asm!( + "mov x8, #8", // 将系统调用号 8 放入 x8 寄存器 + "svc #0", // 执行系统调用 + options(noreturn), + ); } #[panic_handler] diff --git a/arceos/payload/skernel2/Makefile b/arceos/payload/skernel2/Makefile index 4541a9196..532b8adcd 100644 --- a/arceos/payload/skernel2/Makefile +++ b/arceos/payload/skernel2/Makefile @@ -1,18 +1,40 @@ TARGET := skernel2 -TARGET_ELF := ../../target/riscv64gc-unknown-none-elf/release/$(TARGET) +ARCH ?= riscv64 -all: clean $(TARGET) FORCE +ifeq ($(ARCH),riscv64) + TARGET_TRIPLE := riscv64gc-unknown-none-elf + BINARY_ARCH := riscv64 +else ifeq ($(ARCH),x86_64) + TARGET_TRIPLE := x86_64-unknown-none + BINARY_ARCH := x86_64 +else ifeq ($(ARCH),aarch64) + TARGET_TRIPLE := aarch64-unknown-none + BINARY_ARCH := aarch64 +else + $(error Unsupported architecture: $(ARCH)) +endif + +TARGET_ELF := ../../target/$(TARGET_TRIPLE)/release/$(TARGET) +TARGET_BIN := $(TARGET).$(ARCH) + +all: $(TARGET) FORCE $(TARGET): $(TARGET_ELF) - @rust-objcopy --binary-architecture=riscv64 --strip-all -O binary $< $@ + @echo "Building for $(ARCH) architecture..." + @rust-objcopy --binary-architecture=$(BINARY_ARCH) --strip-all -O binary $< $@ $(TARGET_ELF): - @cargo build -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @echo "Compiling for $(ARCH) architecture..." + @cargo build -p $(TARGET) --target $(TARGET_TRIPLE) --release + clean: - @rm -rf ./$(TARGET) - @cargo clean -p $(TARGET) --target riscv64gc-unknown-none-elf --release + @rm -rf ./$(TARGET).* + @for arch in riscv64 x86_64 aarch64 ; do \ + echo "Cleaning $$arch build..."; \ + cargo clean -p $(TARGET) --target $$(make --no-print-directory ARCH=$$arch get-target-triple) --release 2>/dev/null || true; \ + done FORCE: -.PHONY: FORCE +.PHONY: FORCE \ No newline at end of file diff --git a/arceos/payload/skernel2/src/main.rs b/arceos/payload/skernel2/src/main.rs index 638915a92..998dc6dc1 100644 --- a/arceos/payload/skernel2/src/main.rs +++ b/arceos/payload/skernel2/src/main.rs @@ -5,13 +5,32 @@ use core::panic::PanicInfo; #[no_mangle] unsafe extern "C" fn _start() -> ! { + #[cfg(target_arch = "riscv64")] core::arch::asm!( "csrr a1, mhartid", "ld a0, 64(zero)", "li a7, 8", "ecall", options(noreturn) - ) + ); + #[cfg(target_arch = "x86_64")] + core::arch::asm!( + // x86_64 没有直接对应 mhartid 的方式,可以用 CPUID 但这里简化处理 + "xor rsi, rsi", // 清零 rsi (对应 a1) + "mov rdi, [64]", // 从地址 64 加载值到 rdi (对应 a0) + "mov rax, 8", // 系统调用号 + "syscall", // 执行系统调用 + options(noreturn) + ); + #[cfg(target_arch = "aarch64")] + core::arch::asm!( + "mrs x1, mpidr_el1", // 获取处理器 ID 到 x1 (对应 a1) + "mov x9, #64", // 将地址 64 放入临时寄存器 x9 + "ldr x0, [x9]", // 从地址 64 加载值到 x0 (对应 a0) + "mov x8, #8", // 系统调用号 + "svc #0", // 执行系统调用 + options(noreturn) + ); } #[panic_handler] diff --git a/arceos/platforms/aarch64-qemu-virt.toml b/arceos/platforms/aarch64-qemu-virt.toml index 8f8867375..67df4b225 100644 --- a/arceos/platforms/aarch64-qemu-virt.toml +++ b/arceos/platforms/aarch64-qemu-virt.toml @@ -25,11 +25,12 @@ kernel-aspace-base = "0xffff_0000_0000_0000" kernel-aspace-size = "0x0000_ffff_ffff_f000" # MMIO regions with format (`base_paddr`, `size`). mmio-regions = [ + ["0x0400_0000", "0x0400_0000"], # PFlash (added) ["0x0900_0000", "0x1000"], # PL011 UART ["0x0910_0000", "0x1000"], # PL031 RTC ["0x0800_0000", "0x2_0000"], # GICv2 ["0x0a00_0000", "0x4000"], # VirtIO - ["0x1000_0000", "0x2eff_0000"], # PCI memory ranges (ranges 1: 32-bit MMIO space) + ["0x1000_0000", "0x2eff_0000"], # PCI memory ranges (ranges 1: 32-bit MMIO space) ["0x40_1000_0000", "0x1000_0000"], # PCI config space ] # VirtIO MMIO regions with format (`base_paddr`, `size`). diff --git a/arceos/rust-toolchain.toml b/arceos/rust-toolchain.toml index 6d76fb10c..a6126495f 100644 --- a/arceos/rust-toolchain.toml +++ b/arceos/rust-toolchain.toml @@ -1,6 +1,5 @@ [toolchain] profile = "minimal" -# channel = "nightly-2024-05-02" channel = "nightly-2024-09-04" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none", "aarch64-unknown-none-softfloat"] diff --git a/arceos/scripts/make/utils.mk b/arceos/scripts/make/utils.mk index 42579be88..16b8ca4b6 100644 --- a/arceos/scripts/make/utils.mk +++ b/arceos/scripts/make/utils.mk @@ -7,6 +7,16 @@ GRAY_C := \033[90m WHITE_C := \033[37m END_C := \033[0m +ifeq ($(ARCH), x86_64) + COUNT ?= 64 +else ifeq ($(ARCH), riscv64) + COUNT ?= 32 +else ifeq ($(ARCH), aarch64) + COUNT ?= 64 +else + $(error "ARCH" must be one of "x86_64", "riscv64", or "aarch64") +endif + define run_cmd @printf '$(WHITE_C)$(1)$(END_C) $(GRAY_C)$(2)$(END_C)\n' @$(1) $(2) @@ -22,17 +32,20 @@ define make_disk_image $(if $(filter $(1),fat32), $(call make_disk_image_fat32,$(2))) endef + define mk_pflash - @RUSTFLAGS="" cargo build -p origin --target riscv64gc-unknown-none-elf --release - @rust-objcopy --binary-architecture=riscv64 --strip-all -O binary ./target/riscv64gc-unknown-none-elf/release/origin /tmp/origin.bin + @RUSTFLAGS="" cargo build -p origin --target $(AX_TARGET) --release + @rust-objcopy --binary-architecture=$(ARCH) --strip-all -O binary ./target/$(TARGET)/release/origin /tmp/origin.bin @printf "pfld\00\00\00\01" > /tmp/prefix.bin @printf "%08x" `stat -c "%s" /tmp/origin.bin` | xxd -r -ps > /tmp/size.bin @cat /tmp/prefix.bin /tmp/size.bin > /tmp/head.bin - @dd if=/dev/zero of=./$(1) bs=1M count=32 + @dd if=/dev/zero of=./$(1) bs=1M count=$(COUNT) @dd if=/tmp/head.bin of=./$(1) conv=notrunc @dd if=/tmp/origin.bin of=./$(1) seek=16 obs=1 conv=notrunc endef + + define setup_disk $(call build_origin) @mkdir -p ./mnt diff --git a/arceos/tour/m_1_0/src/main.rs b/arceos/tour/m_1_0/src/main.rs index bb58187f8..c7453fd62 100644 --- a/arceos/tour/m_1_0/src/main.rs +++ b/arceos/tour/m_1_0/src/main.rs @@ -41,10 +41,14 @@ fn main() { // Let's kick off the user process. let user_task = task::spawn_user_task( + Arc::new(Mutex::new(uspace)), + + #[cfg(target_arch = "riscv64")] UspaceContext::new(APP_ENTRY.into(), ustack_top), + #[cfg(target_arch = "aarch64")] + UspaceContext::new(APP_ENTRY.into(), ustack_top, 0), ); - // Wait for user process to exit ... let exit_code = user_task.join(); ax_println!("monolithic kernel exit [{:?}] normally!", exit_code); diff --git a/arceos/tour/m_1_1/src/loader.rs b/arceos/tour/m_1_1/src/loader.rs index 163d62805..0fe1b818b 100644 --- a/arceos/tour/m_1_1/src/loader.rs +++ b/arceos/tour/m_1_1/src/loader.rs @@ -7,7 +7,12 @@ use crate::APP_ENTRY; use alloc::vec; use alloc::vec::Vec; +#[cfg(target_arch = "riscv64")] +/// Physical address for pflash#1 const PFLASH_START: usize = 0x2200_0000; +#[cfg(target_arch = "aarch64")] +/// Physical address for pflash#1 +const PFLASH_START: usize = 0x0400_0000; const MAGIC: u32 = 0x64_6C_66_70; const VERSION: u32 = 0x01; diff --git a/arceos/tour/m_1_1/src/main.rs b/arceos/tour/m_1_1/src/main.rs index cbd85d964..25474a3ac 100644 --- a/arceos/tour/m_1_1/src/main.rs +++ b/arceos/tour/m_1_1/src/main.rs @@ -42,7 +42,10 @@ fn main() { // Let's kick off the user process. let user_task = task::spawn_user_task( Arc::new(Mutex::new(uspace)), + #[cfg(target_arch = "riscv64")] UspaceContext::new(APP_ENTRY.into(), ustack_top), + #[cfg(target_arch = "aarch64")] + UspaceContext::new(APP_ENTRY.into(), ustack_top, 0), ); // Wait for user process to exit ... diff --git a/arceos/tour/m_2_0/src/main.rs b/arceos/tour/m_2_0/src/main.rs index 08d5aec32..5ad399964 100644 --- a/arceos/tour/m_2_0/src/main.rs +++ b/arceos/tour/m_2_0/src/main.rs @@ -44,7 +44,10 @@ fn main() { // Let's kick off the user process. let user_task = task::spawn_user_task( Arc::new(Mutex::new(uspace)), + #[cfg(target_arch = "riscv64")] UspaceContext::new(APP_ENTRY.into(), ustack_top), + #[cfg(target_arch = "aarch64")] + UspaceContext::new(APP_ENTRY.into(), ustack_top, 0), ); // Wait for user process to exit ... @@ -70,6 +73,7 @@ fn init_user_stack(uspace: &mut AddrSpace, populating: bool) -> io::Result bool { + ax_println!("handle page fault: {:#x?} {:#x?}", vaddr, access_flags); if is_user { if !axtask::current() .task_ext() diff --git a/arceos/tour/m_3_0/src/main.rs b/arceos/tour/m_3_0/src/main.rs index 6a9f18043..a7e629281 100644 --- a/arceos/tour/m_3_0/src/main.rs +++ b/arceos/tour/m_3_0/src/main.rs @@ -45,7 +45,10 @@ fn main() { // Let's kick off the user process. let user_task = task::spawn_user_task( Arc::new(Mutex::new(uspace)), + #[cfg(target_arch = "riscv64")] UspaceContext::new(entry, ustack_top), + #[cfg(target_arch = "aarch64")] + UspaceContext::new(entry, ustack_top, 0), ); // Wait for user process to exit ... diff --git a/arceos/tour/m_3_0/src/syscall.rs b/arceos/tour/m_3_0/src/syscall.rs index 7e75bbc15..087ce17d9 100644 --- a/arceos/tour/m_3_0/src/syscall.rs +++ b/arceos/tour/m_3_0/src/syscall.rs @@ -18,7 +18,7 @@ const SYS_SET_TID_ADDRESS: usize = 96; fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize { ax_println!("handle_syscall [{}] ...", syscall_num); let ret = match syscall_num { - SYS_IOCTL => sys_ioctl(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _) as _, + SYS_IOCTL => sys_ioctl(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _) as _, SYS_SET_TID_ADDRESS => sys_set_tid_address(tf.arg0() as _), SYS_WRITEV => sys_writev(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _), SYS_EXIT_GROUP => { diff --git a/arceos/tour/m_3_1/src/main.rs b/arceos/tour/m_3_1/src/main.rs index 1d2ba9aee..82429c778 100644 --- a/arceos/tour/m_3_1/src/main.rs +++ b/arceos/tour/m_3_1/src/main.rs @@ -45,7 +45,10 @@ fn main() { // Let's kick off the user process. let user_task = task::spawn_user_task( Arc::new(Mutex::new(uspace)), + #[cfg(target_arch = "riscv64")] UspaceContext::new(entry, ustack_top), + #[cfg(target_arch = "aarch64")] + UspaceContext::new(entry, ustack_top, 0), ); // Wait for user process to exit ... diff --git a/arceos/tour/u_3_0/src/main.rs b/arceos/tour/u_3_0/src/main.rs index 516a4017e..127a67de3 100644 --- a/arceos/tour/u_3_0/src/main.rs +++ b/arceos/tour/u_3_0/src/main.rs @@ -8,8 +8,12 @@ extern crate axstd as std; use core::{mem, str}; use std::os::arceos::modules::axhal::mem::phys_to_virt; +#[cfg(target_arch = "riscv64")] /// Physical address for pflash#1 const PFLASH_START: usize = 0x2200_0000; +#[cfg(target_arch = "aarch64")] +/// Physical address for pflash#1 +const PFLASH_START: usize = 0x0400_0000; #[cfg_attr(feature = "axstd", no_mangle)] fn main() { diff --git a/arceos/tour/u_4_0/src/main.rs b/arceos/tour/u_4_0/src/main.rs index a85c60eef..e76637021 100644 --- a/arceos/tour/u_4_0/src/main.rs +++ b/arceos/tour/u_4_0/src/main.rs @@ -9,8 +9,12 @@ use core::{mem, str}; use std::thread; use std::os::arceos::modules::axhal::mem::phys_to_virt; +#[cfg(target_arch = "riscv64")] /// Physical address for pflash#1 const PFLASH_START: usize = 0x2200_0000; +#[cfg(target_arch = "aarch64")] +/// Physical address for pflash#1 +const PFLASH_START: usize = 0x0400_0000; #[cfg_attr(feature = "axstd", no_mangle)] fn main() { diff --git a/scripts/tour_test.sh b/scripts/tour_test.sh index 79038a8df..a177a9f72 100755 --- a/scripts/tour_test.sh +++ b/scripts/tour_test.sh @@ -20,21 +20,22 @@ run_test() { local args=("$@") - local app=$1 - local blk=$2 - local payload=$3 - local update_disk_file=$4 - local eval_code=$5 + local arch=$1 + local app=$2 + local blk=$3 + local payload=$4 + local update_disk_file=$5 + local eval_code=$6 local tmp_file=tmpfile rm -rf disk.img pflash.img $tmp_file - make disk_img >/dev/null 2>&1 - make pflash_img >/dev/null 2>&1 + make disk_img ARCH="$arch" >/dev/null 2>&1 + make pflash_img ARCH="$arch" >/dev/null 2>&1 if [[ -n $payload ]]; then - make payload >/dev/null 2>&1 + make payload ARCH="$arch" >/dev/null 2>&1 fi if [[ -n $eval_code ]]; then @@ -45,12 +46,12 @@ run_test() { ./update_disk.sh "$update_disk_file" >/dev/null 2>&1 fi - make run A="$app" BLK="$blk" >"$tmp_file" 2>&1 + make run A="$app" BLK="$blk" ARCH="$arch" >"$tmp_file" 2>&1 local OUTPUT=$? local SEARCH=0 - if [ "${#args[@]}" -gt 5 ]; then - if ! search_texts "${args[@]:5}"; then + if [ "${#args[@]}" -gt 6 ]; then + if ! search_texts "${args[@]:6}"; then SEARCH=1 fi fi @@ -67,24 +68,39 @@ run_test() { rm -rf Success.log Error.log # app路径, 是否挂载磁盘(n=否/y=是), 是否重新编译payload, 是否更新磁盘文件, 是否在更新磁盘文件前有额外操作, 之后所有传入的都是需要被查询的参数 -run_test "tour/u_1_0" "n" "" "" "" "Hello, Arceos!" -run_test "tour/u_2_0" "n" "" "" "" "Alloc Vec" "Alloc String:" -run_test "tour/u_3_0" "n" "" "" "" "Try to access dev region" "Got pflash magic: pfld" -run_test "tour/u_4_0" "n" "" "" "" "Got pflash magic: pfld" "Multi-task OK!" "Spawned-thread ..." -run_test "tour/u_5_0" "n" "" "" "" "worker2 ok!" "worker1 ok!" "Multi-task OK!" -run_test "tour/u_6_0" "n" "" "" "" "Multi-task(Preemptible) ok!" -run_test "tour/u_6_1" "n" "" "" "" "worker2 ok!" "worker1 ok!" "WaitQ ok!" -run_test "tour/u_7_0" "y" "" "" "" "[mkfs.fat]" "worker1 ok!" "Load app from disk ok!" -run_test "tour/u_8_0" "y" "" "" "" "worker1 checks code:" "worker1 ok!" "Load app from disk ok!" -run_test "tour/m_1_0" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" -run_test "tour/m_1_1" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" -run_test "tour/m_2_0" "y" "y" "payload/origin/origin" "" "handle page fault OK!" "monolithic kernel exit [Some(0)] normally!" -run_test "tour/m_3_0" "y" "y" "payload/hello_c/hello" "" "Hello, UserApp!" "monolithic kernel exit [Some(0)] normally!" -run_test "tour/m_3_1" "y" "y" "payload/fileops_c/fileops" "" "FileOps ok!" "monolithic kernel exit [Some(0)] normally!" -run_test "tour/h_1_0" "y" "y" "payload/skernel/skernel" "" "Shutdown vm normally!" "Hypervisor ok!" -run_test "tour/h_2_0" "y" "y" "tour/u_3_0/u_3_0_riscv64-qemu-virt.bin" "make A=tour/u_3_0/" "Got pflash magic: pfld" -run_test "tour/h_3_0" "y" "y" "tour/u_6_0/u_6_0_riscv64-qemu-virt.bin" "make A=tour/u_6_0/" "Multi-task(Preemptible) ok!" -run_test "tour/h_4_0" "y" "y" "tour/m_1_1/m_1_1_riscv64-qemu-virt.bin" "make A=tour/m_1_1" "monolithic kernel exit [Some(0)] normally!" "handle_syscall ..." +run_test "riscv64" "tour/u_1_0" "n" "" "" "" "Hello, Arceos!" +run_test "riscv64" "tour/u_2_0" "n" "" "" "" "Alloc Vec" "Alloc String:" +run_test "riscv64" "tour/u_3_0" "n" "" "" "" "Try to access dev region" "Got pflash magic: pfld" +run_test "riscv64" "tour/u_4_0" "n" "" "" "" "Got pflash magic: pfld" "Multi-task OK!" "Spawned-thread ..." +run_test "riscv64" "tour/u_5_0" "n" "" "" "" "worker2 ok!" "worker1 ok!" "Multi-task OK!" +run_test "riscv64" "tour/u_6_0" "n" "" "" "" "Multi-task(Preemptible) ok!" +run_test "riscv64" "tour/u_6_1" "n" "" "" "" "worker2 ok!" "worker1 ok!" "WaitQ ok!" +run_test "riscv64" "tour/u_7_0" "y" "" "" "" "[mkfs.fat]" "worker1 ok!" "Load app from disk ok!" +run_test "riscv64" "tour/u_8_0" "y" "" "" "" "worker1 checks code:" "worker1 ok!" "Load app from disk ok!" +run_test "riscv64" "tour/m_1_0" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" +run_test "riscv64" "tour/m_1_1" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" +run_test "riscv64" "tour/m_2_0" "y" "y" "payload/origin/origin" "" "handle page fault OK!" "monolithic kernel exit [Some(0)] normally!" +run_test "riscv64" "tour/m_3_0" "y" "y" "payload/hello_c/hello" "" "Hello, UserApp!" "monolithic kernel exit [Some(0)] normally!" +run_test "riscv64" "tour/m_3_1" "y" "y" "payload/fileops_c/fileops" "" "FileOps ok!" "monolithic kernel exit [Some(0)] normally!" +run_test "riscv64" "tour/h_1_0" "y" "y" "payload/skernel/skernel" "" "Shutdown vm normally!" "Hypervisor ok!" +run_test "riscv64" "tour/h_2_0" "y" "y" "tour/u_3_0/u_3_0_riscv64-qemu-virt.bin" "make A=tour/u_3_0/" "Got pflash magic: pfld" +run_test "riscv64" "tour/h_3_0" "y" "y" "tour/u_6_0/u_6_0_riscv64-qemu-virt.bin" "make A=tour/u_6_0/" "Multi-task(Preemptible) ok!" +run_test "riscv64" "tour/h_4_0" "y" "y" "tour/m_1_1/m_1_1_riscv64-qemu-virt.bin" "make A=tour/m_1_1" "monolithic kernel exit [Some(0)] normally!" "handle_syscall ..." +make clean >/dev/null 2>&1 +run_test "aarch64" "tour/u_1_0" "n" "" "" "" "Hello, Arceos!" +run_test "aarch64" "tour/u_2_0" "n" "" "" "" "Alloc Vec" "Alloc String:" +run_test "aarch64" "tour/u_3_0" "n" "" "" "" "Try to access dev region" "Got pflash magic: pfld" +run_test "aarch64" "tour/u_4_0" "n" "" "" "" "Got pflash magic: pfld" "Multi-task OK!" "Spawned-thread ..." +run_test "aarch64" "tour/u_5_0" "n" "" "" "" "worker2 ok!" "worker1 ok!" "Multi-task OK!" +run_test "aarch64" "tour/u_6_0" "n" "" "" "" "Multi-task(Preemptible) ok!" +run_test "aarch64" "tour/u_6_1" "n" "" "" "" "worker2 ok!" "worker1 ok!" "WaitQ ok!" +run_test "aarch64" "tour/u_7_0" "y" "" "" "" "[mkfs.fat]" "worker1 ok!" "Load app from disk ok!" +run_test "aarch64" "tour/u_8_0" "y" "" "" "" "worker1 checks code:" "worker1 ok!" "Load app from disk ok!" +run_test "aarch64" "tour/m_1_0" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" +run_test "aarch64" "tour/m_1_1" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" +run_test "aarch64" "tour/m_2_0" "y" "y" "payload/origin/origin" "" "monolithic kernel exit [Some(0)] normally!" +run_test "aarch64" "tour/m_3_0" "y" "y" "payload/hello_c/hello" "" "Hello, UserApp!" "monolithic kernel exit [Some(0)] normally!" +run_test "aarch64" "tour/m_3_1" "y" "y" "payload/fileops_c/fileops" "" "FileOps ok!" "monolithic kernel exit [Some(0)] normally!" if [[ -s Error.log ]]; then cat Error.log # Print the content of Error.log