diff --git a/.gitignore b/.gitignore index 087d952d0..1021e0143 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ jenkins/__pycache__ jenkins-cli.jar tools/kconfig/.venv/ tools/kconfig/__pycache__/ -kernel_build/ \ No newline at end of file +kernel_build/ +*.cpio.gz +*.cpio diff --git a/Makefile b/Makefile index 4fb832a86..a53257dc3 100644 --- a/Makefile +++ b/Makefile @@ -194,6 +194,10 @@ test-pre: download-test-img chmod +x platform/$(ARCH)/$(BOARD)/test/runner.sh @echo "added execute permission to test runner.sh for board $(BOARD)" +fmt-test: all + cargo fmt --all -- --check + @echo "cargo fmt check passed!" + fmt: all cargo fmt --all @echo "your code has been formatted" diff --git a/platform/riscv64/qemu-plic/configs/zone1-asterinas.json b/platform/riscv64/qemu-plic/configs/zone1-asterinas.json new file mode 100644 index 000000000..1e378d0b0 --- /dev/null +++ b/platform/riscv64/qemu-plic/configs/zone1-asterinas.json @@ -0,0 +1,58 @@ +{ + "arch": "riscv", + "name": "asterinas", + "zone_id": 1, + "cpus": [ + 2 + ], + "memory_regions": [ + { + "type": "ram", + "physical_start": "0x80200000", + "virtual_start": "0x80200000", + "size": "0x10000000" + }, + { + "type": "io", + "physical_start": "0x10000000", + "virtual_start": "0x10000000", + "size": "0x100" + }, + { + "type": "io", + "physical_start": "0x10007000", + "virtual_start": "0x10007000", + "size": "0x1000" + }, + { + "type": "ram", + "physical_start": "0x87e00000", + "virtual_start": "0x87e00000", + "size": "0x00200000" + } + ], + "interrupts": [ + 7, + 10 + ], + "ivc_configs": [], + "kernel_filepath": "./asterinas.bin", + "dtb_filepath": "./zone1-asterinas.dtb", + "kernel_load_paddr": "0x84000000", + "dtb_load_paddr": "0x83000000", + "entry_point": "0x84000000", + "modules": [ + { + "name": "initramfs", + "filepath": "./initramfs.cpio.gz", + "load_paddr": "0x87e00000" + } + ], + "arch_config": { + "plic_base": "0xc000000", + "plic_size": "0x600000" + }, + "pci_config": [], + "num_pci_devs": 0, + "alloc_pci_devs": [] +} \ No newline at end of file diff --git a/platform/riscv64/qemu-plic/image/dts/zone1-asterinas.dts b/platform/riscv64/qemu-plic/image/dts/zone1-asterinas.dts new file mode 100644 index 000000000..3f164756a --- /dev/null +++ b/platform/riscv64/qemu-plic/image/dts/zone1-asterinas.dts @@ -0,0 +1,94 @@ +/dts-v1/; + +/ { + #address-cells = <0x2>; + #size-cells = <0x2>; + model = "asterinas,dummy-virt"; + compatible = "asterinas,dummy-virt"; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + timebase-frequency = <10000000>; + + cpu@2 { + device_type = "cpu"; + reg = <0x2>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv48"; + + cpu2_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + }; + + memory@80200000 { + device_type = "memory"; + reg = <0x0 0x80200000 0x0 0x10000000>; + }; + + reserved-memory { + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + + opensbi+hvisor@80000000 { + no-map; + reg = <0x00 0x80000000 0x00 0x05000000>; + }; + + dtbfile@83000000 { + no-map; + reg = <0x00 0x83000000 0x00 0x01000000>; + }; + + initramfs@87e00000 { + no-map; + reg = <0x00 0x87e00000 0x00 0x00200000>; + }; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + + plic: interrupt-controller@c000000 { + phandle = <0x03>; + riscv,ndev = <0x5f>; + reg = <0x00 0xc000000 0x00 0x600000>; + interrupts-extended = <&cpu2_intc 11 &cpu2_intc 9>; + interrupt-controller; + compatible = "riscv,plic0"; + #interrupt-cells = <0x1>; + }; + + uart@10000000 { + interrupts = <0x0a>; + interrupt-parent = <&plic>; + clock-frequency = "\08@"; + reg = <0x00 0x10000000 0x00 0x100>; + compatible = "ns16550a"; + }; + + virtio_mmio@10007000 { + interrupts = <0x07>; + interrupt-parent = <&plic>; + reg = <0x00 0x10007000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + }; + + chosen { + bootargs = "init=/init console=ttyS0 ostd.log_level=debug"; + stdout-path = "serial0:115200n8"; + linux,initrd-start = <0x00 0x87e00000>; + linux,initrd-end = <0x00 0x88000000>; + }; +}; diff --git a/platform/riscv64/qemu-plic/platform.mk b/platform/riscv64/qemu-plic/platform.mk index a3574dc57..3c3c4caa1 100644 --- a/platform/riscv64/qemu-plic/platform.mk +++ b/platform/riscv64/qemu-plic/platform.mk @@ -7,8 +7,11 @@ FSIMG2 := $(image_dir)/virtdisk/rootfs2.ext4 HVISOR_ENTRY_PA := 0x80200000 zone0_kernel := $(image_dir)/kernel/Image zone0_dtb := $(image_dir)/dts/zone0.dtb -# zone1_kernel := $(image_dir)/kernel/Image -# zone1_dtb := $(image_dir)/devicetree/linux.dtb +# zone1 (Asterinas) images +# All zone1 images (kernel, dtb, initramfs) are loaded by hvisor-tool from zone0 +# See zone1-asterinas.json for the modules configuration +zone1_kernel := $(image_dir)/kernel/asterinas.bin +zone1_dtb := $(image_dir)/dts/zone1-asterinas.dtb QEMU_ARGS := -machine virt,aclint=on # ,iommu-sys=on # -d trace:*iommu* QEMU_ARGS += -bios default @@ -20,8 +23,8 @@ QEMU_ARGS += -nographic QEMU_ARGS += -kernel $(hvisor_bin) QEMU_ARGS += -device loader,file="$(zone0_kernel)",addr=0x90000000,force-raw=on QEMU_ARGS += -device loader,file="$(zone0_dtb)",addr=0x8f000000,force-raw=on -# QEMU_ARGS += -device loader,file="$(zone1_kernel)",addr=0x84000000,force-raw=on -# QEMU_ARGS += -device loader,file="$(zone1_dtb)",addr=0x83000000,force-raw=on +# zone1 images (kernel, dtb, initramfs) are all loaded by hvisor-tool from zone0 +# See zone1-asterinas.json for the modules configuration QEMU_ARGS += -drive if=none,file=$(FSIMG1),id=hd0,format=raw # QEMU_ARGS += -device virtio-blk-device,drive=hd0,bus=virtio-mmio-bus.7 diff --git a/src/arch/riscv64/cpu.rs b/src/arch/riscv64/cpu.rs index bc6997dbd..399f8fd6a 100644 --- a/src/arch/riscv64/cpu.rs +++ b/src/arch/riscv64/cpu.rs @@ -67,7 +67,7 @@ impl ArchCpu { //self.sepc = guest_test as usize as u64; write_csr!(CSR_SSCRATCH, self as *const _ as usize); //arch cpu pointer self.sepc = entry; - self.hstatus = 1 << 7 | 2 << 32; // HSTATUS_SPV | HSTATUS_VSXL_64 + self.hstatus = 1 << 7 | 2 << 32 | 1 << 56; // HSTATUS_SPV | HSTATUS_VSXL_64 | HSTATUS_SPVTW #[cfg(aia)] { self.hstatus |= 1 << 12; // HSTATUS_VGEIN @@ -83,12 +83,12 @@ impl ArchCpu { if self.sstc { // hvisor doesn't handle timer interrupt. set_csr!(CSR_STIMECMP, usize::MAX); - set_csr!(CSR_HENVCFG, 1 << 63); + set_csr!(CSR_HENVCFG, 1 << 63 | 1 << 61); // STCE | ADUE (enable hardware A/D bit update for guest) set_csr!(CSR_VSTIMECMP, usize::MAX); } else { // In megrez board, this instruction is not supported. (illegal instruction) #[cfg(not(hypervisor_v0_6))] - set_csr!(CSR_HENVCFG, 0); + set_csr!(CSR_HENVCFG, 1 << 61); // ADUE (enable hardware A/D bit update for guest) } set_csr!(CSR_HCOUNTEREN, 1 << 1); // HCOUNTEREN_TM // In VU-mode, a counter is not readable unless the applicable bits are set in both hcounteren and scounteren. diff --git a/src/arch/riscv64/sbi.rs b/src/arch/riscv64/sbi.rs index 00436ddc9..310c63e43 100644 --- a/src/arch/riscv64/sbi.rs +++ b/src/arch/riscv64/sbi.rs @@ -33,14 +33,24 @@ use sbi_spec::{base, hsm, legacy, rfnc, spi, time}; // Reserved for hvisor-tool. pub const EID_HVISOR: usize = 0x114514; +// SBI Debug Console Extension (DBCN) +pub const EID_DBCN: usize = 0x4442434E; +pub const DBCN_CONSOLE_WRITE_BYTE: usize = 2; + +// SBI System Reset Extension (SRST) +pub const EID_SRST: usize = 0x53525354; +pub const SRST_SYSTEM_RESET: usize = 0; + // Hvisor supported SBI extensions. -pub const NUM_EXT: usize = 6; +pub const NUM_EXT: usize = 8; pub const EXT_TABLE: [usize; NUM_EXT] = [ base::EID_BASE, time::EID_TIME, hsm::EID_HSM, spi::EID_SPI, rfnc::EID_RFNC, + EID_DBCN, + EID_SRST, EID_HVISOR, ]; @@ -91,6 +101,14 @@ pub fn sbi_vs_handler(current_cpu: &mut ArchCpu) { EID_HVISOR => { sbi_ret = sbi_hvisor_handler(current_cpu); } + // Debug Console Extension (DBCN) + EID_DBCN => { + sbi_ret = sbi_dbcn_handler(fid, current_cpu); + } + // System Reset Extension (SRST) + EID_SRST => { + sbi_ret = sbi_srst_handler(fid, current_cpu); + } // Note: hvisor don't suggest to use Legacy Extension. // But for compatibility, we still support some legacy SBI calls. // Legacy::Console putchar (usually used), temporily don't support other legacy extensions. @@ -366,3 +384,65 @@ pub fn sbi_hvisor_handler(current_cpu: &mut ArchCpu) -> SbiRet { } } } + +/// SBI Debug Console Extension handler. +/// Directly writes to UART hardware to avoid recursive SBI traps. +pub fn sbi_dbcn_handler(fid: usize, current_cpu: &mut ArchCpu) -> SbiRet { + match fid { + DBCN_CONSOLE_WRITE_BYTE => { + let byte = current_cpu.x[10] as u8; + // Directly write to UART hardware (QEMU virt 16550 at 0x10000000) + const UART0_BASE: usize = 0x1000_0000; + unsafe { + core::ptr::write_volatile(UART0_BASE as *mut u8, byte); + } + SbiRet { + error: RET_SUCCESS, + value: 1, // 1 byte written + } + } + _ => SbiRet { + error: RET_ERR_NOT_SUPPORTED, + value: 0, + }, + } +} + +/// SBI System Reset Extension handler. +/// Handles guest system reset requests (shutdown/reboot). +pub fn sbi_srst_handler(fid: usize, current_cpu: &mut ArchCpu) -> SbiRet { + if fid != SRST_SYSTEM_RESET { + return SbiRet { + error: RET_ERR_NOT_SUPPORTED, + value: 0, + }; + } + let reset_type = current_cpu.x[10] as u32; + let reset_reason = current_cpu.x[11] as u32; + info!( + "SBI SRST: system_reset type={}, reason={}", + reset_type, reset_reason + ); + // Use sbi_rt to perform the actual system reset. + // reset_type: 0 = shutdown, 1 = cold reboot, 2 = warm reboot + // reset_reason: 0 = none, 1 = failure, 2 = system failure + use sbi_rt::{ColdReboot, NoReason, Shutdown, SystemFailure, WarmReboot}; + let result = match (reset_type, reset_reason) { + (0, 0) => sbi_rt::system_reset(Shutdown, NoReason), + (0, 1) => sbi_rt::system_reset(Shutdown, SystemFailure), + (1, 0) => sbi_rt::system_reset(ColdReboot, NoReason), + (1, 1) => sbi_rt::system_reset(ColdReboot, SystemFailure), + (2, 0) => sbi_rt::system_reset(WarmReboot, NoReason), + (2, 1) => sbi_rt::system_reset(WarmReboot, SystemFailure), + _ => { + return SbiRet { + error: RET_ERR_NOT_SUPPORTED, + value: 0, + } + } + }; + SbiRet { + error: result.error, + value: result.value, + } +} diff --git a/src/arch/riscv64/trap.rs b/src/arch/riscv64/trap.rs index 4c8de184f..916f127ba 100644 --- a/src/arch/riscv64/trap.rs +++ b/src/arch/riscv64/trap.rs @@ -14,6 +14,7 @@ // Authors: // use super::cpu::ArchCpu; +use super::csr::*; use crate::arch::sbi::sbi_vs_handler; #[cfg(plic)] use crate::device::irqchip::plic::{inject_irq, plic_get_hwirq}; @@ -39,6 +40,7 @@ interrupts_arch_handle=sym interrupts_arch_handle); pub mod ExceptionType { pub const ECALL_VU: usize = 8; pub const ECALL_VS: usize = 10; + pub const INSTRUCTION_GUEST_PAGE_FAULT: usize = 20; pub const LOAD_GUEST_PAGE_FAULT: usize = 21; pub const STORE_GUEST_PAGE_FAULT: usize = 23; } @@ -135,6 +137,29 @@ pub fn sync_exception_handler(current_cpu: &mut ArchCpu) { sbi_vs_handler(current_cpu); current_cpu.sepc += 4; // For ecall, skip the ecall instruction. } + ExceptionType::INSTRUCTION_GUEST_PAGE_FAULT => { + trace!("INSTRUCTION_GUEST_PAGE_FAULT"); + // When guest hasn't set up its trap vector (vtvec=0), a timer interrupt + // can cause guest to jump to 0x0, leading to instruction page fault. + // Recover by disabling the timer interrupt and resuming from VSEPC. + let guest_vtvec = read_csr!(CSR_VSTVEC); + if guest_vtvec == 0 { + let vsepc = read_csr!(CSR_VSEPC); + warn!( + "Guest trapped to 0x0 (vtvec=0), VSEPC={:#x}. \ + Disabling VSTIMECMP and resuming.", + vsepc + ); + write_csr!(CSR_VSTIMECMP, usize::MAX); + unsafe { + hvip::clear_vstip(); + sie::clear_stimer(); + } + current_cpu.sepc = vsepc; + return; + } + guest_page_fault_handler(current_cpu); + } ExceptionType::LOAD_GUEST_PAGE_FAULT => { trace!("LOAD_GUEST_PAGE_FAULT"); guest_page_fault_handler(current_cpu);