diff --git a/Cargo.lock b/Cargo.lock index f713d8cdb4..7098b7c922 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,8 +175,7 @@ dependencies = [ [[package]] name = "arm_gicv2" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d25e73c949c69f75d1b9dba39c5475523403b31eb8c2fdc99da4dc33bc1aca" +source = "git+https://github.com/arceos-hypervisor/arm_gicv2#dfe5f164b94cdd07081c2fe74a0cfe4bef2852c9" dependencies = [ "tock-registers", ] @@ -214,6 +213,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axaddrspace" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axaddrspace.git#f1ab1108c1477f6f4f56035b74ea76c8932d6b8d" +dependencies = [ + "axerrno", + "bit_field", + "bitflags 2.6.0", + "cfg-if", + "lazyinit", + "log", + "memory_addr", + "memory_set", + "numeric-enum-macro", + "page_table_entry", + "page_table_multiarch", +] + [[package]] name = "axalloc" version = "0.1.0" @@ -446,6 +463,8 @@ dependencies = [ "axlog", "bitflags 2.6.0", "cfg-if", + "cortex-a", + "crate_interface", "dw_apb_uart", "handler_table", "int_ratio", @@ -462,12 +481,14 @@ dependencies = [ "riscv", "riscv_goldfish", "sbi-rt", + "spin", "static_assertions", "tock-registers", "x2apic", "x86", "x86_64 0.15.2", "x86_rtc", + "x86_vcpu", ] [[package]] @@ -550,6 +571,7 @@ dependencies = [ "chrono", "crate_interface", "kernel_guard", + "lazyinit", "percpu", ] @@ -595,6 +617,17 @@ dependencies = [ "timer_list", ] +[[package]] +name = "axvcpu" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axvcpu.git#8414a575723f929d5fd24010ce16446d90ecf268" +dependencies = [ + "axaddrspace", + "axerrno", + "memory_addr", + "percpu", +] + [[package]] name = "base64" version = "0.13.1" @@ -814,6 +847,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" +[[package]] +name = "cortex-a" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8256fd5103e10027467cc7a97c9ff27fcc4547ea24864da0aff2e7aef6e18e28" +dependencies = [ + "tock-registers", +] + [[package]] name = "cpumask" version = "0.1.0" @@ -1165,6 +1207,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f769efcf10b9dfb4c913bebb409cda77b1a3f072b249bf5465e250bcb30eb49" +[[package]] +name = "memory_set" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4552d02c866c57e8b06b919ea8c2f8f398cad245b8f6aac726657bc972d663d" +dependencies = [ + "memory_addr", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1190,6 +1241,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "numeric-enum-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e4bdb6b46b592948e700ea1ef24a4296491f6a0ee722b258040abd15a3714" + [[package]] name = "once_cell" version = "1.18.0" @@ -1904,6 +1961,27 @@ dependencies = [ "x86_64 0.15.2", ] +[[package]] +name = "x86_vcpu" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/x86_vcpu.git#5f6eaf4157ee8385ba1a2bfe5060098be18b0245" +dependencies = [ + "axaddrspace", + "axerrno", + "axvcpu", + "bit_field", + "bitflags 2.6.0", + "cfg-if", + "crate_interface", + "log", + "memory_addr", + "numeric-enum-macro", + "page_table_entry", + "raw-cpuid 11.1.0", + "x86", + "x86_64 0.15.2", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 2924a174af..87e7f85b09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,5 +63,7 @@ axsync = { path = "modules/axsync" } axtask = { path = "modules/axtask" } axdma = { path = "modules/axdma" } +allocator = { git = "https://github.com/arceos-org/allocator.git", tag = "v0.1.1" } + [profile.release] lto = true diff --git a/Makefile b/Makefile index f71378e3d8..322945da56 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,7 @@ APP_NAME := $(shell basename $(APP)) LD_SCRIPT := $(TARGET_DIR)/$(TARGET)/$(MODE)/linker_$(PLAT_NAME).lds OUT_ELF := $(OUT_DIR)/$(APP_NAME)_$(PLAT_NAME).elf OUT_BIN := $(OUT_DIR)/$(APP_NAME)_$(PLAT_NAME).bin +OUT_ASM := $(OUT_DIR)/$(APP_NAME)_$(PLAT_NAME).asm all: build @@ -142,6 +143,8 @@ ifeq ($(PLAT_NAME), aarch64-raspi4) include scripts/make/raspi4.mk else ifeq ($(PLAT_NAME), aarch64-bsta1000b) include scripts/make/bsta1000b-fada.mk +else ifeq ($(PLATFORM_NAME), aarch64-rk3588j) + include scripts/make/rk3588.mk endif defconfig: _axconfig-gen @@ -150,7 +153,7 @@ defconfig: _axconfig-gen oldconfig: _axconfig-gen $(call oldconfig) -build: $(OUT_DIR) $(OUT_BIN) +build: $(OUT_DIR) $(OUT_BIN) $(OUT_ASM) disasm: $(OBJDUMP) $(OUT_ELF) | less diff --git a/api/axfeat/Cargo.toml b/api/axfeat/Cargo.toml index ab86e937b5..804531f671 100644 --- a/api/axfeat/Cargo.toml +++ b/api/axfeat/Cargo.toml @@ -58,6 +58,9 @@ driver-ramdisk = ["axdriver?/ramdisk", "axfs?/use-ramdisk"] driver-ixgbe = ["axdriver?/ixgbe"] driver-bcm2835-sdhci = ["axdriver?/bcm2835-sdhci"] +#Hypervisor support +hv = ["axhal/hv"] + # Logging log-level-off = ["axlog/log-level-off"] log-level-error = ["axlog/log-level-error"] diff --git a/configs/platforms/aarch64-rk3588j.toml b/configs/platforms/aarch64-rk3588j.toml new file mode 100644 index 0000000000..61d0793234 --- /dev/null +++ b/configs/platforms/aarch64-rk3588j.toml @@ -0,0 +1,68 @@ +# Architecture identifier. +arch = "aarch64" +# Platform identifier. +platform = "aarch64-rk3588j" +# Platform family. +family = "aarch64-rk3588j" + +# Base address of the whole physical memory. +phys-memory-base = "0x20_0000" +# Size of the whole physical memory. +phys-memory-size = "0x800_0000" # 128M +# Base physical address of the kernel image. +kernel-base-paddr = "0x48_0000" +# Base virtual address of the kernel image. +kernel-base-vaddr = "0x0000_0000_0048_0000" +# Linear mapping offset, for quick conversions between physical and virtual +# addresses. +phys-virt-offset = "0xffff_0000_0000_0000" +# Kernel address space base. +kernel-aspace-base = "0xffff_0000_0000_0000" +# Kernel address space size. +kernel-aspace-size = "0x0000_ffff_ffff_f000" +# MMIO regions with format (`base_paddr`, `size`). +mmio-regions = [ + # ["0x0900_0000", "0x1000"], # PL011 UART + # ["0x0800_0000", "0x5_0000"], # GICv2 with Virtualization (GICV@0x0803_0000, GICH@0x0804_0000) + # ["0x0a00_0000", "0x4000"], # VirtIO + # ["0x1000_0000", "0x2eff_0000"], # PCI memory ranges (ranges 1: 32-bit MMIO space) + # ["0x40_1000_0000", "0x1000_0000"], # PCI config space + ["0xfeb50000", "0x1000"], # uart8250 UART0 + ["0xfe600000", "0x10000"], # gic-v3 gicd + ["0xfe680000", "0x100000"], # gic-v3 gicr + ["0xa41000000", "0x400000"], + ["0xa40c00000", "0x400000"], + ["0xf4000000","0x1000000"], + ["0xf3000000","0x1000000"], +] +# VirtIO MMIO regions with format (`base_paddr`, `size`). +virtio-mmio-regions = [] + +# Base physical address of the PCIe ECAM space. +pci-ecam-base = "0xf4000000" +# End PCI bus number (`bus-range` property in device tree). +pci-bus-end = "0xff" +# PCI device memory ranges (`ranges` property in device tree). +pci-ranges = [] +# UART Address +uart-paddr = "0xfeb5_0000" +uart-irq = "0x14d" + +# GICC Address +gicd-paddr = "0xfe600000" +# GICR Address +gicc-paddr = "0xfe680000" +gicr-paddr = "0xfe680000" + +# PSCI +psci-method = "smc" + +# pl031@9010000 { +# clock-names = "apb_pclk"; +# clocks = <0x8000>; +# interrupts = <0x00 0x02 0x04>; +# reg = <0x00 0x9010000 0x00 0x1000>; +# compatible = "arm,pl031\0arm,primecell"; +# }; +# RTC (PL031) Address +# rtc-paddr = "0x901_0000" \ No newline at end of file diff --git a/configs/platforms/x86_64-qemu-linux.toml b/configs/platforms/x86_64-qemu-linux.toml new file mode 100644 index 0000000000..4abca925dc --- /dev/null +++ b/configs/platforms/x86_64-qemu-linux.toml @@ -0,0 +1,48 @@ +# Architecture identifier. +arch = "x86_64" # str +# Platform identifier. +platform = "x86_64-qemu-linux" # str + +# +# Platform configs +# +[plat] +# Platform family. +family = "x86-linux" # str + +# Base address of the whole physical memory. +phys-memory-base = "0x4000_0000" # uint +# Size of the whole physical memory. (# 1G) +phys-memory-size = "0x4000_0000" # uint +# Base physical address of the kernel image. +kernel-base-paddr = "0x4000_0000" # uint +# Base virtual address of the kernel image. +kernel-base-vaddr = "0xffff_ff00_0000_0000" # uint +# Linear mapping offset, for quick conversions between physical and virtual +# addresses. (# kernel-base-vaddr - phys-memory-base) +phys-virt-offset = "0xffff_feff_c000_0000" # uint + +# +# Device specifications +# +[devices] +# MMIO regions with format (`base_paddr`, `size`). +mmio-regions = [ + [0xb000_0000, 0x1000_0000], # PCI config space + [0xfe00_0000, 0xc0_0000], # PCI devices + [0xfec0_0000, 0x1000], # IO APIC + [0xfed0_0000, 0x1000], # HPET + [0xfee0_0000, 0x1000], # Local APIC + [0x380000000000, 0x4000] # PCI devices +] # [(uint, uint)] +# VirtIO MMIO regions with format (`base_paddr`, `size`). +virtio-mmio-regions = [] # [(uint, uint)] +# Base physical address of the PCIe ECAM space (should read from ACPI 'MCFG' table). +pci-ecam-base = 0xb000_0000 # uint +# End PCI bus number. +pci-bus-end = 0xff # uint +# PCI device memory ranges (not used on x86). +pci-ranges = [] # [(uint, uint)] + +# Timer interrupt frequencyin Hz. (4.0GHz) +timer-frequency = 4_000_000_000 # uint diff --git a/configs/platforms/x86_64-qemu-q35.toml b/configs/platforms/x86_64-qemu-q35.toml index a44e98cf93..b68a10873a 100644 --- a/configs/platforms/x86_64-qemu-q35.toml +++ b/configs/platforms/x86_64-qemu-q35.toml @@ -40,6 +40,7 @@ mmio-regions = [ [0xfec0_0000, 0x1000], # IO APIC [0xfed0_0000, 0x1000], # HPET [0xfee0_0000, 0x1000], # Local APIC + [0x380000000000, 0x4000] # PCI devices ] # [(uint, uint)] # VirtIO MMIO regions with format (`base_paddr`, `size`). virtio-mmio-regions = [] # [(uint, uint)] diff --git a/doc/figures/RKDevTool3.3.png b/doc/figures/RKDevTool3.3.png new file mode 100644 index 0000000000..d390f4219b Binary files /dev/null and b/doc/figures/RKDevTool3.3.png differ diff --git a/doc/platform_rk3588.md b/doc/platform_rk3588.md new file mode 100644 index 0000000000..5e3cc5a873 --- /dev/null +++ b/doc/platform_rk3588.md @@ -0,0 +1,9 @@ +# How to run ArceOS on rk3588 + +1. Use Command `make ARCH=aarch64 PLATFORM=aarch64-rk3588j A=$(pwd)/examples/helloworld kernel` to build the kernel image `boot.img`. +2. Download the [RKDevTool](https://download.t-firefly.com/product/Board/RK3588/Tool/Window/RKDevTool_Release_v3.31.zip). + >This tool has only been tested on [Pji's](https://www.pji.net.cn/) Electronic Control Unit of RK3588. Other RK3588 development boards require independent testing. +3. Set the path of `boot.img` in **boot** and connect the RK3588 board. +4. Press the `Run` button to flash the image to the RK3588 board. + +![RKDevTool](./figures/RKDevTool3.3.png) \ No newline at end of file diff --git a/modules/axalloc/src/lib.rs b/modules/axalloc/src/lib.rs index ef6f0f6c94..979c3e67cf 100644 --- a/modules/axalloc/src/lib.rs +++ b/modules/axalloc/src/lib.rs @@ -143,7 +143,31 @@ impl GlobalAllocator { /// `align_pow2` must be a power of 2, and the returned region bound will be /// aligned to it. pub fn alloc_pages(&self, num_pages: usize, align_pow2: usize) -> AllocResult { - self.palloc.lock().alloc_pages(num_pages, align_pow2) + let res = self.palloc.lock().alloc_pages(num_pages, align_pow2); + + if res.is_err() { + warn!("alloc_pages res {:?}", res); + } + + res + } + + /// Allocates contiguous pages starting from the given address. + /// + /// It allocates `num_pages` pages from the page allocator starting from the + /// given address. + /// + /// `align_pow2` must be a power of 2, and the returned region bound will be + /// aligned to it. + pub fn alloc_pages_at( + &self, + start: usize, + num_pages: usize, + align_pow2: usize, + ) -> AllocResult { + self.palloc + .lock() + .alloc_pages_at(start, num_pages, align_pow2) } /// Gives back the allocated pages starts from `pos` to the page allocator. diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index b08438ef73..65a4783265 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -18,6 +18,15 @@ irq = [] tls = ["alloc"] rtc = ["x86_rtc", "riscv_goldfish", "arm_pl031"] default = [] +hv = [ + "paging", + "cortex-a", + "percpu/arm-el2", + "page_table_entry/arm-el2", + "arm_gicv2/el2", + "dep:crate_interface", + "dep:x86_vcpu", +] [dependencies] log = "=0.4.21" @@ -37,6 +46,8 @@ page_table_multiarch = { version = "0.5", optional = true } axlog = { workspace = true } axconfig = { workspace = true } axalloc = { workspace = true, optional = true } +cortex-a = { version = "8.1.1", optional = true } +spin = "0.9" [target.'cfg(target_arch = "x86_64")'.dependencies] x86 = "0.52" @@ -44,6 +55,7 @@ x86_64 = "0.15" x2apic = "0.4" raw-cpuid = "11.1" x86_rtc = { version = "0.1", optional = true } +x86_vcpu = { git = "https://github.com/arceos-hypervisor/x86_vcpu.git", optional = true } [target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] riscv = "0.11" @@ -53,7 +65,8 @@ riscv_goldfish = { version = "0.1", optional = true } [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "9.4" tock-registers = "0.8" -arm_gicv2 = "0.1" +arm_gicv2 = { git = "https://github.com/arceos-hypervisor/arm_gicv2" } +crate_interface = { version = "0.1.3", optional = true } arm_pl011 = "0.1" arm_pl031 = { version = "0.2", optional = true } dw_apb_uart = "0.1" diff --git a/modules/axhal/build.rs b/modules/axhal/build.rs index 56061e6434..774cb2c974 100644 --- a/modules/axhal/build.rs +++ b/modules/axhal/build.rs @@ -5,6 +5,7 @@ const BUILTIN_PLATFORMS: &[&str] = &[ "aarch64-bsta1000b", "aarch64-qemu-virt", "aarch64-raspi4", + "aarch64-rk3588j", "riscv64-qemu-virt", "x86_64-pc-oslab", "x86_64-qemu-q35", @@ -15,6 +16,7 @@ const BUILTIN_PLATFORM_FAMILIES: &[&str] = &[ "aarch64-phytium-pi", "aarch64-qemu-virt", "aarch64-raspi", + "aarch64-rk3588j", "riscv64-qemu-virt", "x86-pc", ]; @@ -58,7 +60,11 @@ fn gen_linker_script(arch: &str, platform: &str) -> Result<()> { } else { arch }; - let ld_content = std::fs::read_to_string("linker.lds.S")?; + let ld_content = if platform.contains("linux") { + std::fs::read_to_string("linker_linux.lds.S") + } else { + std::fs::read_to_string("linker.lds.S") + }?; let ld_content = ld_content.replace("%ARCH%", output_arch); let ld_content = ld_content.replace( "%KERNEL_BASE%", diff --git a/modules/axhal/linker_linux.lds.S b/modules/axhal/linker_linux.lds.S new file mode 100644 index 0000000000..c3daa62abc --- /dev/null +++ b/modules/axhal/linker_linux.lds.S @@ -0,0 +1,101 @@ +OUTPUT_ARCH(%ARCH%) + +BASE_ADDRESS = %KERNEL_BASE%; + +ENTRY(_start) +SECTIONS +{ + . = BASE_ADDRESS; + _skernel = .; + + .header : { + __header_start = .; + KEEP(*(.header)) + . = ALIGN(4K); + __header_end = .; + } + + .text : ALIGN(4K) { + _stext = .; + *(.text.boot) + *(.text .text.*) + . = ALIGN(4K); + _etext = .; + } + + .rodata : ALIGN(4K) { + _srodata = .; + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.sdata2 .sdata2.*) + . = ALIGN(4K); + _erodata = .; + } + + .data : ALIGN(4K) { + _sdata = .; + *(.data.boot_page_table) + . = ALIGN(4K); + *(.data .data.*) + *(.sdata .sdata.*) + *(.got .got.*) + } + + .tdata : ALIGN(0x10) { + _stdata = .; + *(.tdata .tdata.*) + _etdata = .; + } + + .tbss : ALIGN(0x10) { + _stbss = .; + *(.tbss .tbss.*) + *(.tcommon) + _etbss = .; + } + + . = ALIGN(4K); + _percpu_start = .; + _percpu_end = _percpu_start + SIZEOF(.percpu); + .percpu 0x0 : AT(_percpu_start) { + _percpu_load_start = .; + *(.percpu .percpu.*) + _percpu_load_end = .; + . = _percpu_load_start + ALIGN(64) * %SMP%; + } + . = _percpu_end; + + . = ALIGN(4K); + _edata = .; + + .bss : ALIGN(4K) { + boot_stack = .; + *(.bss.stack) + . = ALIGN(4K); + boot_stack_top = .; + + _sbss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + . = ALIGN(4K); + _ebss = .; + } + + _ekernel = .; + + __entry_offset = _start - BASE_ADDRESS; + __kernel_size = _ekernel - BASE_ADDRESS; + + /DISCARD/ : { + *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) + } +} + +SECTIONS { + linkme_IRQ : { *(linkme_IRQ) } + linkm2_IRQ : { *(linkm2_IRQ) } + linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } + linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } +} +INSERT AFTER .tbss; diff --git a/modules/axhal/src/arch/aarch64/mod.rs b/modules/axhal/src/arch/aarch64/mod.rs index 19c0134bb8..9a850cb47f 100644 --- a/modules/axhal/src/arch/aarch64/mod.rs +++ b/modules/axhal/src/arch/aarch64/mod.rs @@ -5,6 +5,10 @@ mod trap; use core::arch::asm; +#[cfg(feature = "hv")] +use aarch64_cpu::registers::{TTBR0_EL2, VBAR_EL2}; +//Todo: remove this, when hv is enabled, `TTBR1_EL1` is not used. +#[cfg_attr(feature = "hv", allow(unused_imports))] use aarch64_cpu::registers::{DAIF, TPIDR_EL0, TTBR0_EL1, TTBR1_EL1, VBAR_EL1}; use memory_addr::{PhysAddr, VirtAddr}; use tock_registers::interfaces::{Readable, Writeable}; @@ -49,8 +53,13 @@ pub fn halt() { /// Returns the physical address of the page table root. #[inline] pub fn read_page_table_root() -> PhysAddr { + #[cfg(not(feature = "hv"))] let root = TTBR1_EL1.get(); - pa!(root as usize) + + #[cfg(feature = "hv")] + let root = TTBR0_EL2.get(); + + PhysAddr::from(root as usize) } /// Reads the `TTBR0_EL1` register. @@ -68,8 +77,17 @@ 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); if old_root != root_paddr { - // kernel space page table use TTBR1 (0xffff_0000_0000_0000..0xffff_ffff_ffff_ffff) - TTBR1_EL1.set(root_paddr.as_usize() as _); + #[cfg(not(feature = "hv"))] + { + // kernel space page table use TTBR1 (0xffff_0000_0000_0000..0xffff_ffff_ffff_ffff) + TTBR1_EL1.set(root_paddr.as_usize() as _); + } + + #[cfg(feature = "hv")] + { + // kernel space page table at EL2 use TTBR0_EL2 (0x0000_0000_0000_0000..0x0000_ffff_ffff_ffff) + TTBR0_EL2.set(root_paddr.as_usize() as _); + } flush_tlb(None); } } @@ -92,10 +110,24 @@ pub unsafe fn write_page_table_root0(root_paddr: PhysAddr) { pub fn flush_tlb(vaddr: Option) { unsafe { if let Some(vaddr) = vaddr { - asm!("tlbi vaae1is, {}; dsb sy; isb", in(reg) vaddr.as_usize()) + #[cfg(not(feature = "hv"))] + { + asm!("tlbi vaae1is, {}; dsb sy; isb", in(reg) vaddr.as_usize()) + } + #[cfg(feature = "hv")] + { + asm!("tlbi vae2is, {}; dsb sy; isb", in(reg) vaddr.as_usize()) + } } else { // flush the entire TLB - asm!("tlbi vmalle1; dsb sy; isb") + #[cfg(not(feature = "hv"))] + { + asm!("tlbi vmalle1; dsb sy; isb") + } + #[cfg(feature = "hv")] + { + asm!("tlbi alle2is; dsb sy; isb") + } } } } @@ -108,8 +140,11 @@ pub fn flush_icache_all() { /// Sets the base address of the exception vector (writes `VBAR_EL1`). #[inline] -pub fn set_exception_vector_base(vbar_el1: usize) { - VBAR_EL1.set(vbar_el1 as _); +pub fn set_exception_vector_base(vbar: usize) { + #[cfg(not(feature = "hv"))] + VBAR_EL1.set(vbar as _); + #[cfg(feature = "hv")] + VBAR_EL2.set(vbar as _); } /// Flushes the data cache line (64 bytes) at the given virtual address diff --git a/modules/axhal/src/cpu.rs b/modules/axhal/src/cpu.rs index ae35d3654b..07aa4818e5 100644 --- a/modules/axhal/src/cpu.rs +++ b/modules/axhal/src/cpu.rs @@ -9,6 +9,9 @@ static IS_BSP: bool = false; #[percpu::def_percpu] static CURRENT_TASK_PTR: usize = 0; +#[percpu::def_percpu] +static IS_RESERVED: bool = false; + /// Returns the ID of the current CPU. #[inline] pub fn this_cpu_id() -> usize { @@ -22,6 +25,19 @@ pub fn this_cpu_is_bsp() -> bool { IS_BSP.read_current() } +/// Returns whether the current CPU is reserved for Linux. +#[inline] +pub fn this_cpu_is_reserved() -> bool { + IS_RESERVED.read_current() +} + +/// Sets the current CPU as reserved for Linux. +/// Only called from initialization code related to boot for Linux. +#[inline] +pub fn set_this_cpu_is_reserved() { + IS_RESERVED.write_current(true); +} + /// Gets the pointer to the current task with preemption-safety. /// /// Preemption may be enabled when calling this function. This function will diff --git a/modules/axhal/src/irq.rs b/modules/axhal/src/irq.rs index 9498bcb002..2d6b1247c6 100644 --- a/modules/axhal/src/irq.rs +++ b/modules/axhal/src/irq.rs @@ -7,6 +7,9 @@ use crate::trap::{IRQ, register_trap_handler}; pub use crate::platform::irq::{register_handler, set_enable}; +#[cfg(target_arch = "aarch64")] +pub use crate::platform::irq::fetch_irq; + /// The type if an IRQ handler. pub type IrqHandler = handler_table::Handler; @@ -35,8 +38,13 @@ pub(crate) fn register_handler_common(irq_num: usize, handler: IrqHandler) -> bo false } +/// Core IRQ handling routine, registered at `axhal::trap::IRQ`, +/// which dispatches IRQs to registered handlers. +/// +/// Note: this function is denoted as public here because it'll be called by the +/// hypervisor for hypervisor reserved IRQ handling. #[register_trap_handler(IRQ)] -fn handler_irq(irq_num: usize) -> bool { +pub fn handler_irq(irq_num: usize) -> bool { let guard = kernel_guard::NoPreempt::new(); dispatch_irq(irq_num); drop(guard); // rescheduling may occur when preemption is re-enabled. diff --git a/modules/axhal/src/lib.rs b/modules/axhal/src/lib.rs index 87d61b0747..aeec8fa4f2 100644 --- a/modules/axhal/src/lib.rs +++ b/modules/axhal/src/lib.rs @@ -28,6 +28,7 @@ #![feature(naked_functions)] #![feature(doc_auto_cfg)] #![feature(sync_unsafe_cell)] +#![feature(const_trait_impl)] #[allow(unused_imports)] #[macro_use] @@ -76,3 +77,9 @@ pub use self::platform::platform_init; #[cfg(feature = "smp")] pub use self::platform::platform_init_secondary; + +pub use self::platform::config as hvconfig; +pub use self::platform::context::get_linux_context_list; +pub use self::platform::header as hvheader; +pub use self::platform::mem::host_memory_regions; +pub use self::platform::mp::shutdown_secondary_cpus; diff --git a/modules/axhal/src/paging.rs b/modules/axhal/src/paging.rs index 3d390304dc..ec3e02cb5e 100644 --- a/modules/axhal/src/paging.rs +++ b/modules/axhal/src/paging.rs @@ -42,10 +42,21 @@ impl PagingHandler for PagingHandlerImpl { .ok() } + fn alloc_frames(count: usize, align_pow2: usize) -> Option { + global_allocator() + .alloc_pages(count, align_pow2) + .map(|vaddr| virt_to_phys(vaddr.into())) + .ok() + } + fn dealloc_frame(paddr: PhysAddr) { global_allocator().dealloc_pages(phys_to_virt(paddr).as_usize(), 1) } + fn dealloc_frames(paddr: PhysAddr, count: usize) { + global_allocator().dealloc_pages(phys_to_virt(paddr).as_usize(), count) + } + #[inline] fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { phys_to_virt(paddr) diff --git a/modules/axhal/src/platform/aarch64_bsta1000b/mem.rs b/modules/axhal/src/platform/aarch64_bsta1000b/mem.rs index 63b0349b91..f122e28f77 100644 --- a/modules/axhal/src/platform/aarch64_bsta1000b/mem.rs +++ b/modules/axhal/src/platform/aarch64_bsta1000b/mem.rs @@ -1,9 +1,21 @@ -use crate::mem::MemRegion; +use crate::mem::{MemRegion, MemRegionFlags}; use page_table_entry::{GenericPTE, MappingFlags, aarch64::A64PTE}; +/// Returns (a1000b only) memory regions. +pub(crate) fn default_a1000b_regions() -> impl Iterator { + [MemRegion { + paddr: pa!(0x80000000), + size: 0x70000000, + flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE, + name: "reserved memory", + }] + .into_iter() +} /// Returns platform-specific memory regions. pub(crate) fn platform_regions() -> impl Iterator { - crate::mem::default_free_regions().chain(crate::mem::default_mmio_regions()) + crate::mem::default_free_regions() + .chain(default_a1000b_regions()) + .chain(crate::mem::default_mmio_regions()) } pub(crate) unsafe fn init_boot_page_table( @@ -32,4 +44,19 @@ pub(crate) unsafe fn init_boot_page_table( MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, true, ); + boot_pt_l1[3] = A64PTE::new_page( + pa!(0xc0000000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[6] = A64PTE::new_page( + pa!(0x180000000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[7] = A64PTE::new_page( + pa!(0x1C0000000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); } diff --git a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs index cc9cbed108..8f1a582580 100644 --- a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs +++ b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs @@ -19,6 +19,9 @@ pub mod time { pub use crate::platform::aarch64_common::generic_timer::*; } +use crate::mp::CPU_HWID; +use crate::mp::MAX_HARTS; + unsafe extern "C" { fn exception_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); @@ -36,7 +39,19 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { } #[cfg(feature = "smp")] -pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { +pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_hwid: usize) { + let mut cpu_id = cpu_hwid; + let mut map_success = false; + for index in 0..MAX_HARTS { + if cpu_id == CPU_HWID[index] { + cpu_id = index; + map_success = true; + break; + } + } + if !map_success { + panic!("CPU{} not found", cpu_id); + } crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); diff --git a/modules/axhal/src/platform/aarch64_common/boot_el2.rs b/modules/axhal/src/platform/aarch64_common/boot_el2.rs new file mode 100644 index 0000000000..931a00e991 --- /dev/null +++ b/modules/axhal/src/platform/aarch64_common/boot_el2.rs @@ -0,0 +1,213 @@ +use core::ptr::addr_of_mut; + +use aarch64_cpu::{asm, asm::barrier, registers::*}; +use memory_addr::PhysAddr; +use page_table_entry::aarch64::A64PTE; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; + +use axconfig::TASK_STACK_SIZE; + +#[unsafe(link_section = ".bss.stack")] +static mut BOOT_STACK: [u8; TASK_STACK_SIZE] = [0; TASK_STACK_SIZE]; + +#[unsafe(link_section = ".data.boot_page_table")] +static mut BOOT_PT_L0: [A64PTE; 512] = [A64PTE::empty(); 512]; + +#[unsafe(link_section = ".data.boot_page_table")] +static mut BOOT_PT_L1: [A64PTE; 512] = [A64PTE::empty(); 512]; + +unsafe fn switch_to_el2() { + SPSel.write(SPSel::SP::ELx); + let current_el = CurrentEL.read(CurrentEL::EL); + + if current_el == 3 { + SCR_EL3.write( + SCR_EL3::NS::NonSecure + SCR_EL3::HCE::HvcEnabled + SCR_EL3::RW::NextELIsAarch64, + ); + SPSR_EL3.write( + SPSR_EL3::M::EL2h + + SPSR_EL3::D::Masked + + SPSR_EL3::A::Masked + + SPSR_EL3::I::Masked + + SPSR_EL3::F::Masked, + ); + ELR_EL3.set(LR.get()); + asm::eret(); + } +} + +unsafe fn init_mmu_el2() { + // Set EL1 to 64bit. + // Enable `IMO` and `FMO` to make sure that: + // * Physical IRQ interrupts are taken to EL2; + // * Virtual IRQ interrupts are enabled; + // * Physical FIQ interrupts are taken to EL2; + // * Virtual FIQ interrupts are enabled. + HCR_EL2.modify( + HCR_EL2::VM::Enable + + HCR_EL2::RW::EL1IsAarch64 + + HCR_EL2::IMO::EnableVirtualIRQ // Physical IRQ Routing. + + HCR_EL2::FMO::EnableVirtualFIQ // Physical FIQ Routing. + + HCR_EL2::TSC::EnableTrapEl1SmcToEl2, + ); + + // Device-nGnRE memory + let attr0 = MAIR_EL2::Attr0_Device::nonGathering_nonReordering_EarlyWriteAck; + // Normal memory + let attr1 = MAIR_EL2::Attr1_Normal_Inner::WriteBack_NonTransient_ReadWriteAlloc + + MAIR_EL2::Attr1_Normal_Outer::WriteBack_NonTransient_ReadWriteAlloc; + MAIR_EL2.write(attr0 + attr1); // 0xff_04 + + // Enable TTBR0 and TTBR1 walks, page size = 4K, vaddr size = 48 bits, paddr size = 40 bits. + let tcr_flags0 = TCR_EL2::TG0::KiB_4 + + TCR_EL2::SH0::Inner + + TCR_EL2::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL2::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL2::T0SZ.val(16); + TCR_EL2.write(TCR_EL2::PS::Bits_40 + tcr_flags0); + barrier::isb(barrier::SY); + + let root_paddr = PhysAddr::from(&raw const BOOT_PT_L0 as usize).as_usize() as _; + TTBR0_EL2.set(root_paddr); + + // Flush the entire TLB + crate::arch::flush_tlb(None); + + // Enable the MMU and turn on I-cache and D-cache + SCTLR_EL2.modify(SCTLR_EL2::M::Enable + SCTLR_EL2::C::Cacheable + SCTLR_EL2::I::Cacheable); + barrier::isb(barrier::SY); +} + +unsafe fn enable_fp() { + if cfg!(feature = "fp_simd") { + CPACR_EL1.write(CPACR_EL1::FPEN::TrapNothing); + barrier::isb(barrier::SY); + } +} + +unsafe fn init_boot_page_table() { + crate::platform::mem::init_boot_page_table(addr_of_mut!(BOOT_PT_L0), addr_of_mut!(BOOT_PT_L1)); +} + +unsafe fn cache_invalidate(cache_level: usize) { + core::arch::asm!( + r#" + msr csselr_el1, {0} + mrs x4, ccsidr_el1 // read cache size id. + and x1, x4, #0x7 + add x1, x1, #0x4 // x1 = cache line size. + ldr x3, =0x7fff + and x2, x3, x4, lsr #13 // x2 = cache set number – 1. + ldr x3, =0x3ff + and x3, x3, x4, lsr #3 // x3 = cache associativity number – 1. + clz w4, w3 // x4 = way position in the cisw instruction. + mov x5, #0 // x5 = way counter way_loop. + // way_loop: + 1: + mov x6, #0 // x6 = set counter set_loop. + // set_loop: + 2: + lsl x7, x5, x4 + orr x7, {0}, x7 // set way. + lsl x8, x6, x1 + orr x7, x7, x8 // set set. + dc cisw, x7 // clean and invalidate cache line. + add x6, x6, #1 // increment set counter. + cmp x6, x2 // last set reached yet? + ble 2b // if not, iterate set_loop, + add x5, x5, #1 // else, next way. + cmp x5, x3 // last way reached yet? + ble 1b // if not, iterate way_loop + "#, + in(reg) cache_level, + options(nostack) + ); +} + +/// The earliest entry point for the primary CPU. +#[naked] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start() -> ! { + unsafe { + // PC = 0x8_0000 + // X0 = dtb + core::arch::naked_asm!(" + // save DTB pointer + mov x20, x0 + + // disable cache and MMU + mrs x1, sctlr_el2 + bic x1, x1, #0xf + msr sctlr_el2, x1 + + // cache_invalidate(0): clear dl1$ + mov x0, #0 + bl {cache_invalidate} + mov x0, #2 + bl {cache_invalidate} + + mrs x19, mpidr_el1 + and x19, x19, #0xffffff // get current CPU id + + adrp x8, {boot_stack} // setup boot stack + add x8, x8, {boot_stack_size} + mov sp, x8 + + bl {switch_to_el2} // switch to EL2 + bl {enable_fp} // enable fp/neon + bl {init_boot_page_table} + bl {init_mmu_el2} + + mov x8, {phys_virt_offset} // set SP to the high address + add sp, sp, x8 + + mov x0, x19 // call rust_entry(cpu_id, dtb) + mov x1, x20 + ldr x8, ={entry} + blr x8 + b .", + cache_invalidate = sym cache_invalidate, + init_boot_page_table = sym init_boot_page_table, + init_mmu_el2 = sym init_mmu_el2, + switch_to_el2 = sym switch_to_el2, + enable_fp = sym enable_fp, + boot_stack = sym BOOT_STACK, + boot_stack_size = const TASK_STACK_SIZE, + phys_virt_offset = const axconfig::plat::PHYS_VIRT_OFFSET, + entry = sym crate::platform::rust_entry, + ); + } +} + +/// The earliest entry point for the secondary CPUs. +#[cfg(feature = "smp")] +#[naked] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start_secondary() -> ! { + unsafe { + core::arch::naked_asm!(" + mrs x19, mpidr_el1 + and x19, x19, #0xffffff // get current CPU id + + mov sp, x0 + bl {switch_to_el2} + bl {init_mmu_el2} + bl {enable_fp} + + mov x8, {phys_virt_offset} // set SP to the high address + add sp, sp, x8 + + mov x0, x19 // call rust_entry_secondary(cpu_id) + ldr x8, ={entry} + blr x8 + b .", + switch_to_el2 = sym switch_to_el2, + init_mmu_el2 = sym init_mmu_el2, + enable_fp = sym enable_fp, + phys_virt_offset = const axconfig::plat::PHYS_VIRT_OFFSET, + entry = sym crate::platform::rust_entry_secondary, + ) + } +} diff --git a/modules/axhal/src/platform/aarch64_common/generic_timer.rs b/modules/axhal/src/platform/aarch64_common/generic_timer.rs index 8b5bcb97b6..127db32c39 100644 --- a/modules/axhal/src/platform/aarch64_common/generic_timer.rs +++ b/modules/axhal/src/platform/aarch64_common/generic_timer.rs @@ -34,8 +34,8 @@ pub fn epochoffset_nanos() -> u64 { /// Set a one-shot timer. /// -/// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds). -#[cfg(feature = "irq")] +/// A timer interrupt will be triggered at the given deadline (in nanoseconds). +#[cfg(all(feature = "irq", not(feature = "hv")))] pub fn set_oneshot_timer(deadline_ns: u64) { let cnptct = CNTPCT_EL0.get(); let cnptct_deadline = nanos_to_ticks(deadline_ns); @@ -48,6 +48,23 @@ pub fn set_oneshot_timer(deadline_ns: u64) { } } +#[cfg(all(feature = "irq", feature = "hv"))] +pub fn set_oneshot_timer(deadline_ns: u64) { + let cnptct = CNTPCT_EL0.get(); + let cnptct_deadline = nanos_to_ticks(deadline_ns); + if cnptct < cnptct_deadline { + let interval = cnptct_deadline - cnptct; + debug_assert!(interval <= u32::MAX as u64); + unsafe { + core::arch::asm!("msr CNTHP_TVAL_EL2, {}", in(reg) interval); + } + } else { + unsafe { + core::arch::asm!("msr CNTHP_TVAL_EL2, {0:x}", in(reg) 0); + } + } +} + /// Early stage initialization: stores the timer frequency. pub(crate) fn init_early() { let freq = CNTFRQ_EL0.get(); @@ -77,10 +94,21 @@ pub(crate) fn init_early() { } pub(crate) fn init_percpu() { - #[cfg(feature = "irq")] + #[cfg(all(feature = "irq", not(feature = "hv")))] { CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET); CNTP_TVAL_EL0.set(0); - crate::platform::irq::set_enable(crate::platform::irq::TIMER_IRQ_NUM, true); } + #[cfg(all(feature = "irq", feature = "hv"))] + { + unsafe { + // ENABLE, bit [0], Enables the timer. + // * 0b0: Timer disabled. + // * 0b1: Timer enabled. + core::arch::asm!("msr CNTHP_CTL_EL2, {0:x}", in(reg) 0b1); + core::arch::asm!("msr CNTHP_TVAL_EL2, {0:x}", in(reg) 0); + } + } + #[cfg(feature = "irq")] + crate::platform::irq::set_enable(crate::platform::irq::TIMER_IRQ_NUM, true); } diff --git a/modules/axhal/src/platform/aarch64_common/gic.rs b/modules/axhal/src/platform/aarch64_common/gic.rs index f8b21c9113..dad3172cd3 100644 --- a/modules/axhal/src/platform/aarch64_common/gic.rs +++ b/modules/axhal/src/platform/aarch64_common/gic.rs @@ -7,9 +7,14 @@ use memory_addr::PhysAddr; /// The maximum number of IRQs. pub const MAX_IRQ_COUNT: usize = 1024; +#[cfg(not(feature = "hv"))] /// The timer IRQ number. pub const TIMER_IRQ_NUM: usize = translate_irq(14, InterruptType::PPI).unwrap(); +#[cfg(feature = "hv")] +/// Non-secure EL2 Physical Timer irq number. +pub const TIMER_IRQ_NUM: usize = translate_irq(10, InterruptType::PPI).unwrap(); + /// The UART IRQ number. pub const UART_IRQ_NUM: usize = translate_irq(UART_IRQ, InterruptType::SPI).unwrap(); @@ -37,13 +42,24 @@ pub fn register_handler(irq_num: usize, handler: IrqHandler) -> bool { crate::irq::register_handler_common(irq_num, handler) } +/// Fetches the IRQ number. +pub fn fetch_irq() -> usize { + GICC.iar() as usize +} + /// Dispatches the IRQ. /// /// This function is called by the common interrupt handler. It looks /// up in the IRQ handler table and calls the corresponding handler. If /// necessary, it also acknowledges the interrupt controller after handling. -pub fn dispatch_irq(_unused: usize) { - GICC.handle_irq(|irq_num| crate::irq::dispatch_irq_common(irq_num as _)); +pub fn dispatch_irq(irq_no: usize) { + if irq_no == 0 { + GICC.handle_irq(|irq_num| crate::irq::dispatch_irq_common(irq_num as _)); + } else { + crate::irq::dispatch_irq_common(irq_no as _); + GICC.eoi(irq_no as _); + GICC.dir(irq_no as _); + } } /// Initializes GICD, GICC on the primary CPU. diff --git a/modules/axhal/src/platform/aarch64_common/mod.rs b/modules/axhal/src/platform/aarch64_common/mod.rs index c585541fe8..8a2a450930 100644 --- a/modules/axhal/src/platform/aarch64_common/mod.rs +++ b/modules/axhal/src/platform/aarch64_common/mod.rs @@ -1,4 +1,8 @@ +#[cfg(not(feature = "hv"))] mod boot; +#[cfg(feature = "hv")] +// Todo: maybe we can enter el2 in arm_vcpu? +mod boot_el2; pub mod generic_timer; #[cfg(not(platform_family = "aarch64-raspi"))] @@ -7,5 +11,8 @@ pub mod psci; #[cfg(feature = "irq")] pub mod gic; -#[cfg(not(platform_family = "aarch64-bsta1000b"))] +#[cfg(not(any( + platform_family = "aarch64-bsta1000b", + platform_family = "aarch64-rk3588j" +)))] pub mod pl011; diff --git a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs index 13a865384d..4bb16d8450 100644 --- a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs @@ -30,6 +30,7 @@ unsafe extern "C" { pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); crate::arch::set_exception_vector_base(exception_vector_base as usize); + #[cfg(not(feature = "hv"))] crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_primary(cpu_id); super::aarch64_common::pl011::init_early(); @@ -38,8 +39,10 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { } #[cfg(feature = "smp")] +#[allow(dead_code)] // FIXME: temporariy allowd to bypass clippy warnings. pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { crate::arch::set_exception_vector_base(exception_vector_base as usize); + #[cfg(not(feature = "hv"))] crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); diff --git a/modules/axhal/src/platform/aarch64_rk3588j/dw_apb_uart.rs b/modules/axhal/src/platform/aarch64_rk3588j/dw_apb_uart.rs new file mode 100644 index 0000000000..5d34bc9b0f --- /dev/null +++ b/modules/axhal/src/platform/aarch64_rk3588j/dw_apb_uart.rs @@ -0,0 +1,169 @@ +//! Definitions for snps,dw-apb-uart serial driver. +//! Uart snps,dw-apb-uart driver in Rust for BST A1000b FADA board. +use crate::mem::phys_to_virt; +use kspin::SpinNoIrq; +use memory_addr::PhysAddr; + +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +register_structs! { + DW8250Regs { + /// Get or Put Register. + (0x00 => rbr: ReadWrite), + (0x04 => ier: ReadWrite), + (0x08 => fcr: ReadWrite), + (0x0c => lcr: ReadWrite), + (0x10 => mcr: ReadWrite), + (0x14 => lsr: ReadOnly), + (0x18 => msr: ReadOnly), + (0x1c => scr: ReadWrite), + (0x20 => lpdll: ReadWrite), + (0x24 => _reserved0), + /// Uart Status Register. + (0x7c => usr: ReadOnly), + (0x80 => _reserved1), + (0xc0 => dlf: ReadWrite), + (0xc4 => @END), + } +} + +/// dw-apb-uart serial driver: DW8250 +pub struct DW8250 { + base_vaddr: usize, +} + +impl DW8250 { + /// New a DW8250 + pub const fn new(base_vaddr: usize) -> Self { + Self { base_vaddr } + } + + const fn regs(&self) -> &DW8250Regs { + unsafe { &*(self.base_vaddr as *const _) } + } + + /// DW8250 initialize + pub fn init(&mut self) { + const UART_SRC_CLK: u32 = 25000000; + const BST_UART_DLF_LEN: u32 = 6; + const BAUDRATE: u32 = 115200; + //const BAUDRATE: u32 = 38400; + + let get_baud_divider = |baudrate| (UART_SRC_CLK << (BST_UART_DLF_LEN - 4)) / baudrate; + let divider = get_baud_divider(BAUDRATE); + + // Waiting to be no USR_BUSY. + while self.regs().usr.get() & 0b1 != 0 {} + + // bst_serial_hw_init_clk_rst + + /* Disable interrupts and Enable FIFOs */ + self.regs().ier.set(0); + self.regs().fcr.set(1); + + /* Disable flow ctrl */ + self.regs().mcr.set(0); + + /* Clear MCR_RTS */ + self.regs().mcr.set(self.regs().mcr.get() | (1 << 1)); + + /* Enable access DLL & DLH. Set LCR_DLAB */ + self.regs().lcr.set(self.regs().lcr.get() | (1 << 7)); + + /* Set baud rate. Set DLL, DLH, DLF */ + self.regs().rbr.set((divider >> BST_UART_DLF_LEN) & 0xff); + self.regs() + .ier + .set((divider >> (BST_UART_DLF_LEN + 8)) & 0xff); + self.regs().dlf.set(divider & ((1 << BST_UART_DLF_LEN) - 1)); + + /* Clear DLAB bit */ + self.regs().lcr.set(self.regs().lcr.get() & !(1 << 7)); + + /* Set data length to 8 bit, 1 stop bit, no parity. Set LCR_WLS1 | LCR_WLS0 */ + self.regs().lcr.set(self.regs().lcr.get() | 0b11); + } + + /// DW8250 serial output + pub fn putchar(&mut self, c: u8) { + // Check LSR_TEMT + // Wait for last character to go. + while self.regs().lsr.get() & (1 << 6) == 0 {} + self.regs().rbr.set(c as u32); + } + + /// DW8250 serial input + pub fn getchar(&mut self) -> Option { + // Check LSR_DR + // Wait for a character to arrive. + if self.regs().lsr.get() & 0b1 != 0 { + Some((self.regs().rbr.get() & 0xff) as u8) + } else { + None + } + } + + /// DW8250 serial interrupt enable or disable + pub fn set_ier(&mut self, enable: bool) { + if enable { + // Enable interrupts + self.regs().ier.set(1); + } else { + // Disable interrupts + self.regs().ier.set(0); + } + } +} + +const UART_BASE: PhysAddr = pa!(axconfig::devices::UART_PADDR); + +pub static UART: SpinNoIrq = + SpinNoIrq::new(DW8250::new(phys_to_virt(UART_BASE).as_usize())); + +/// Write a slice of bytes to the console. +pub fn write_bytes(bytes: &[u8]) { + for c in bytes { + putchar(*c); + } +} + +/// Reads bytes from the console into the given mutable slice. +/// Returns the number of bytes read. +pub fn read_bytes(bytes: &mut [u8]) -> usize { + let mut read_len = 0; + while read_len < bytes.len() { + if let Some(c) = getchar() { + bytes[read_len] = c; + } else { + break; + } + read_len += 1; + } + read_len +} + +/// Writes a byte to the console. +pub fn putchar(c: u8) { + let mut uart = UART.lock(); + match c { + b'\r' | b'\n' => { + uart.putchar(b'\r'); + uart.putchar(b'\n'); + } + c => uart.putchar(c), + } +} + +/// Reads a byte from the console, or returns [`None`] if no input is available. +pub fn getchar() -> Option { + UART.lock().getchar() +} + +/// UART simply initialize +pub fn init_early() { + UART.lock().init(); +} diff --git a/modules/axhal/src/platform/aarch64_rk3588j/mem.rs b/modules/axhal/src/platform/aarch64_rk3588j/mem.rs new file mode 100644 index 0000000000..5c2fd2d609 --- /dev/null +++ b/modules/axhal/src/platform/aarch64_rk3588j/mem.rs @@ -0,0 +1,68 @@ +use crate::mem::{MemRegion, MemRegionFlags, PhysAddr}; +use page_table_entry::{GenericPTE, MappingFlags, aarch64::A64PTE}; + +/// Returns (rk3588j only) memory regions. +pub(crate) fn default_rk3588j_regions() -> impl Iterator { + [ + MemRegion { + paddr: PhysAddr::from(0x9400000), + size: 0xe6c00000, + flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE, + name: "reserved memory", + }, + MemRegion { + paddr: PhysAddr::from(0x1f0000000), + size: 0x10000000, + flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE, + name: "free memory", + }, + ] + .into_iter() +} + +/// Returns platform-specific memory regions. +pub(crate) fn platform_regions() -> impl Iterator { + crate::mem::default_free_regions() + .chain(default_rk3588j_regions()) + .chain(crate::mem::default_mmio_regions()) +} + +pub(crate) unsafe fn init_boot_page_table( + boot_pt_l0: *mut [A64PTE; 512], + boot_pt_l1: *mut [A64PTE; 512], +) { + let boot_pt_l0 = &mut *boot_pt_l0; + let boot_pt_l1 = &mut *boot_pt_l1; + boot_pt_l0[0] = A64PTE::new_table(PhysAddr::from(boot_pt_l1.as_ptr() as usize)); + + boot_pt_l1[0] = A64PTE::new_page( + PhysAddr::from(0), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[1] = A64PTE::new_page( + PhysAddr::from(0x4000_0000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[2] = A64PTE::new_page( + PhysAddr::from(0x8000_0000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[3] = A64PTE::new_page( + PhysAddr::from(0xC000_0000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::DEVICE, + true, + ); + boot_pt_l1[7] = A64PTE::new_page( + PhysAddr::from(0x1_C000_0000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); + boot_pt_l1[8] = A64PTE::new_page( + PhysAddr::from(0x1_F000_0000), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + true, + ); +} diff --git a/modules/axhal/src/platform/aarch64_rk3588j/mod.rs b/modules/axhal/src/platform/aarch64_rk3588j/mod.rs new file mode 100644 index 0000000000..873cde06d3 --- /dev/null +++ b/modules/axhal/src/platform/aarch64_rk3588j/mod.rs @@ -0,0 +1,66 @@ +pub mod dw_apb_uart; +pub mod mem; + +#[cfg(feature = "smp")] +pub mod mp; + +#[cfg(feature = "irq")] +pub mod irq { + pub use crate::platform::aarch64_common::gic::*; +} + +pub mod console { + pub use super::dw_apb_uart::*; +} + +pub mod time { + pub use crate::platform::aarch64_common::generic_timer::*; +} + +pub mod misc { + pub use crate::platform::aarch64_common::psci::system_off as terminate; +} + +unsafe extern "C" { + fn exception_vector_base(); + fn rust_main(cpu_id: usize, dtb: usize); + #[cfg(feature = "smp")] + fn rust_main_secondary(cpu_id: usize); +} + +pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { + crate::mem::clear_bss(); + crate::arch::set_exception_vector_base(exception_vector_base as usize); + #[cfg(not(feature = "hv"))] + crate::arch::write_page_table_root0(0.into()); // disable low address access + crate::cpu::init_primary(cpu_id); + super::dw_apb_uart::init_early(); + super::aarch64_common::generic_timer::init_early(); + rust_main(cpu_id, dtb); +} + +#[cfg(feature = "smp")] +#[allow(dead_code)] // FIXME: temporariy allowd to bypass clippy warnings. +pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { + crate::arch::set_exception_vector_base(exception_vector_base as usize); + crate::arch::write_page_table_root0(0.into()); // disable low address access + crate::cpu::init_secondary(cpu_id); + rust_main_secondary(cpu_id); +} + +/// Initializes the platform devices for the primary CPU. +/// +/// For example, the interrupt controller and the timer. +pub fn platform_init() { + #[cfg(feature = "irq")] + super::aarch64_common::gic::init_primary(); + super::aarch64_common::generic_timer::init_percpu(); +} + +/// Initializes the platform devices for secondary CPUs. +#[cfg(feature = "smp")] +pub fn platform_init_secondary() { + #[cfg(feature = "irq")] + super::aarch64_common::gic::init_secondary(); + super::aarch64_common::generic_timer::init_percpu(); +} diff --git a/modules/axhal/src/platform/aarch64_rk3588j/mp.rs b/modules/axhal/src/platform/aarch64_rk3588j/mp.rs new file mode 100644 index 0000000000..70d8cebecb --- /dev/null +++ b/modules/axhal/src/platform/aarch64_rk3588j/mp.rs @@ -0,0 +1,20 @@ +use crate::mem::{PhysAddr, VirtAddr, virt_to_phys}; + +/// Hart number of rk3588 board +pub const MAX_HARTS: usize = 8; +/// CPU HWID from cpu device tree nodes with "reg" property +pub const CPU_HWID: [usize; MAX_HARTS] = [0x00, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700]; + +/// Starts the given secondary CPU with its boot stack. +pub fn start_secondary_cpu(cpu_id: usize, stack_top: PhysAddr) { + assert!(cpu_id < MAX_HARTS, "No support for rk3588 core {}", cpu_id); + unsafe extern "C" { + fn _start_secondary(); + } + let entry = virt_to_phys(VirtAddr::from(_start_secondary as usize)); + crate::platform::aarch64_common::psci::cpu_on( + CPU_HWID[cpu_id], + entry.as_usize(), + stack_top.as_usize(), + ); +} diff --git a/modules/axhal/src/platform/dummy/mod.rs b/modules/axhal/src/platform/dummy/mod.rs index e0d229ff3a..616ca47e7d 100644 --- a/modules/axhal/src/platform/dummy/mod.rs +++ b/modules/axhal/src/platform/dummy/mod.rs @@ -83,6 +83,11 @@ pub mod irq { /// up in the IRQ handler table and calls the corresponding handler. If /// necessary, it also acknowledges the interrupt controller after handling. pub fn dispatch_irq(irq_num: usize) {} + + /// Fetches the IRQ number. + pub fn fetch_irq() -> usize { + 0 + } } /// Initializes the platform devices for the primary CPU. diff --git a/modules/axhal/src/platform/mod.rs b/modules/axhal/src/platform/mod.rs index 349aac9ae0..d6a652e715 100644 --- a/modules/axhal/src/platform/mod.rs +++ b/modules/axhal/src/platform/mod.rs @@ -25,7 +25,14 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_arch = "aarch64", platform_family = "aarch64-phytium-pi"))] { mod aarch64_phytium_pi; pub use self::aarch64_phytium_pi::*; - } else { + } else if #[cfg(all(target_arch = "aarch64", platform_family = "aarch64-rk3588j"))] { + mod aarch64_rk3588j; + pub use self::aarch64_rk3588j::*; + // } else if #[cfg(all(target_arch = "x86_64", platform_family = "x86-linux"))] { + } else if #[cfg(all(target_arch = "x86_64"))] { + mod x86_linux; + pub use self::x86_linux::*; + }else { mod dummy; pub use self::dummy::*; } diff --git a/modules/axhal/src/platform/x86_linux/ap_start.S b/modules/axhal/src/platform/x86_linux/ap_start.S new file mode 100644 index 0000000000..0b53a6cd6e --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/ap_start.S @@ -0,0 +1,70 @@ +# Boot application processors into the protected mode. + +# Each non-boot CPU ("AP") is started up in response to a STARTUP +# IPI from the boot CPU. Section B.4.2 of the Multi-Processor +# Specification says that the AP will start in real mode with CS:IP +# set to XY00:0000, where XY is an 8-bit value sent with the +# STARTUP. Thus this code must start at a 4096-byte boundary. +# +# Because this code sets DS to zero, it must sit +# at an address in the low 2^16 bytes. + +.equ pa_ap_start32, ap_start32 - ap_start + {start_page_paddr} +.equ pa_ap_gdt, .Lap_tmp_gdt - ap_start + {start_page_paddr} +.equ pa_ap_gdt_desc, .Lap_tmp_gdt_desc - ap_start + {start_page_paddr} + +.equ stack_ptr, {start_page_paddr} + 0xff0 +.equ entry_ptr, {start_page_paddr} + 0xff8 + +.global ap_start +.global ap_end + +# 0x6000 +.section .text +.code16 +.p2align 12 +ap_start: + cli + wbinvd + + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + # load the 64-bit GDT + lgdt [pa_ap_gdt_desc] + + # switch to protected-mode + mov eax, cr0 + or eax, (1 << 0) + mov cr0, eax + + # far jump to 32-bit code. 0x8 is code32 segment selector + ljmp 0x8, offset pa_ap_start32 + +.code32 +ap_start32: + mov esp, [stack_ptr] + mov eax, [entry_ptr] + jmp eax + +.balign 8 +# .type multiboot_header, STT_OBJECT +.Lap_tmp_gdt_desc: + .short .Lap_tmp_gdt_end - .Lap_tmp_gdt - 1 # limit + .long pa_ap_gdt # base + +.balign 16 +.Lap_tmp_gdt: + .quad 0x0000000000000000 # 0x00: null + .quad 0x00cf9b000000ffff # 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) + .quad 0x00af9b000000ffff # 0x10: code segment (base=0, limit=0xfffff, type=64bit code exec/read, DPL=0, 4k) + .quad 0x00cf93000000ffff # 0x18: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) +.Lap_tmp_gdt_end: + +# 0x7000 +.p2align 12 +ap_end: diff --git a/modules/axhal/src/platform/x86_linux/apic.rs b/modules/axhal/src/platform/x86_linux/apic.rs new file mode 100644 index 0000000000..e7efca59d6 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/apic.rs @@ -0,0 +1,189 @@ +#![allow(dead_code)] + +use core::{cell::SyncUnsafeCell, mem::MaybeUninit}; + +use kspin::SpinNoIrq; +use lazyinit::LazyInit; +use memory_addr::PhysAddr; +use x2apic::ioapic::IoApic; +use x2apic::lapic::{LocalApic, LocalApicBuilder, xapic_base}; +use x86_64::instructions::port::Port; + +use self::vectors::*; +use crate::mem::phys_to_virt; + +pub(super) mod vectors { + pub const APIC_TIMER_VECTOR: u8 = 0xf0; + pub const APIC_SPURIOUS_VECTOR: u8 = 0xf1; + pub const APIC_ERROR_VECTOR: u8 = 0xf2; +} + +/// The maximum number of IRQs. +pub const MAX_IRQ_COUNT: usize = 256; + +/// The timer IRQ number. +pub const TIMER_IRQ_NUM: usize = APIC_TIMER_VECTOR as usize; + +const IO_APIC_BASE: PhysAddr = pa!(0xFEC0_0000); + +static LOCAL_APIC: SyncUnsafeCell> = + SyncUnsafeCell::new(MaybeUninit::uninit()); +static mut IS_X2APIC: bool = false; +static IO_APIC: LazyInit> = LazyInit::new(); + +const MAX_APIC_ID: u32 = 254; +static mut APIC_TO_CPU_ID: [u32; MAX_APIC_ID as usize + 1] = [u32::MAX; MAX_APIC_ID as usize + 1]; +static mut APIC_ID_IS_RESERVED: [bool; MAX_APIC_ID as usize + 1] = + [false; MAX_APIC_ID as usize + 1]; + +/// Enables or disables the given IRQ. +#[cfg(feature = "irq")] +pub fn set_enable(vector: usize, enabled: bool) { + // should not affect LAPIC interrupts + if vector < APIC_TIMER_VECTOR as _ { + unsafe { + if enabled { + IO_APIC.lock().enable_irq(vector as u8); + } else { + IO_APIC.lock().disable_irq(vector as u8); + } + } + } +} + +/// Registers an IRQ handler for the given IRQ. +/// +/// It also enables the IRQ if the registration succeeds. It returns `false` if +/// the registration failed. +#[cfg(feature = "irq")] +pub fn register_handler(vector: usize, handler: crate::irq::IrqHandler) -> bool { + crate::irq::register_handler_common(vector, handler) +} + +/// Dispatches the IRQ. +/// +/// This function is called by the common interrupt handler. It looks +/// up in the IRQ handler table and calls the corresponding handler. If +/// necessary, it also acknowledges the interrupt controller after handling. +#[cfg(feature = "irq")] +pub fn dispatch_irq(vector: usize) { + crate::irq::dispatch_irq_common(vector); + unsafe { local_apic().end_of_interrupt() }; +} + +pub(super) fn local_apic<'a>() -> &'a mut LocalApic { + // It's safe as `LOCAL_APIC` is initialized in `init_primary`. + unsafe { LOCAL_APIC.get().as_mut().unwrap().assume_init_mut() } +} + +pub(super) fn raw_apic_id(id_u8: u8) -> u32 { + if unsafe { IS_X2APIC } { + id_u8 as u32 + } else { + (id_u8 as u32) << 24 + } +} + +fn cpu_has_x2apic() -> bool { + match raw_cpuid::CpuId::new().get_feature_info() { + Some(finfo) => finfo.has_x2apic(), + None => false, + } +} + +pub(super) fn init_primary(enabled: bool, cpu_id: usize) { + info!("Initialize Local APIC..."); + + if enabled { + unsafe { + // Disable 8259A interrupt controllers + Port::::new(0x21).write(0xff); + Port::::new(0xA1).write(0xff); + } + } + + let mut builder = LocalApicBuilder::new(); + builder + .timer_vector(APIC_TIMER_VECTOR as _) + .error_vector(APIC_ERROR_VECTOR as _) + .spurious_vector(APIC_SPURIOUS_VECTOR as _); + + if cpu_has_x2apic() { + info!("Using x2APIC."); + unsafe { IS_X2APIC = true }; + } else { + info!("Using xAPIC."); + let base_vaddr = phys_to_virt(PhysAddr::from(unsafe { xapic_base() } as usize)); + builder.set_xapic_base(base_vaddr.as_usize() as u64); + } + + let mut lapic = builder.build().unwrap(); + unsafe { + if enabled { + lapic.enable(); + } + + let apic_id = lapic.id(); + APIC_TO_CPU_ID[apic_id as usize] = cpu_id as u32; + if crate::cpu::this_cpu_is_reserved() { + APIC_ID_IS_RESERVED[apic_id as usize] = true; + } + + LOCAL_APIC.get().as_mut().unwrap().write(lapic); + } + + // info!("Initialize IO APIC..."); + // let io_apic = unsafe { IoApic::new(phys_to_virt(IO_APIC_BASE).as_usize() as u64) }; + // IO_APIC.init_once(SpinNoIrq::new(io_apic)); +} + +#[cfg(feature = "smp")] +pub(super) fn init_secondary(enabled: bool, cpu_id: usize) { + let lapic = local_apic(); + + if enabled { + unsafe { + lapic.enable(); + } + } + unsafe { + let apic_id = lapic.id(); + APIC_TO_CPU_ID[apic_id as usize] = cpu_id as u32; + if crate::cpu::this_cpu_is_reserved() { + APIC_ID_IS_RESERVED[apic_id as usize] = true; + } + }; +} + +/// Returns if the given APIC ID is reserved. +/// The APIC ID is reserved if it entered Linux, which has set the corresponding +/// entry in `APIC_TO_CPU_ID` to 0. +pub(super) fn apic_id_is_reserved(apic_id: usize) -> bool { + unsafe { APIC_ID_IS_RESERVED[apic_id] } +} + +pub(super) fn apic_to_cpu_id(apic_id: u32) -> u32 { + if apic_id <= MAX_APIC_ID { + unsafe { APIC_TO_CPU_ID[apic_id as usize] } + } else { + u32::MAX + } +} + +pub(super) fn cpu_id_to_apic_id(cpu_id: usize) -> Option { + for (apic_id, id) in unsafe { APIC_TO_CPU_ID.iter().enumerate() } { + if *id == cpu_id as u32 { + return Some(apic_id as u32); + } + } + None +} + +/// Shuts down the target CPU by sending an INIT IPI to it. +pub(super) fn shutdown_ap(apic_id: u32) { + info!("Shutting down ArceOS cpu {apic_id}..."); + let apic_id = raw_apic_id(apic_id as u8); + unsafe { + local_apic().send_init_ipi(apic_id); + } +} diff --git a/modules/axhal/src/platform/x86_linux/boot.rs b/modules/axhal/src/platform/x86_linux/boot.rs new file mode 100644 index 0000000000..59efca0473 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/boot.rs @@ -0,0 +1,49 @@ +use core::arch::global_asm; + +use x86_64::registers::control::{Cr0Flags, Cr4Flags}; +use x86_64::registers::model_specific::EferFlags; + +use axconfig::plat::PHYS_VIRT_OFFSET; + +/// Flags set in the ’flags’ member of the multiboot header. +/// +/// (bits 1, 16: memory information, address fields in header) +const MULTIBOOT_HEADER_FLAGS: usize = 0x0001_0002; + +/// The magic field should contain this. +const MULTIBOOT_HEADER_MAGIC: usize = 0x1BADB002; + +/// This should be in EAX. +pub(super) const MULTIBOOT_BOOTLOADER_MAGIC: usize = 0x2BADB002; + +const CR0: u64 = Cr0Flags::PROTECTED_MODE_ENABLE.bits() + | Cr0Flags::MONITOR_COPROCESSOR.bits() + | Cr0Flags::NUMERIC_ERROR.bits() + | Cr0Flags::WRITE_PROTECT.bits() + | Cr0Flags::PAGING.bits(); +const CR4: u64 = Cr4Flags::PHYSICAL_ADDRESS_EXTENSION.bits() + | Cr4Flags::PAGE_GLOBAL.bits() + | if cfg!(feature = "fp_simd") { + Cr4Flags::OSFXSR.bits() | Cr4Flags::OSXMMEXCPT_ENABLE.bits() + } else { + 0 + }; +const EFER: u64 = EferFlags::LONG_MODE_ENABLE.bits() | EferFlags::NO_EXECUTE_ENABLE.bits(); + +global_asm!( + include_str!("multiboot.S"), + mb_magic = const MULTIBOOT_BOOTLOADER_MAGIC, + mb_hdr_magic = const MULTIBOOT_HEADER_MAGIC, + mb_hdr_flags = const MULTIBOOT_HEADER_FLAGS, + // entry = sym super::rust_entry, + entry_secondary = sym super::rust_entry_secondary, + + offset = const PHYS_VIRT_OFFSET, + // boot_stack_size = const TASK_STACK_SIZE, + // boot_stack = sym BOOT_STACK, + + cr0 = const CR0, + cr4 = const CR4, + efer_msr = const x86::msr::IA32_EFER, + efer = const EFER, +); diff --git a/modules/axhal/src/platform/x86_linux/config.rs b/modules/axhal/src/platform/x86_linux/config.rs new file mode 100644 index 0000000000..7f9e716508 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/config.rs @@ -0,0 +1,174 @@ +//! Cell configuration structures inherited from Jailhouse. + +use core::fmt::{Debug, Formatter, Result}; +use core::{mem::size_of, slice}; + +use bitflags::bitflags; + +use crate::mem::MemRegionFlags; + +const CONFIG_SIGNATURE: [u8; 6] = *b"EVMSYS"; +const CONFIG_REVISION: u16 = 314; + +const HV_CELL_NAME_MAXLEN: usize = 31; + +/// The jailhouse cell configuration. +/// +/// @note Keep Config._HEADER_FORMAT in jailhouse-cell-linux in sync with this +/// structure. +#[derive(Debug)] +#[repr(C, packed)] +pub struct HvCellDesc { + signature: [u8; 6], + revision: u16, + name: [u8; HV_CELL_NAME_MAXLEN + 1], + id: u32, // set by the driver + num_memory_regions: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct HvMemoryRegion { + pub phys_start: u64, + pub virt_start: u64, + pub size: u64, + pub flags: MemFlags, +} + +bitflags! { + #[derive(Debug, Clone)] + pub struct MemFlags: u64 { + const READ = 1 << 0; + const WRITE = 1 << 1; + const EXECUTE = 1 << 2; + const DMA = 1 << 3; + const IO = 1 << 4; + const NO_HUGEPAGES = 1 << 8; + const USER = 1 << 9; + } +} + +impl Into for MemFlags { + fn into(self) -> MemRegionFlags { + let mut flags = MemRegionFlags::empty(); + if self.contains(MemFlags::READ) { + flags |= MemRegionFlags::READ; + } + if self.contains(MemFlags::WRITE) { + flags |= MemRegionFlags::WRITE; + } + if self.contains(MemFlags::EXECUTE) { + flags |= MemRegionFlags::EXECUTE; + } + if self.contains(MemFlags::DMA) { + flags |= MemRegionFlags::DEVICE; + } + if self.contains(MemFlags::IO) { + flags |= MemRegionFlags::DEVICE; + } + flags + } +} + +/// General descriptor of the system. +/// +/// See jailhouse dir `driver/cell-config.h` for details. +#[derive(Debug)] +#[repr(C)] +pub struct HvSystemConfig { + pub signature: [u8; 6], + pub revision: u16, + /// AxVisor location in memory + pub hypervisor_memory: HvMemoryRegion, + pub root_cell: HvCellDesc, + // CellConfigLayout placed here. +} + +// /// A dummy layout with all variant-size fields empty. +// #[derive(Debug)] +// #[repr(C, packed)] +// struct CellConfigLayout { +// mem_regions: [HvMemoryRegion; 0], +// } + +pub struct CellConfig<'a> { + desc: &'a HvCellDesc, +} + +impl HvCellDesc { + pub const fn config(&self) -> CellConfig { + CellConfig::from(self) + } + + pub const fn config_size(&self) -> usize { + self.num_memory_regions as usize * size_of::() + } +} + +impl HvSystemConfig { + pub fn get<'a>() -> &'a Self { + unsafe { &*super::consts::hv_config_ptr() } + } + + pub const fn size(&self) -> usize { + size_of::() + self.root_cell.config_size() + } + + pub fn check(&self) { + assert_eq!(self.signature, CONFIG_SIGNATURE); + assert_eq!(self.revision, CONFIG_REVISION); + } +} + +impl<'a> CellConfig<'a> { + const fn from(desc: &'a HvCellDesc) -> Self { + Self { desc } + } + + fn config_ptr(&self) -> *const T { + unsafe { (self.desc as *const HvCellDesc).add(1) as _ } + } + + pub const fn size(&self) -> usize { + self.desc.config_size() + } + + pub fn mem_regions(&self) -> &'static [HvMemoryRegion] { + // XXX: data may unaligned, which cause panic on debug mode. Same below. + // See: https://doc.rust-lang.org/src/core/slice/mod.rs.html#6435-6443 + unsafe { + let ptr = self.config_ptr() as _; + slice::from_raw_parts(ptr, self.desc.num_memory_regions as usize) + } + } +} + +impl Debug for CellConfig<'_> { + fn fmt(&self, f: &mut Formatter) -> Result { + let name = self.desc.name; + let mut len = 0; + while name[len] != 0 { + len += 1; + } + f.debug_struct("CellConfig") + .field("name", &core::str::from_utf8(&name[..len])) + .field("size", &self.size()) + .field("mem regions", &self.mem_regions()) + .finish() + } +} + +pub fn cpu_is_reserved(cpu_id: usize) -> bool { + use super::apic::{apic_id_is_reserved, cpu_id_to_apic_id}; + if cpu_id >= axconfig::SMP { + warn!("cpu_id {} is out of range", cpu_id); + return false; + } + + if let Some(apic_id) = cpu_id_to_apic_id(cpu_id) { + return apic_id_is_reserved(apic_id as usize); + } else { + warn!("cpu_id {} is not mapped to apic id", cpu_id); + return false; + } +} diff --git a/modules/axhal/src/platform/x86_linux/consts.rs b/modules/axhal/src/platform/x86_linux/consts.rs new file mode 100644 index 0000000000..e6d4c9aab3 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/consts.rs @@ -0,0 +1,36 @@ +use memory_addr::{MemoryAddr, is_aligned_4k}; + +use super::config::HvSystemConfig; +use super::header::HvHeader; +use crate::mem::VirtAddr; +use axconfig::plat::KERNEL_BASE_VADDR; + +/// Pointer of the `HvHeader` structure. +pub const HV_HEADER_PTR: *const HvHeader = __header_start as _; + +/// Pointer of the `HvSystemConfig` structure. +pub fn hv_config_ptr() -> *const HvSystemConfig { + _ekernel as _ +} + +pub fn cfg_region_start() -> VirtAddr { + let cfg_ptr = hv_config_ptr() as usize; + // The linker script ensures that `ekernel` is aligned to 4KB. + assert!(is_aligned_4k(cfg_ptr), "_ekernel is not aligned to 4KB"); + VirtAddr::from(cfg_ptr) +} + +/// Pointer of the free memory pool. +pub fn free_memory_start() -> VirtAddr { + VirtAddr::from(hv_config_ptr() as usize + HvSystemConfig::get().size()).align_up_4k() +} + +/// End virtual address of the hypervisor memory. +pub fn hv_end() -> VirtAddr { + VirtAddr::from(KERNEL_BASE_VADDR + HvSystemConfig::get().hypervisor_memory.size as usize) +} + +unsafe extern "C" { + unsafe fn __header_start(); + unsafe fn _ekernel(); +} diff --git a/modules/axhal/src/platform/x86_linux/context.rs b/modules/axhal/src/platform/x86_linux/context.rs new file mode 100644 index 0000000000..8be903d43f --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/context.rs @@ -0,0 +1,26 @@ +use core::mem::MaybeUninit; + +use lazyinit::LazyInit; + +use x86_vcpu::LinuxContext; + +use axconfig::SMP; + +static mut LINUX_CTX_LIST: LazyInit<[MaybeUninit; SMP]> = LazyInit::new(); + +/// Set Linux context for current CPU. +pub fn set_linux_context(linux_sp: usize, cpu_id: usize) { + unsafe { + if LINUX_CTX_LIST.is_inited() { + LINUX_CTX_LIST[cpu_id].write(LinuxContext::load_from(linux_sp)); + } else { + let mut list = [const { MaybeUninit::uninit() }; SMP]; + list[cpu_id].write(LinuxContext::load_from(linux_sp)); + LINUX_CTX_LIST.init_once(list); + } + } +} + +pub fn get_linux_context_list() -> &'static [LinuxContext; SMP] { + unsafe { &*(&raw const LINUX_CTX_LIST as *const [LinuxContext; SMP]) } +} diff --git a/modules/axhal/src/platform/x86_linux/dtables.rs b/modules/axhal/src/platform/x86_linux/dtables.rs new file mode 100644 index 0000000000..0d3415130c --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/dtables.rs @@ -0,0 +1,38 @@ +//! Description tables (per-CPU GDT, per-CPU ISS, IDT) + +use lazyinit::LazyInit; + +use crate::arch::{GdtStruct, IdtStruct, TaskStateSegment}; + +static IDT: LazyInit = LazyInit::new(); + +#[percpu::def_percpu] +static TSS: LazyInit = LazyInit::new(); + +#[percpu::def_percpu] +static GDT: LazyInit = LazyInit::new(); + +fn init_percpu() { + unsafe { + IDT.load(); + let tss = TSS.current_ref_mut_raw(); + let gdt = GDT.current_ref_mut_raw(); + tss.init_once(TaskStateSegment::new()); + gdt.init_once(GdtStruct::new(tss)); + gdt.load(); + gdt.load_tss(); + } +} + +/// Initializes IDT, GDT on the primary CPU. +pub(super) fn init_primary() { + axlog::ax_println!("\nInitialize IDT & GDT..."); + IDT.init_once(IdtStruct::new()); + init_percpu(); +} + +/// Initializes IDT, GDT on secondary CPUs. +#[cfg(feature = "smp")] +pub(super) fn init_secondary() { + init_percpu(); +} diff --git a/modules/axhal/src/platform/x86_linux/entry.rs b/modules/axhal/src/platform/x86_linux/entry.rs new file mode 100644 index 0000000000..856a121693 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/entry.rs @@ -0,0 +1,105 @@ +use core::sync::atomic::{AtomicU32, Ordering}; + +use super::header::HvHeader; +use axconfig::{SMP, TASK_STACK_SIZE}; + +static ENTERED_CPUS: AtomicU32 = AtomicU32::new(0); + +#[unsafe(link_section = ".bss.stack")] +static mut VMM_BOOT_STACK: [[u8; TASK_STACK_SIZE]; SMP] = [[0; TASK_STACK_SIZE]; SMP]; + +pub fn entered_cpus() -> u32 { + ENTERED_CPUS.load(Ordering::Acquire) +} + +#[unsafe(link_section = ".text.boot")] +unsafe extern "sysv64" fn switch_stack(linux_sp: usize) -> i32 { + unsafe { + let linux_cr3 = x86::controlregs::cr3(); + let linux_tp = x86::msr::rdmsr(x86::msr::IA32_GS_BASE); + + let vmm_entry = |linux_sp: usize| -> i32 { + if entered_cpus() >= HvHeader::get().max_cpus { + panic!( + "enter cpus exceed Linux max cpus {}", + HvHeader::get().max_cpus + ); + } + if entered_cpus() >= SMP as u32 { + panic!("enter cpus exceed configured SMP {}", SMP); + } + + // Note: cpu_id here is not Local APIC ID, it is the index of entered CPUs. + // We just use it here to choose VMM_BOOT_STACK. + let core_id = ENTERED_CPUS.fetch_add(1, Ordering::SeqCst); + + let hv_sp = VMM_BOOT_STACK[core_id as usize].as_ptr_range().end as usize; + let ret; + core::arch::asm!(" + mov [rsi], {linux_tp} // save gs_base to stack + mov rcx, rsp + mov rsp, {hv_sp} + push rcx + call {entry} + pop rsp", + entry = sym super::vmm_cpu_entry, + linux_tp = in(reg) linux_tp, + hv_sp = in(reg) hv_sp, + in("rdi") core_id, + in("rsi") linux_sp, + lateout("rax") ret, + out("rcx") _, + clobber_abi("sysv64"), + ); + ret + }; + + let ret = vmm_entry(linux_sp); + + axlog::ax_println!( + "CPU {} return back to driver with code {}.", + ret, + super::current_cpu_id() + ); + + x86::msr::wrmsr(x86::msr::IA32_GS_BASE, linux_tp); + x86::controlregs::cr3_write(linux_cr3); + ret + } +} + +/// Cores entered from Linux will call this function. +/// This function will switch to VMM stack and call `super::vmm_cpu_entry`. +#[naked] +#[unsafe(link_section = ".text.boot")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _start() -> i32 { + unsafe { + core::arch::naked_asm!(" + .code64 + // rip is pushed + cli + push rbp + push rbx + push r12 + push r13 + push r14 + push r15 + push 0 // skip gs_base + + mov rdi, rsp + call {0} + + pop r15 // skip gs_base + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + pop rbp + ret + // rip will pop when return", + sym switch_stack, + ); + } +} diff --git a/modules/axhal/src/platform/x86_linux/header.rs b/modules/axhal/src/platform/x86_linux/header.rs new file mode 100644 index 0000000000..56b9e25286 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/header.rs @@ -0,0 +1,83 @@ +use core::fmt::{Debug, Formatter, Result}; + +use super::consts::HV_HEADER_PTR; + +const HEADER_SIGNATURE: [u8; 8] = *b"EVMIMAGE"; + +/// Hypervisor description. +/// Located at the beginning of the hypervisor binary image and loaded by +/// the driver (which also initializes some fields). +/// See jailhouse dir `driver/jailhouse.h` for details. +#[repr(C)] +pub struct HvHeader { + pub signature: [u8; 8], + pub core_size: usize, + /// Not used, always 0. + pub percpu_size: usize, + pub entry: usize, + /// Available CPU numbers provided by current physical platform. + pub max_cpus: u32, + /// CPU numbers reserved for ArceOS. + /// The Rest of Available CPUs will be reserved for host Linux. + pub arceos_cpus: u32, +} + +impl HvHeader { + pub fn get<'a>() -> &'a Self { + unsafe { &*HV_HEADER_PTR } + } + + /// Get the number of CPUs reserved for Linux. + pub fn reserved_cpus(&self) -> u32 { + if self.arceos_cpus < self.max_cpus { + self.max_cpus - self.arceos_cpus + } else { + warn!( + "Invalid HvHeader: arceos_cpus ({}) >= max_cpus ({})", + self.arceos_cpus, self.max_cpus + ); + self.max_cpus + } + } +} + +#[repr(C)] +struct HvHeaderStuff { + signature: [u8; 8], + core_size: unsafe extern "C" fn(), + percpu_size: usize, + entry: unsafe extern "C" fn(), + max_cpus: u32, + rt_cpus: u32, +} + +unsafe extern "C" { + unsafe fn __entry_offset(); + unsafe fn __kernel_size(); + +} + +#[used] +#[unsafe(link_section = ".header")] +static HEADER_STUFF: HvHeaderStuff = HvHeaderStuff { + signature: HEADER_SIGNATURE, + core_size: __kernel_size, + percpu_size: 0, + entry: __entry_offset, + max_cpus: 0, + rt_cpus: 0, +}; + +impl Debug for HvHeader { + fn fmt(&self, f: &mut Formatter) -> Result { + f.debug_struct("HvHeader") + .field("signature", &core::str::from_utf8(&self.signature)) + .field("core_size", &self.core_size) + .field("percpu_size", &self.percpu_size) + .field("entry", &self.entry) + .field("max_cpus", &self.max_cpus) + .field("arceos_cpus", &self.arceos_cpus) + .field("reserved_cpus", &self.reserved_cpus()) + .finish() + } +} diff --git a/modules/axhal/src/platform/x86_linux/mem.rs b/modules/axhal/src/platform/x86_linux/mem.rs new file mode 100644 index 0000000000..48bf91314e --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/mem.rs @@ -0,0 +1,78 @@ +// TODO: get memory regions from multiboot info. + +use crate::mem::{MemRegion, MemRegionFlags, MemoryAddr, PhysAddr, virt_to_phys}; + +use super::config::HvSystemConfig; + +// MemRegion { +// paddr: virt_to_phys((_stext as usize).into()), +// size: _etext as usize - _stext as usize, +// flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::EXECUTE, +// name: ".text", +// }, + +unsafe extern "C" { + unsafe fn __header_start(); + unsafe fn __header_end(); +} + +/// Returns the vmm free memory regions (kernel image end to physical memory end). +fn vmm_free_regions() -> impl Iterator { + let mem_pool_start = super::consts::free_memory_start(); + let mem_pool_end = super::consts::hv_end().align_down_4k(); + let mem_pool_size = mem_pool_end.as_usize() - mem_pool_start.as_usize(); + core::iter::once(MemRegion { + paddr: virt_to_phys(mem_pool_start), + size: mem_pool_size, + flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE, + name: "free memory", + }) +} + +fn vmm_cfg_regions() -> impl Iterator { + let vmm_cfg_start = super::consts::cfg_region_start(); + let vmm_cfg_end = super::consts::free_memory_start(); + let vmm_cfg_size = vmm_cfg_end.as_usize() - vmm_cfg_start.as_usize(); + + core::iter::once(MemRegion { + paddr: virt_to_phys(vmm_cfg_start), + size: vmm_cfg_size, + // Provided by host, read-only. + flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, + name: "System config (for VMM)", + }) +} + +pub fn host_memory_regions() -> impl Iterator { + let sys_config = HvSystemConfig::get(); + let cell_config = &sys_config.root_cell.config(); + + cell_config.mem_regions().iter().map(|region| MemRegion { + paddr: PhysAddr::from(region.phys_start as usize), + size: region.size as usize, + flags: region.flags.clone().into(), + name: "Linux mem", + }) +} + +/// Returns platform-specific memory regions. +pub(crate) fn platform_regions() -> impl Iterator { + // Add region for HvHeader. + // See modules/axhal/linker_hv.lds.S for details. + core::iter::once(MemRegion { + paddr: virt_to_phys((__header_start as usize).into()), + size: __header_end as usize - __header_start as usize, + flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, + name: ".header (for VMM)", + }) + .chain(vmm_cfg_regions()) + .chain(vmm_free_regions()) + // Here we do not use the `default_free_regions`` from `crate::mem`. + // Cause we need to reserved regions for + // per-CPU data and `HvSystemConfig` + .chain(crate::mem::default_mmio_regions()) + .chain(host_memory_regions().filter(|region| { + // Map all guest RAM to directly access in hypervisor. + region.flags.contains(MemRegionFlags::DEVICE) + })) +} diff --git a/modules/axhal/src/platform/x86_linux/misc.rs b/modules/axhal/src/platform/x86_linux/misc.rs new file mode 100644 index 0000000000..ba54713474 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/misc.rs @@ -0,0 +1,32 @@ +use x86::bits64::vmx; +use x86_64::instructions::port::PortWriteOnly; +use x86_64::registers::control::{Cr4, Cr4Flags}; + +use crate::cpu::{this_cpu_id, this_cpu_is_reserved}; + +/// Shutdown the whole system (in QEMU), including all CPUs. +/// +/// See for more information. +pub fn terminate() -> ! { + info!("Shutting down..."); + + if this_cpu_is_reserved() { + // #[cfg(platform = "x86_64-qemu-q35")] + unsafe { PortWriteOnly::new(0x604).write(0x2000u16) }; + } else { + warn!("Instance {} CPU terminated", this_cpu_id()); + + // Execute VMXOFF. + unsafe { + let _ = vmx::vmxoff(); + // Remove VMXE bit in CR4. + Cr4::update(|cr4| cr4.remove(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS)); + } + } + + crate::arch::halt(); + warn!("It should shutdown!"); + loop { + crate::arch::halt(); + } +} diff --git a/modules/axhal/src/platform/x86_linux/mod.rs b/modules/axhal/src/platform/x86_linux/mod.rs new file mode 100644 index 0000000000..7ca192edab --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/mod.rs @@ -0,0 +1,205 @@ +mod apic; +mod boot; +mod dtables; +mod entry; +mod uart16550; + +pub mod mem; +pub mod misc; +pub mod time; + +// mods for vmm usage. +pub mod config; +mod consts; +pub mod context; +pub mod header; + +// #[cfg(feature = "smp")] +pub mod mp; + +#[cfg(feature = "irq")] +pub mod irq { + pub use super::apic::*; +} + +pub mod console { + pub use super::uart16550::*; +} + +use core::sync::atomic::{AtomicI32, AtomicU32, Ordering}; + +use axlog::ax_println as println; + +use config::HvSystemConfig; +// use error::HvResult; +use header::HvHeader; + +static VMM_PRIMARY_INIT_OK: AtomicU32 = AtomicU32::new(0); +static ERROR_NUM: AtomicI32 = AtomicI32::new(0); + +fn has_err() -> bool { + ERROR_NUM.load(Ordering::Acquire) != 0 +} + +fn wait_while(condition: impl Fn() -> bool) { + while !has_err() && condition() { + core::hint::spin_loop(); + } + if has_err() { + println!("[Error] Other cpu init failed!") + } +} + +unsafe extern "C" { + fn rust_main(cpu_id: usize, dtb: usize) -> !; + #[cfg(feature = "smp")] + fn rust_main_secondary(cpu_id: usize) -> !; +} + +fn current_cpu_id() -> usize { + match raw_cpuid::CpuId::new().get_feature_info() { + Some(finfo) => finfo.initial_local_apic_id() as usize, + None => 0, + } +} + +fn vmm_primary_init_early(cpu_id: usize) { + println!("Primary CPU {} init early", cpu_id); + // We do not clear bss here. + // Because currently the image was loaded by Linux. + // crate::mem::clear_bss(); + crate::cpu::init_primary(cpu_id); + crate::cpu::set_this_cpu_is_reserved(); + self::uart16550::init(); +} + +fn vmm_secondary_init_early(cpu_id: usize) { + #[cfg(feature = "smp")] + { + println!("Secondary CPU {} init early.", cpu_id); + crate::cpu::init_secondary(cpu_id); + crate::cpu::set_this_cpu_is_reserved(); + } +} + +fn vmm_primary_init(cpu_id: usize) { + self::dtables::init_primary(); + self::time::init_early(); + + println!("HvHeader\n{:#?}", HvHeader::get()); + + let system_config = HvSystemConfig::get(); + + system_config.check(); + + println!( + "\n\ + Initializing ARCEOS on Core [{}]...\n\ + config_signature = {:?}\n\ + config_revision = {}\n\ + ", + cpu_id, + core::str::from_utf8(&system_config.signature), + system_config.revision, + ); +} + +fn vmm_secondary_init(_cpu_id: usize) { + #[cfg(feature = "smp")] + { + self::dtables::init_secondary(); + } +} + +/// Cores entered from Linux will call this function. +/// Cores reserved for ArceOS for other purposed will be shutdown by Linux +/// before entry and restarted through SIPI by ArceOS's `start_secondary_cpu` in mp.rs. +extern "sysv64" fn vmm_cpu_entry(core_id: usize, linux_sp: usize) -> i32 { + let cpu_id = current_cpu_id(); + + // Use Cpu ID 0 as primary core. + // TODO: on some platform Local Apic ID may not start from Zero. + let is_primary = core_id == 0; + + let vm_cpus = HvHeader::get().reserved_cpus(); + + println!( + "{} Core {} (LAPIC_ID {}) entered. [{}/{}]", + if is_primary { "Primary" } else { "Secondary" }, + core_id, + cpu_id, + core_id, + vm_cpus + ); + + wait_while(|| entry::entered_cpus() < vm_cpus); + + println!( + "{} Core {} CPU {} start to initialize.", + if is_primary { "Primary" } else { "Secondary" }, + core_id, + cpu_id, + ); + + // First, we init primary core for VMM. + if is_primary { + vmm_primary_init_early(cpu_id); + } else { + wait_while(|| VMM_PRIMARY_INIT_OK.load(Ordering::Acquire) == 0); + vmm_secondary_init_early(cpu_id); + } + + // Note: this has to be done after `cpu::init_primary`. + // Because LinuxContext will be stored in percpu area. + context::set_linux_context(linux_sp, cpu_id); + + if is_primary { + vmm_primary_init(cpu_id); + } else { + vmm_secondary_init(cpu_id); + } + + unsafe { + if is_primary { + rust_main(cpu_id, 0); + } else { + rust_main_secondary(cpu_id); + } + } +} + +/// Core reserved for ArceOS will entered through this function. +unsafe extern "C" fn rust_entry_secondary(magic: usize) { + // #[cfg(feature = "smp")] + if magic == self::boot::MULTIBOOT_BOOTLOADER_MAGIC { + let cpu_id = current_cpu_id(); + // Note: DO not call log related functions before percpu area is initialized. + crate::cpu::init_secondary(cpu_id); + self::dtables::init_secondary(); + unsafe { + rust_main_secondary(cpu_id); + } + } +} + +/// Initializes the platform devices for the primary CPU. +pub fn platform_init() { + let cpu_id = current_cpu_id(); + + // Consruct LAPIC but DO NOT operate the LAPIC. + // because the LAPIC belongs to Linux, we should not touch it. + self::apic::init_primary(false, cpu_id); + // self::time::init_primary(); + + VMM_PRIMARY_INIT_OK.store(1, Ordering::Release); + // Secondary CPUs continue to initialize. +} + +/// Initializes the platform devices for secondary CPUs. +#[cfg(feature = "smp")] +pub fn platform_init_secondary() { + let cpu_id = current_cpu_id(); + + self::apic::init_secondary(false, cpu_id); + // self::time::init_secondary(); +} diff --git a/modules/axhal/src/platform/x86_linux/mp.rs b/modules/axhal/src/platform/x86_linux/mp.rs new file mode 100644 index 0000000000..5e3a146aa7 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/mp.rs @@ -0,0 +1,111 @@ +use core::sync::atomic::AtomicBool; + +use memory_addr::VirtAddr; + +use crate::mem::{PAGE_SIZE_4K, PhysAddr, phys_to_virt, virt_to_phys}; +use crate::time::{Duration, busy_wait}; + +const START_PAGE_IDX: u8 = 6; +const START_PAGE_PADDR: PhysAddr = pa!(START_PAGE_IDX as usize * PAGE_SIZE_4K); +const U64_PER_PAGE: usize = PAGE_SIZE_4K / 8; + +core::arch::global_asm!( + include_str!("ap_start.S"), + start_page_paddr = const START_PAGE_PADDR.as_usize(), +); + +unsafe extern "C" { + unsafe fn ap_entry32(); + unsafe fn ap_start(); + unsafe fn ap_end(); +} + +static mut BACKUP_PAGE: [u64; U64_PER_PAGE] = [0; U64_PER_PAGE]; + +unsafe fn setup_startup_page(stack_top: PhysAddr, boot_fn: F) +where + F: FnOnce(), +{ + let start_page_ptr = phys_to_virt(START_PAGE_PADDR).as_mut_ptr() as *mut u64; + let start_page = unsafe { core::slice::from_raw_parts_mut(start_page_ptr, U64_PER_PAGE) }; + + // Since start page located at START_PAGE_PADDR belongs to Linux's physical address space. + // We construct a backup space for start page, after `start_ap`, we just copy the backup space back. + unsafe { BACKUP_PAGE.copy_from_slice(&start_page) }; + + unsafe { + core::ptr::copy_nonoverlapping( + ap_start as *const u64, + start_page_ptr, + (ap_end as usize - ap_start as usize) / 8, + ); + } + + // We need to use physical address here. + // Since current physical to virtual address is not identical mapped with offset 0xffff_ff80_0000_0000. + let ap_entry_virt = VirtAddr::from_usize(ap_entry32 as usize); + let ap_entry_phys = virt_to_phys(ap_entry_virt); + + start_page[U64_PER_PAGE - 2] = stack_top.as_usize() as u64; // stack_top + start_page[U64_PER_PAGE - 1] = ap_entry_phys.as_usize() as _; // entry + + boot_fn(); + + // Restore the start page. + unsafe { start_page.copy_from_slice(&BACKUP_PAGE) }; +} + +/// Starts the given secondary CPU with its boot stack. +/// Returns true if the caller should wait for the CPU to be ready. +pub fn start_secondary_cpu(apic_id: usize, stack_top: PhysAddr) -> bool { + // DO not boot CPUs that are reserved for host Linux. + if super::apic::apic_id_is_reserved(apic_id) { + info!( + "CPU {} APIC id {} is reserved for Linux, skip", + super::apic::apic_to_cpu_id(apic_id as u32), + apic_id + ); + return false; + } + + let boot_fn = || { + let apic_id = super::apic::raw_apic_id(apic_id as u8); + let lapic = super::apic::local_apic(); + + info!("Starting secondary CPU {}", apic_id); + + // INIT-SIPI-SIPI Sequence + // Ref: Intel SDM Vol 3C, Section 8.4.4, MP Initialization Example + unsafe { lapic.send_init_ipi(apic_id) }; + busy_wait(Duration::from_millis(10)); // 10ms + unsafe { lapic.send_sipi(START_PAGE_IDX, apic_id) }; + busy_wait(Duration::from_micros(200)); // 200us + unsafe { lapic.send_sipi(START_PAGE_IDX, apic_id) }; + }; + + unsafe { setup_startup_page(stack_top, boot_fn) }; + + return true; +} + +static SHUTDOWN_SECONDARY_CPUS: AtomicBool = AtomicBool::new(false); + +pub fn shutdown_secondary_cpus() { + // Only need to shutdown secondary CPUs once. + if SHUTDOWN_SECONDARY_CPUS.load(core::sync::atomic::Ordering::SeqCst) { + return; + } + + SHUTDOWN_SECONDARY_CPUS.store(true, core::sync::atomic::Ordering::SeqCst); + + info!("Shutting down secondary CPUs..."); + + for cpuid in 0..axconfig::SMP { + // DO not shutdown CPUs that are reserved for host Linux. + if super::apic::apic_id_is_reserved(cpuid) { + continue; + } + debug!("Trying to shut down CPU {}", cpuid); + super::apic::shutdown_ap(cpuid as u32); + } +} diff --git a/modules/axhal/src/platform/x86_linux/multiboot.S b/modules/axhal/src/platform/x86_linux/multiboot.S new file mode 100644 index 0000000000..fdf48d3f0c --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/multiboot.S @@ -0,0 +1,134 @@ +# Bootstrapping from 32-bit with the Multiboot specification. +# See https://www.gnu.org/software/grub/manual/multiboot/multiboot.html + +.section .text.boot +.code32 +# .global _start +# _start: +# mov edi, eax # arg1: magic: 0x2BADB002 +# mov esi, ebx # arg2: multiboot info +# jmp bsp_entry32 + +.balign 4 +.type multiboot_header, STT_OBJECT +multiboot_header: + .int {mb_hdr_magic} # magic: 0x1BADB002 + .int {mb_hdr_flags} # flags + .int -({mb_hdr_magic} + {mb_hdr_flags}) # checksum + .int multiboot_header - {offset} # header_addr + .int _skernel - {offset} # load_addr + .int _edata - {offset} # load_end + .int _ebss - {offset} # bss_end_addr + .int _start - {offset} # entry_addr + +# Common code in 32-bit, prepare states to enter 64-bit. +.macro ENTRY32_COMMON + # set data segment selectors + mov ax, 0x18 + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + # set PAE, PGE bit in CR4 + mov eax, {cr4} + mov cr4, eax + + # load the temporary page table + lea eax, [.Ltmp_pml4 - {offset}] + mov cr3, eax + + # set LME, NXE bit in IA32_EFER + mov ecx, {efer_msr} + mov edx, 0 + mov eax, {efer} + wrmsr + + # set protected mode, write protect, paging bit in CR0 + mov eax, {cr0} + mov cr0, eax +.endm + +# Common code in 64-bit +.macro ENTRY64_COMMON + # clear segment selectors + xor ax, ax + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax +.endm + +.code32 +bsp_entry32: + lgdt [.Ltmp_gdt_desc - {offset}] # load the temporary GDT + ENTRY32_COMMON + ljmp 0x10, offset bsp_entry64 - {offset} # 0x10 is code64 segment + +.code32 +.global ap_entry32 +ap_entry32: + ENTRY32_COMMON + ljmp 0x10, offset ap_entry64 - {offset} # 0x10 is code64 segment + +.code64 +bsp_entry64: + ENTRY64_COMMON + jmp .Lhlt + +.code64 +ap_entry64: + ENTRY64_COMMON + + # set RSP to high address (already set in ap_start.S) + mov rax, {offset} + add rsp, rax + + # call rust_entry_from_vmm(magic) + mov rdi, {mb_magic} + movabs rax, offset {entry_secondary} + + call rax + jmp .Lhlt + +.Lhlt: + hlt + jmp .Lhlt + +.section .rodata +.balign 8 +.Ltmp_gdt_desc: + .short .Ltmp_gdt_end - .Ltmp_gdt - 1 # limit + .long .Ltmp_gdt - {offset} # base + +.section .data +.balign 16 +.Ltmp_gdt: + .quad 0x0000000000000000 # 0x00: null + .quad 0x00cf9b000000ffff # 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) + .quad 0x00af9b000000ffff # 0x10: code segment (base=0, limit=0xfffff, type=64bit code exec/read, DPL=0, 4k) + .quad 0x00cf93000000ffff # 0x18: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) +.Ltmp_gdt_end: + +.balign 4096 +.Ltmp_pml4: + # 0x0000_0000 ~ 0xffff_ffff + .quad .Ltmp_pdpt_low - {offset} + 0x3 # PRESENT | WRITABLE | paddr(tmp_pdpt) + .zero 8 * 510 + # 0xffff_ff80_0000_0000 ~ 0xffff_ff80_ffff_ffff + .quad .Ltmp_pdpt_high - {offset} + 0x3 # PRESENT | WRITABLE | paddr(tmp_pdpt) + +# FIXME: may not work on macOS using hvf as the CPU does not support 1GB page (pdpe1gb) +.Ltmp_pdpt_low: + .quad 0x0000 | 0x83 # PRESENT | WRITABLE | HUGE_PAGE | paddr(0x0) + .quad 0x40000000 | 0x83 # PRESENT | WRITABLE | HUGE_PAGE | paddr(0x4000_0000) + .quad 0x80000000 | 0x83 # PRESENT | WRITABLE | HUGE_PAGE | paddr(0x8000_0000) + .quad 0xc0000000 | 0x83 # PRESENT | WRITABLE | HUGE_PAGE | paddr(0xc000_0000) + .zero 8 * 508 + +.Ltmp_pdpt_high: + # 0xffff_ff80_0000_0000 to 0x4000_0000 + .quad 0x40000000 | 0x83 # PRESENT | WRITABLE | HUGE_PAGE | paddr(0x4000_0000) + .zero 8 * 511 diff --git a/modules/axhal/src/platform/x86_linux/time.rs b/modules/axhal/src/platform/x86_linux/time.rs new file mode 100644 index 0000000000..ee7046ca25 --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/time.rs @@ -0,0 +1,109 @@ +use raw_cpuid::CpuId; + +#[cfg(feature = "irq")] +use int_ratio::Ratio; + +#[cfg(feature = "irq")] +const LAPIC_TICKS_PER_SEC: u64 = 1_000_000_000; // TODO: need to calibrate + +#[cfg(feature = "irq")] +static mut NANOS_TO_LAPIC_TICKS_RATIO: Ratio = Ratio::zero(); + +static mut INIT_TICK: u64 = 0; +static mut CPU_FREQ_MHZ: u64 = axconfig::devices::TIMER_FREQUENCY as u64 / 1_000_000; + +/// RTC wall time offset in nanoseconds at monotonic time base. +static mut RTC_EPOCHOFFSET_NANOS: u64 = 0; + +/// Returns the current clock time in hardware ticks. +pub fn current_ticks() -> u64 { + unsafe { core::arch::x86_64::_rdtsc() - INIT_TICK } +} + +/// Converts hardware ticks to nanoseconds. +pub fn ticks_to_nanos(ticks: u64) -> u64 { + ticks * 1_000 / unsafe { CPU_FREQ_MHZ } +} + +/// Converts nanoseconds to hardware ticks. +pub fn nanos_to_ticks(nanos: u64) -> u64 { + nanos * unsafe { CPU_FREQ_MHZ } / 1_000 +} + +/// Return epoch offset in nanoseconds (wall time offset to monotonic clock start). +pub fn epochoffset_nanos() -> u64 { + unsafe { RTC_EPOCHOFFSET_NANOS } +} + +/// Set a one-shot timer. +/// +/// A timer interrupt will be triggered at the given deadline (in nanoseconds). +#[cfg(feature = "irq")] +pub fn set_oneshot_timer(deadline_ns: u64) { + let lapic = super::apic::local_apic(); + let now_ns = crate::time::monotonic_time_nanos(); + unsafe { + if now_ns < deadline_ns { + let apic_ticks = NANOS_TO_LAPIC_TICKS_RATIO.mul_trunc(deadline_ns - now_ns); + assert!(apic_ticks <= u32::MAX as u64); + lapic.set_timer_initial(apic_ticks.max(1) as u32); + } else { + lapic.set_timer_initial(1); + } + } +} + +pub(super) fn init_early() { + if let Some(freq) = CpuId::new() + .get_processor_frequency_info() + .map(|info| info.processor_base_frequency()) + { + if freq > 0 { + axlog::ax_println!("Got TSC frequency by CPUID: {} MHz", freq); + unsafe { CPU_FREQ_MHZ = freq as u64 } + } + } + + unsafe { INIT_TICK = core::arch::x86_64::_rdtsc() }; + + #[cfg(feature = "rtc")] + { + use x86_rtc::Rtc; + + // Get the current time in microseconds since the epoch (1970-01-01) from the x86 RTC. + // Subtract the timer ticks to get the actual time when ArceOS was booted. + let eopch_time_nanos = Rtc::new().get_unix_timestamp() * 1_000_000_000; + unsafe { + RTC_EPOCHOFFSET_NANOS = eopch_time_nanos - ticks_to_nanos(INIT_TICK); + } + } +} + +/// Currently we just passthrough the Lapic to host Linux. +/// We can just enable APIC timer on CPUs that are reserved for ArceOS itself. +#[allow(unused)] +pub(super) fn init_primary() { + #[cfg(feature = "irq")] + unsafe { + use x2apic::lapic::{TimerDivide, TimerMode}; + let lapic = super::apic::local_apic(); + lapic.set_timer_mode(TimerMode::OneShot); + lapic.set_timer_divide(TimerDivide::Div256); // indeed it is Div1, the name is confusing. + lapic.enable_timer(); + + // TODO: calibrate with HPET + NANOS_TO_LAPIC_TICKS_RATIO = Ratio::new( + LAPIC_TICKS_PER_SEC as u32, + crate::time::NANOS_PER_SEC as u32, + ); + } +} + +#[allow(unused)] +#[cfg(feature = "smp")] +pub(super) fn init_secondary() { + #[cfg(feature = "irq")] + unsafe { + super::apic::local_apic().enable_timer(); + } +} diff --git a/modules/axhal/src/platform/x86_linux/uart16550.rs b/modules/axhal/src/platform/x86_linux/uart16550.rs new file mode 100644 index 0000000000..5909b9524e --- /dev/null +++ b/modules/axhal/src/platform/x86_linux/uart16550.rs @@ -0,0 +1,129 @@ +//! Uart 16550. + +use kspin::SpinNoIrq; +use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly}; + +const UART_CLOCK_FACTOR: usize = 16; +const OSC_FREQ: usize = 1_843_200; + +const PORT_COM1: u16 = 0x2f8; + +static COM1: SpinNoIrq = SpinNoIrq::new(Uart16550::new(PORT_COM1)); + +bitflags::bitflags! { + /// Line status flags + struct LineStsFlags: u8 { + const INPUT_FULL = 1; + // 1 to 4 unknown + const OUTPUT_EMPTY = 1 << 5; + // 6 and 7 unknown + } +} + +struct Uart16550 { + data: Port, + int_en: PortWriteOnly, + fifo_ctrl: PortWriteOnly, + line_ctrl: PortWriteOnly, + modem_ctrl: PortWriteOnly, + line_sts: PortReadOnly, +} + +impl Uart16550 { + const fn new(port: u16) -> Self { + Self { + data: Port::new(port), + int_en: PortWriteOnly::new(port + 1), + fifo_ctrl: PortWriteOnly::new(port + 2), + line_ctrl: PortWriteOnly::new(port + 3), + modem_ctrl: PortWriteOnly::new(port + 4), + line_sts: PortReadOnly::new(port + 5), + } + } + + fn init(&mut self, baud_rate: usize) { + unsafe { + // Disable interrupts + self.int_en.write(0x00); + + // Enable DLAB + self.line_ctrl.write(0x80); + + // Set maximum speed according the input baud rate by configuring DLL and DLM + let divisor = OSC_FREQ / (baud_rate * UART_CLOCK_FACTOR); + self.data.write((divisor & 0xff) as u8); + self.int_en.write((divisor >> 8) as u8); + + // Disable DLAB and set data word length to 8 bits + self.line_ctrl.write(0x03); + + // Enable FIFO, clear TX/RX queues and + // set interrupt watermark at 14 bytes + self.fifo_ctrl.write(0xC7); + + // Mark data terminal ready, signal request to send + // and enable auxilliary output #2 (used as interrupt line for CPU) + self.modem_ctrl.write(0x0B); + } + } + + fn line_sts(&mut self) -> LineStsFlags { + unsafe { LineStsFlags::from_bits_truncate(self.line_sts.read()) } + } + + fn putchar(&mut self, c: u8) { + while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {} + unsafe { self.data.write(c) }; + } + + fn getchar(&mut self) -> Option { + if self.line_sts().contains(LineStsFlags::INPUT_FULL) { + unsafe { Some(self.data.read()) } + } else { + None + } + } +} + +/// Writes a byte to the console. +fn putchar(c: u8) { + let mut uart = COM1.lock(); + match c { + b'\n' => { + uart.putchar(b'\r'); + uart.putchar(b'\n'); + } + c => uart.putchar(c), + } +} + +/// Reads a byte from the console, or returns [`None`] if no input is available. +fn getchar() -> Option { + COM1.lock().getchar() +} + +/// Write a slice of bytes to the console. +pub fn write_bytes(bytes: &[u8]) { + for c in bytes { + putchar(*c); + } +} + +/// Reads bytes from the console into the given mutable slice. +/// Returns the number of bytes read. +pub fn read_bytes(bytes: &mut [u8]) -> usize { + let mut read_len = 0; + while read_len < bytes.len() { + if let Some(c) = getchar() { + bytes[read_len] = c; + } else { + break; + } + read_len += 1; + } + read_len +} + +pub(super) fn init() { + COM1.lock().init(115200); +} diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index 23165d996f..e7cc036beb 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -74,6 +74,10 @@ impl AddrSpace { flags: MappingFlags, ) -> AxResult { if !self.contains_range(start_vaddr, size) { + warn!( + "address out of range: {:?} + {:#x}, paddr {:?} {:?}", + start_vaddr, size, start_paddr, flags + ); return ax_err!(InvalidInput, "address out of range"); } if !start_vaddr.is_aligned_4k() || !start_paddr.is_aligned_4k() || !is_aligned_4k(size) { diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index e6029da263..c9e7388d6d 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -37,7 +37,8 @@ axdisplay = { workspace = true, optional = true } axtask = { workspace = true, optional = true } crate_interface = "0.1" -percpu = { version = "0.1", optional = true } +percpu = { version = "0.1.4", optional = true } kernel_guard = { version = "0.1", optional = true } chrono = { version = "0.4.38", default-features = false } +lazyinit = "0.2" diff --git a/modules/axruntime/src/lang_items.rs b/modules/axruntime/src/lang_items.rs index 6600cdd746..61a5869ee2 100644 --- a/modules/axruntime/src/lang_items.rs +++ b/modules/axruntime/src/lang_items.rs @@ -2,6 +2,6 @@ use core::panic::PanicInfo; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - error!("{}", info); + axlog::ax_println!("[{}]:{}", axhal::cpu::this_cpu_id(), info); axhal::misc::terminate() } diff --git a/modules/axruntime/src/mp.rs b/modules/axruntime/src/mp.rs index 5e1baab4b9..3acc4966aa 100644 --- a/modules/axruntime/src/mp.rs +++ b/modules/axruntime/src/mp.rs @@ -18,10 +18,10 @@ pub fn start_secondary_cpus(primary_cpu_id: usize) { })); debug!("starting CPU {}...", i); - axhal::mp::start_secondary_cpu(i, stack_top); + let should_wait = axhal::mp::start_secondary_cpu(i, stack_top); logic_cpu_id += 1; - while ENTERED_CPUS.load(Ordering::Acquire) <= logic_cpu_id { + while should_wait && ENTERED_CPUS.load(Ordering::Acquire) <= logic_cpu_id { core::hint::spin_loop(); } } diff --git a/modules/axtask/Cargo.toml b/modules/axtask/Cargo.toml index 6232c42f9d..f5d0ad73aa 100644 --- a/modules/axtask/Cargo.toml +++ b/modules/axtask/Cargo.toml @@ -40,7 +40,7 @@ cfg-if = "1.0" log = "=0.4.21" axhal = { workspace = true } axconfig = { workspace = true, optional = true } -percpu = { version = "0.1", optional = true } +percpu = { version = "0.1.4", optional = true } kspin = { version = "0.1", optional = true } lazyinit = { version = "0.2", optional = true } memory_addr = { version = "0.3", optional = true } diff --git a/modules/axtask/src/api.rs b/modules/axtask/src/api.rs index 7cf96a043c..5226116634 100644 --- a/modules/axtask/src/api.rs +++ b/modules/axtask/src/api.rs @@ -210,8 +210,9 @@ pub fn exit(exit_code: i32) -> ! { pub fn run_idle() -> ! { loop { yield_now(); - debug!("idle task: waiting for IRQs..."); - #[cfg(feature = "irq")] - axhal::arch::wait_for_irqs(); + // debug!("idle task: waiting for IRQs..."); + // #[cfg(feature = "irq")] + // axhal::arch::wait_for_irqs(); + core::hint::spin_loop(); } } diff --git a/scripts/make/build.mk b/scripts/make/build.mk index d346b03375..2be9a11cc6 100644 --- a/scripts/make/build.mk +++ b/scripts/make/build.mk @@ -52,4 +52,7 @@ $(OUT_DIR): $(OUT_BIN): _cargo_build $(OUT_ELF) $(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@) +$(OUT_ASM): $(OUT_BIN) + $(call run_cmd,$(OBJDUMP),$(OUT_ELF) > $(OUT_ASM)) + .PHONY: _cargo_build diff --git a/scripts/make/rk3588.mk b/scripts/make/rk3588.mk new file mode 100644 index 0000000000..49c0a44434 --- /dev/null +++ b/scripts/make/rk3588.mk @@ -0,0 +1,13 @@ +RK3588_GITHUB_URL = https://github.com/arceos-hypervisor/platform_tools/releases/download/latest/rk3588.zip +RK3588_MKIMG_FILE = ./tools/rk3588/mkimg +check-download: +ifeq ("$(wildcard $(RK3588_MKIMG_FILE))","") + @echo "file not found, downloading from $(RK3588_GITHUB_URL)..."; + wget $(RK3588_GITHUB_URL); + unzip -o rk3588.zip -d tools; + rm rk3588.zip; +endif + +kernel: check-download build + $(RK3588_MKIMG_FILE) --dtb rk3588-firefly-itx-3588j.dtb --img $(OUT_BIN) + @echo 'Built the FIT-uImage boot.img' diff --git a/scripts/vmm/guest/disable-arceos-vmm.sh b/scripts/vmm/guest/disable-arceos-vmm.sh new file mode 100755 index 0000000000..2b46fa4494 --- /dev/null +++ b/scripts/vmm/guest/disable-arceos-vmm.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +JH_DIR=~/jailhouse-equation +JH=$JH_DIR/tools/jailhouse + +sudo $JH disable diff --git a/scripts/vmm/guest/enable-arceos-vmm.sh b/scripts/vmm/guest/enable-arceos-vmm.sh new file mode 100755 index 0000000000..0b4c1a2061 --- /dev/null +++ b/scripts/vmm/guest/enable-arceos-vmm.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +JH_DIR=~/jailhouse-equation +JH=$JH_DIR/tools/jailhouse + +sudo $JH disable +sudo rmmod jailhouse +sudo insmod $JH_DIR/driver/jailhouse.ko +sudo chown $(whoami) /dev/jailhouse +sudo $JH enable diff --git a/scripts/vmm/guest/setup.sh b/scripts/vmm/guest/setup.sh new file mode 100755 index 0000000000..7d141f4c98 --- /dev/null +++ b/scripts/vmm/guest/setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Install packages +sudo sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list +sudo apt-get update +sudo apt-get install -y build-essential python3-mako + +# Create a hypervisor image link to /lib/firmware/evm-intel.bin +sudo mkdir -p /lib/firmware +sudo ln -sf ~/evm-intel.bin /lib/firmware + +# Clone jailhouse-equation, apply patches and build +# git clone https://github.com/EquationOS/jailhouse-equation +cd jailhouse-equation +make + +# Update grub config file to update kernel cmdline +./update-cmdline.sh +sudo update-grub + +echo +echo "Setup OK!" +echo "Press ENTER to reboot..." +read +sudo reboot diff --git a/scripts/vmm/guest/test_hypercall.c b/scripts/vmm/guest/test_hypercall.c new file mode 100644 index 0000000000..ceeec157f9 --- /dev/null +++ b/scripts/vmm/guest/test_hypercall.c @@ -0,0 +1,40 @@ +#include +#include +#include + +#define HYPERCALL "vmcall" + +static void in_guest() { + printf("Execute VMCALL OK.\n"); + printf("You are in the Guest mode.\n"); + exit(0); +} + +static void in_host() { + printf("Execute VMCALL failed.\n"); + printf("You are in the Host mode.\n"); + exit(1); +} + +static void sig_handler(int signum) { + printf("Caught signal %d\n", signum); + in_host(); +} + +static inline long hypercall(int num) { + long ret; + asm volatile(HYPERCALL : "=a"(ret) : "a"(num) : "memory"); + return ret; +} + +int main () { + signal(SIGSEGV, sig_handler); + signal(SIGILL, sig_handler); + int ret = hypercall(2333); + if (ret == 2333) { + in_guest(); + } else { + in_host(); + } + return 0; +} diff --git a/scripts/vmm/guest/update-cmdline.sh b/scripts/vmm/guest/update-cmdline.sh new file mode 100755 index 0000000000..67c72af656 --- /dev/null +++ b/scripts/vmm/guest/update-cmdline.sh @@ -0,0 +1,4 @@ +# Update grub config +cmdline='memmap=0x8000000\\\\\\$0x40000000' +sudo sed -i "s/GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=$cmdline/" /etc/default/grub +echo "Appended kernel cmdline: $cmdline, see '/etc/default/grub'" diff --git a/scripts/vmm/host/.gitignore b/scripts/vmm/host/.gitignore new file mode 100644 index 0000000000..37c1c8464d --- /dev/null +++ b/scripts/vmm/host/.gitignore @@ -0,0 +1,2 @@ +*.img +user-data \ No newline at end of file diff --git a/scripts/vmm/host/Makefile b/scripts/vmm/host/Makefile new file mode 100644 index 0000000000..23a03f6e4a --- /dev/null +++ b/scripts/vmm/host/Makefile @@ -0,0 +1,58 @@ +QEMU ?= qemu-system-x86_64 +PORT ?= 2334 +SMP ?= 4 +OUT_ELF ?= ../../../apps/vmm/vmm_x86_64-linux.elf + +TELNET_PORT := 4321 + +UV ?= 24.04 + +ifeq ($(UV),22.04) +RELEASE_NAME = focal +endif + +ifeq ($(UV),24.04) +RELEASE_NAME = noble +endif + +qemu_image := ubuntu-$(UV)-server-cloudimg-amd64.img +qemu_args := \ + -smp $(SMP) -m 4G -accel kvm -nographic \ + -machine q35,kernel_irqchip=split \ + -cpu host,-kvm-asyncpf,-kvm-pv-eoi,-kvm-pv-ipi,-kvm-pv-sched-yield,-kvm-pv-unhalt,-kvm-steal-time,-kvmclock \ + -drive file=$(qemu_image) \ + -net user,id=net,hostfwd=tcp::$(PORT)-:22 -net nic,model=e1000e \ + -serial mon:stdio \ + -serial telnet:localhost:$(TELNET_PORT),server + +$(qemu_image): + wget -nc https://cloud-images.ubuntu.com/releases/$(RELEASE_NAME)/release/$(qemu_image) + +.ONESHELL: +image: $(qemu_image) + cat >user-data <