diff --git a/.gitignore b/.gitignore index 087d952d0..d71e45608 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /platform/*/*/image/kernel/* /platform/*/*/image/virtdisk/* /platform/*/*/image/bootloader/out +/platform/*/*/image/mb2_bootloader/out /platform/*/*/image/iso/boot/kernel/* /platform/*/*/image/iso/boot/hvisor /tools/hvisor @@ -41,4 +42,4 @@ jenkins/__pycache__ jenkins-cli.jar tools/kconfig/.venv/ tools/kconfig/__pycache__/ -kernel_build/ \ No newline at end of file +kernel_build/ diff --git a/platform/x86_64/nuc14mnk/board.rs b/platform/x86_64/nuc14mnk/board.rs index 9ee5d4c7c..1768ecea9 100644 --- a/platform/x86_64/nuc14mnk/board.rs +++ b/platform/x86_64/nuc14mnk/board.rs @@ -53,7 +53,7 @@ const ROOT_ZONE_UEFI_REGION: HvConfigMemoryRegion = HvConfigMemoryRegion { const ROOT_ZONE_UEFI_REGION_ID: usize = 0x3; pub const ROOT_ZONE_NAME: &str = "root-linux"; -pub const ROOT_ZONE_CMDLINE: &str = "video=vesafb console=tty0 nointremap no_timer_check efi=noruntime pci=pcie_scan_all root=/dev/nvme0n1p5 rw init=/init rootwait\0"; +pub const ROOT_ZONE_CMDLINE: &str = "video=vesafb console=tty0 earlycon=efifb nointremap no_timer_check efi=noruntime pci=pcie_scan_all root=/dev/nvme0n1p5 rw init=/init rootwait\0"; // pub const ROOT_ZONE_CMDLINE: &str = "video=vesafb console=ttyS0 earlyprintk=serial nointremap no_timer_check pci=pcie_scan_all root=/dev/vda rw init=/init\0"; //"console=ttyS0 earlyprintk=serial rdinit=/init nokaslr nointremap\0"; // noapic // video=vesafb @@ -113,9 +113,9 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 15] = [ }, // pnp 00:05 HvConfigMemoryRegion { mem_type: MEM_TYPE_RESERVED, - physical_start: 0xfe01_1000, - virtual_start: 0xfe01_1000, - size: 0x40_0000, + physical_start: 0xfe00_0000, + virtual_start: 0xfe00_0000, + size: 0xd0_0000, }, // reserved HvConfigMemoryRegion { mem_type: MEM_TYPE_RESERVED, diff --git a/platform/x86_64/qemu/board.rs b/platform/x86_64/qemu/board.rs index fc25028db..b29fe4d8a 100644 --- a/platform/x86_64/qemu/board.rs +++ b/platform/x86_64/qemu/board.rs @@ -54,7 +54,9 @@ const ROOT_ZONE_UEFI_REGION_ID: usize = 0x3; pub const ROOT_ZONE_NAME: &str = "root-linux"; pub const ROOT_ZONE_CMDLINE: &str = - "console=tty0 console=ttyS0 earlycon=efifb earlyprintk=serial nointremap no_timer_check efi=noruntime pci=pcie_scan_all,lastbus=1 root=/dev/vda rw init=/init\0"; +// "BOOT_IMAGE=/boot/aster-kernel-osdk-bin SHELL=/bin/sh LOGNAME=root HOME=/ USER=root PATH=/bin:/benchmark ostd.log_level=info console=ttyS0 console=tty0 -- sh -l"; +// TODO: Asterinas +"console=ttyS0 earlyprintk=serial nointremap no_timer_check efi=noruntime pci=pcie_scan_all,lastbus=1 root=/dev/vda rw init=/init\0"; //"console=ttyS0 earlyprintk=serial rdinit=/init nokaslr nointremap\0"; // noapic // video=vesafb // /lib/systemd/systemd @@ -109,6 +111,8 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 10] = [ ]; const ROOT_ZONE_CMDLINE_ADDR: GuestPhysAddr = 0x9000; +// TODO: Asterinas +// const ROOT_ZONE_SETUP_ADDR: GuestPhysAddr = 0xf_f000; const ROOT_ZONE_SETUP_ADDR: GuestPhysAddr = 0xa000; const ROOT_ZONE_VMLINUX_ENTRY_ADDR: GuestPhysAddr = 0x10_0000; const ROOT_ZONE_SCREEN_BASE_ADDR: GuestPhysAddr = 0x7000_0000; @@ -122,8 +126,9 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { kernel_entry_gpa: ROOT_ZONE_VMLINUX_ENTRY_ADDR, cmdline_load_gpa: ROOT_ZONE_CMDLINE_ADDR, setup_load_gpa: ROOT_ZONE_SETUP_ADDR, - initrd_load_gpa: 0, // 0x1500_0000, - initrd_size: 0, //0x26_b000, + // TODO: Asterinas + initrd_load_gpa: 0, // 0x1530_0000, + initrd_size: 0, // 0x210_0000, //0x26_b000, rsdp_memory_region_id: ROOT_ZONE_RSDP_REGION_ID, acpi_memory_region_id: ROOT_ZONE_ACPI_REGION_ID, uefi_memory_region_id: ROOT_ZONE_UEFI_REGION_ID, @@ -149,11 +154,13 @@ pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [HvPciConfig { }]; pub const ROOT_PCI_MAX_BUS: usize = 1; -pub const ROOT_PCI_DEVS: [HvPciDevConfig; 7] = [ +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 8] = [ pci_dev!(0x0, 0x0, 0x0, 0x0 => 0x0, 0x0, 0x0, VpciDevType::Physical), // host bridge pci_dev!(0x0, 0x0, 0x1, 0x0 => 0x0, 0x1, 0x0, VpciDevType::Physical), // VGA controller pci_dev!(0x0, 0x0, 0x2, 0x0 => 0x0, 0x2, 0x0, VpciDevType::Physical), // Ethernet controller pci_dev!(0x0, 0x0, 0x3, 0x0 => 0x0, 0x3, 0x0, VpciDevType::Physical), // PCI bridge + // Asterinas + pci_dev!(0x0, 0x0, 0x4, 0x0 => 0x0, 0x4, 0x0, VpciDevType::Physical), // PCI bridge pci_dev!(0x0, 0x0, 0x1f, 0x0 => 0x0, 0x1f, 0x0, VpciDevType::Physical), // ISA bridge pci_dev!(0x0, 0x0, 0x1f, 0x2 => 0x0, 0x1f, 0x2, VpciDevType::Physical), // SATA controller // pci_dev!(0x0, 0x0, 0x1f, 0x3 => 0x0, 0x1f, 0x3, VpciDevType::Physical), // SMBus diff --git a/platform/x86_64/qemu/configs/virtio_cfg_asterinas.json b/platform/x86_64/qemu/configs/virtio_cfg_asterinas.json new file mode 100644 index 000000000..34d7ff2ea --- /dev/null +++ b/platform/x86_64/qemu/configs/virtio_cfg_asterinas.json @@ -0,0 +1,45 @@ +{ + "zones": [ + { + "id": 1, + "memory_region": [ + { + "zone0_ipa": "0x40300000", + "zonex_ipa": "0x0", + "size": "0x20000000" + }, + { + "zone0_ipa": "0x100000000", + "zonex_ipa": "0x20000000", + "size": "0x40000000" + } + ], + "devices": [ + { + "type": "console", + "status": "enable", + "addr": "0xFEB00000", + "len": "0x200", + "irq": "16" + }, + { + "type": "net", + "status": "enable", + "addr": "0xFEB00200", + "len": "0x200", + "irq": "17", + "tap": "tap0", + "mac": ["0x00", "0x16", "0x3e", "0x10", "0x10", "0x10"] + }, + { + "type": "blk", + "status": "enable", + "addr": "0xFEB00400", + "len": "0x200", + "irq": "18", + "img": "/zone1_disk.img" + } + ] + } + ] +} diff --git a/platform/x86_64/qemu/configs/zone1-asterinas.json b/platform/x86_64/qemu/configs/zone1-asterinas.json new file mode 100644 index 000000000..4ded57c8c --- /dev/null +++ b/platform/x86_64/qemu/configs/zone1-asterinas.json @@ -0,0 +1,90 @@ +{ + "name": "asterinas", + "zone_id": 1, + "cpus": [2], + "memory_regions": [ + { + "type": "ram", + "physical_start": "0x40300000", + "virtual_start": "0x0", + "size": "0xe0000" + }, + { + "type": "ram", + "physical_start": "0x403e0000", + "virtual_start": "0xe0000", + "size": "0x20000" + }, + { + "type": "ram", + "physical_start": "0x40400000", + "virtual_start": "0x100000", + "size": "0x1fe00000" + }, + { + "type": "ram", + "physical_start": "0x60200000", + "virtual_start": "0x1ff00000", + "size": "0x100000" + }, + { + "type": "ram", + "physical_start": "0x100000000", + "virtual_start": "0x20000000", + "size": "0x40000000" + }, + { + "type": "virtio", + "physical_start": "0xFEB00000", + "virtual_start": "0xFEB00000", + "size": "0x2000" + } + ], + "interrupts": [16, 17, 18], + "ivc_configs": [], + "kernel_filepath": "./aster-kernel-osdk-bin", + "dtb_filepath": "null", + "kernel_load_paddr": "0x8000000", + "dtb_load_paddr": "0x00000000", + "entry_point": "0x8000", + "multiboot_enabled": true, + "multiboot_info_paddr": "0x9000000", + "kernel_cmdline": "console=hvc0 ostd.log_level=error SHELL=/bin/sh LOGNAME=root HOME=/ USER=root PATH=/bin:/benchmark init=/init -- sh -l", + "initramfs_filepath": "./initramfs.cpio.gz", + "initramfs_load_gpa": "0x10000000", + "arch_config": { + "ioapic_base": "0xfec00000", + "ioapic_size": "0x1000", + "boot_filepath": "mb2_boot.bin", + "boot_load_paddr": "0x8000", + "kernel_entry_gpa": "0x8001238", + "cmdline_load_gpa": "0x9020000", + "setup_load_gpa": "0x80000", + "initrd_load_gpa": "0x0", + "initrd_size": "0x0", + "rsdp_memory_region_id": 1, + "acpi_memory_region_id": 3, + "uefi_memory_region_id": 0, + "screen_base": "0x0" + }, + "pci_config": [ + { + "ecam_base": "0xe0000000", + "ecam_size": "0x200000", + "io_base": "0x0", + "io_size": "0x0", + "pci_io_base": "0x0", + "mem32_base": "0x0", + "mem32_size": "0x0", + "pci_mem32_base": "0x0", + "mem64_base": "0x0", + "mem64_size": "0x0", + "pci_mem64_base": "0x0", + "bus_range_begin": "0x0", + "bus_range_end": "0x1f", + "domain": "0x0" + } + ], + "num_pci_devs": 0, + "alloc_pci_devs": [] +} diff --git a/platform/x86_64/qemu/image/iso/boot/grub/grub.cfg b/platform/x86_64/qemu/image/iso/boot/grub/grub.cfg index c2a54ae3b..93796f90c 100644 --- a/platform/x86_64/qemu/image/iso/boot/grub/grub.cfg +++ b/platform/x86_64/qemu/image/iso/boot/grub/grub.cfg @@ -3,13 +3,22 @@ set default=0 # default menu entry index insmod all_video + menuentry "Hvisor" { multiboot2 /boot/hvisor # use multiboot spec to boot module2 /boot/kernel/boot.bin 0 module2 /boot/kernel/boot.bin 5008000 module2 /boot/kernel/setup.bin 500a000 module2 /boot/kernel/vmlinux.bin 5100000 - module2 /boot/kernel/initramfs.cpio.gz 1a000000 + boot +} + +menuentry "Asterinas" { + multiboot2 /boot/hvisor # use multiboot spec to boot + module2 /boot/kernel/boot.bin 0 + module2 /boot/kernel/boot.bin 5008000 + module2 /boot/kernel/aster-kernel-osdk-bin 50ff000 + module2 /boot/kernel/initramfs.cpio.gz 1a300000 boot } diff --git a/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.S b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.S new file mode 100644 index 000000000..eb0cfa2b7 --- /dev/null +++ b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.S @@ -0,0 +1,68 @@ +# Multiboot2 bootloader for Asterinas zone1 +# Transitions from 16-bit real mode to 32-bit protected mode, +# sets up GDT with TSS, then jumps to the kernel entry point. +# +# Entry state (from VMCS guest_regs via unrestricted guest): +# EAX = Multiboot2 magic (0x36D76289) +# EBX = Multiboot2 info pointer (GPA) +# ESI = Kernel entry point (GPA) + +.section .text +.code16 +.global mb2_entry16 +mb2_entry16: + cli + cld + + mov ebp, eax # save multiboot magic + mov edi, ebx # save multiboot info pointer + mov ecx, esi # save kernel entry point + + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + + lgdt [mb2_gdt_desc] + mov eax, cr0 + or eax, 0x1 + mov cr0, eax + + ljmp 0x08, mb2_entry32 + +.code32 +.global mb2_entry32 +mb2_entry32: + mov ax, 0x10 + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + # Load TSS (entry 4 at selector 0x20) + mov ax, 0x20 + ltr ax + + # Restore Multiboot2 parameters + mov eax, ebp + mov ebx, edi + + # Set up stack + mov esp, MB2_STACK + + # Jump to kernel entry + jmp ecx + +.align 16 +mb2_gdt: + .quad 0x0000000000000000 # 0x00: null + .quad 0x00cf9b000000ffff # 0x08: 32-bit code + .quad 0x00cf93000000ffff # 0x10: 32-bit data + .quad 0x0000000000000000 # 0x18: unused + .quad MB2_TSS_DESCRIPTOR # 0x20: 32-bit available TSS +mb2_gdt_end: + +mb2_gdt_desc: + .short mb2_gdt_end - mb2_gdt - 1 + .long mb2_gdt diff --git a/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.ld b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.ld new file mode 100644 index 000000000..dc5e86666 --- /dev/null +++ b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.ld @@ -0,0 +1,15 @@ +OUTPUT_ARCH(i386) +BASE_ADDRESS = 0x8000; + +ENTRY(mb2_entry16) +SECTIONS +{ + . = BASE_ADDRESS; + .text : { + *(.text .text.*) + } + + /DISCARD/ : { + *(.eh_frame) *(.eh_frame_hdr) + } +} diff --git a/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.mk b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.mk new file mode 100644 index 000000000..9917a74eb --- /dev/null +++ b/platform/x86_64/qemu/image/mb2_bootloader/mb2_boot.mk @@ -0,0 +1,40 @@ +mb2_boot_dir := $(image_dir)/mb2_bootloader +mb2_boot_out_dir := $(image_dir)/mb2_bootloader/out + +mb2_boot_src := $(mb2_boot_dir)/mb2_boot.S +mb2_boot_lds := $(mb2_boot_dir)/mb2_boot.ld + +mb2_boot_o := $(mb2_boot_out_dir)/mb2_boot.o +mb2_boot_elf := $(mb2_boot_out_dir)/mb2_boot.elf +mb2_boot_bin := $(mb2_boot_out_dir)/mb2_boot.bin + +AS ?= as +LD ?= ld +OBJCOPY ?= objcopy + +# TSS descriptor: base=0x1fffc000, limit=103, type=0x89 (32-bit available TSS) +# Encoded bytes: 67 00 00 c0 ff 89 00 1f +# Stack/TSS live near the top of zone1 low RAM, outside the kernel LOAD range. +# The stack at 0x1fffe000 must not grow more than 8KB into the TSS below. +MB2_TSS_DESC := 0x1F0089FFC0000067 +MB2_STACK := 0x1fffe000 + +mb2_boot_flags := --32 -msyntax=intel -mnaked-reg +mb2_boot_flags += --defsym MB2_STACK=$(MB2_STACK) +mb2_boot_flags += --defsym MB2_TSS_DESCRIPTOR=$(MB2_TSS_DESC) + +mb2_boot: | $(mb2_boot_out_dir) $(mb2_boot_bin) + +$(mb2_boot_out_dir): + mkdir -p $@ + +$(mb2_boot_o): $(mb2_boot_src) + $(AS) $(mb2_boot_flags) $< -o $@ + +$(mb2_boot_elf): $(mb2_boot_o) $(mb2_boot_lds) + $(LD) -T$(mb2_boot_lds) $< -o $@ + +$(mb2_boot_bin): $(mb2_boot_elf) + $(OBJCOPY) $< --strip-all -O binary $@ + +.PHONY: mb2_boot diff --git a/platform/x86_64/qemu/platform.mk b/platform/x86_64/qemu/platform.mk index 3e3d8cb2e..eb9692595 100644 --- a/platform/x86_64/qemu/platform.mk +++ b/platform/x86_64/qemu/platform.mk @@ -1,8 +1,10 @@ QEMU := qemu-system-x86_64 zone0_boot := $(image_dir)/bootloader/out/boot.bin +zone1_mb2_boot := $(image_dir)/mb2_bootloader/out/mb2_boot.bin zone0_setup := $(image_dir)/kernel/setup.bin zone0_vmlinux := $(image_dir)/kernel/vmlinux.bin +zone0_asterinas := $(image_dir)/kernel/aster-kernel-osdk-bin zone0_initrd := $(image_dir)/virtdisk/initramfs.cpio.gz zone0_rootfs := $(image_dir)/virtdisk/rootfs1.img zone1_rootfs := $(image_dir)/virtdisk/rootfs2.img @@ -10,20 +12,29 @@ zone1_rootfs := $(image_dir)/virtdisk/rootfs2.img QEMU_ARGS := -machine q35,kernel-irqchip=split QEMU_ARGS += -cpu host,+x2apic,+invtsc,+vmx -accel kvm QEMU_ARGS += -smp 4 +# TODO: Asterinas QEMU_ARGS += -serial mon:stdio QEMU_ARGS += -m 4G QEMU_ARGS += -bios /usr/share/ovmf/OVMF.fd QEMU_ARGS += -vga std -# QEMU_ARGS += -nographic +QEMU_ARGS += -nographic QEMU_ARGS += -nodefaults QEMU_ARGS += -net nic -net user QEMU_ARGS += -device intel-iommu,intremap=on,eim=on,caching-mode=on,device-iotlb=on,aw-bits=48 QEMU_ARGS += -device ioh3420,id=pcie.1,chassis=1 + +# TODO: Asterinas QEMU_ARGS += -drive if=none,file="$(zone0_rootfs)",id=X10008000,format=raw QEMU_ARGS += -device virtio-blk-pci,bus=pcie.1,drive=X10008000,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on +# TODO: Asterinas +# QEMU_ARGS += -device virtio-serial +# QEMU_ARGS += -device virtconsole,chardev=mux +# QEMU_ARGS += -serial chardev:mux +# QEMU_ARGS += -chardev stdio,id=mux,mux=on,signal=off + # QEMU_ARGS += -drive if=none,file="$(zone0_rootfs)",id=X10009000,format=raw # QEMU_ARGS += -device nvme,serial=deadbeef,drive=X10009000 # QEMU_ARGS += -drive if=none,file="$(zone1_rootfs)",id=X10009000,format=raw @@ -45,7 +56,7 @@ QEMU_ARGS += -nographic # QEMU_ARGS += -device loader,file="$(zone0_initrd)",addr=0x1a000000,force-raw=on # QEMU_ARGS += -append "initrd_size=$(shell stat -c%s $(zone0_initrd))" -$(hvisor_bin): elf boot +$(hvisor_bin): elf boot mb2_boot $(OBJCOPY) $(hvisor_elf) --strip-all -O binary $@ cp $(hvisor_elf) $(image_dir)/iso/boot mkdir -p $(image_dir)/iso/boot/kernel @@ -68,12 +79,28 @@ $(hvisor_bin): elf boot echo "Warning: $(zone0_vmlinux) not found, skipping"; \ fi + if [ -f $(zone0_asterinas) ]; then \ + cp $(zone0_asterinas) $(image_dir)/iso/boot/kernel; \ + else \ + echo "Warning: $(zone0_asterinas) not found, skipping"; \ + fi + mkdir -p $(image_dir)/virtdisk + if [ -f $(zone0_initrd) ]; then \ + cp $(zone0_initrd) $(image_dir)/iso/boot/kernel; \ + else \ + echo "Warning: $(zone0_initrd) not found, skipping"; \ + fi + if [ -f $(zone1_mb2_boot) ]; then \ + cp $(zone1_mb2_boot) $(image_dir)/iso/boot/kernel; \ + fi + if command -v xorriso >/dev/null 2>&1; then \ grub-mkrescue /usr/lib/grub/x86_64-efi -o $(image_dir)/virtdisk/hvisor.iso $(image_dir)/iso; \ else \ echo "Warning: xorriso not installed, skipping ISO creation"; \ fi -include $(image_dir)/bootloader/boot.mk \ No newline at end of file +include $(image_dir)/bootloader/boot.mk +include $(image_dir)/mb2_bootloader/mb2_boot.mk diff --git a/src/arch/aarch64/cpu.rs b/src/arch/aarch64/cpu.rs index cc7a43f32..8c3a1c81f 100644 --- a/src/arch/aarch64/cpu.rs +++ b/src/arch/aarch64/cpu.rs @@ -133,6 +133,7 @@ impl ArchCpu { PER_CPU_ARRAY_PTR as VirtAddr + (self.cpuid + 1) as usize * PER_CPU_SIZE } + #[allow(clippy::mut_from_ref)] fn guest_reg(&self) -> &mut GeneralRegisters { unsafe { &mut *((self.stack_top() - 32 * 8) as *mut GeneralRegisters) } } diff --git a/src/arch/x86_64/acpi.rs b/src/arch/x86_64/acpi.rs index 701b5dffb..123b26971 100644 --- a/src/arch/x86_64/acpi.rs +++ b/src/arch/x86_64/acpi.rs @@ -697,6 +697,13 @@ pub fn root_get_config_space_info() -> Option<(usize, usize)> { Some((acpi.config_space_base, acpi.config_space_size)) } +pub fn try_get_cpu_id(apic_id: usize) -> Option { + ROOT_ACPI + .get() + .and_then(|acpi| acpi.apic_id_to_cpu_id.get(&apic_id)) + .copied() +} + fn contains_apic_id(apic_id: usize) -> bool { ROOT_ACPI .get() diff --git a/src/arch/x86_64/boot.rs b/src/arch/x86_64/boot.rs index c3ec46367..e4ef27242 100644 --- a/src/arch/x86_64/boot.rs +++ b/src/arch/x86_64/boot.rs @@ -22,7 +22,10 @@ use crate::{ memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet, PAGE_SIZE}, platform::MEM_TYPE_RESERVED, }; -use alloc::string::{String, ToString}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; use bit_field::BitField; use core::{ arch::{self, global_asm}, @@ -185,7 +188,7 @@ impl BootParams { } as HostPhysAddr; let boot_params = unsafe { &mut *(boot_params_hpa as *mut BootParams) }; - // info!("boot_proto_version: {:x?}", boot_params.boot_proto_version); + info!("boot_proto_version: {:x?}", boot_params.boot_proto_version); if boot_params.boot_proto_version < 0x0204 { panic!("kernel boot protocol version older than 2.04 not supported!"); } @@ -415,7 +418,7 @@ impl BootParams { self.screen_info.red_pos = 16; self.screen_info.alpha_size = 8; self.screen_info.alpha_pos = 24; - self.screen_info.orig_video_is_vga = VIDEO_TYPE_EFI; + self.screen_info.orig_video_is_vga = VIDEO_TYPE_VLFB; self.screen_info.capabilities = 0; self.vid_mode = 0xffff; @@ -534,50 +537,336 @@ pub fn print_memory_map() { } } +/// Construct Multiboot2 info structure in guest memory at `multiboot_info_paddr`. +pub fn multiboot2_info_fill( + config: &HvZoneConfig, + gpm: &mut MemorySet, + multiboot_info_paddr: GuestPhysAddr, +) -> HvResult { + let info_gpa = multiboot_info_paddr; + let (info_hpa, _, _) = unsafe { + gpm.page_table_query(info_gpa) + .expect("multiboot2 info GPA unmapped") + }; + + // Read raw cmdline from guest memory FIRST, before writing tags + // (cmdline is at the same GPA as multiboot info, so writing tags + // would overwrite it) + let mut cmdline_buf = [0u8; 256]; + let cmdline_len = { + let cmdline_gpa = config.arch_config.cmdline_load_gpa as GuestPhysAddr; + if cmdline_gpa != 0 { + if let Ok((cmdline_hpa, _, _)) = unsafe { gpm.page_table_query(cmdline_gpa) } { + let src = cmdline_hpa as *const u8; + let mut len = 0usize; + while len < 255 && unsafe { *src.add(len) } != 0 { + cmdline_buf[len] = unsafe { *src.add(len) }; + len += 1; + } + len + } else { + 0 + } + } else { + 0 + } + }; + + let info_ptr = info_hpa as *mut u8; + + const TAG_END: u32 = 0; + const TAG_CMDLINE: u32 = 1; + const TAG_BASIC_MEMINFO: u32 = 4; + const TAG_MMAP: u32 = 6; + const MB_MEM_AVAILABLE: u32 = 1; + const LOW_MEM_TOP: u64 = 0xC0000000; + const LOW_MMIO_TOP: u64 = 0x100000000; + + let mut offset = 8usize; + + // Tag 1: Basic memory info + let total_ram: u64 = config + .memory_regions() + .iter() + .filter(|r| r.mem_type == MEM_TYPE_RAM) + .map(|r| r.size) + .sum(); + let mem_upper_kb = if total_ram > 0x100000 { + ((total_ram - 0x100000) / 1024) as u32 + } else { + 0 + }; + unsafe { + let t = info_ptr.add(offset); + *(t as *mut u32) = TAG_BASIC_MEMINFO; + *(t.add(4) as *mut u32) = 16; + *(t.add(8) as *mut u32) = 640; + *(t.add(12) as *mut u32) = mem_upper_kb; + } + offset += 16; + + // Tag 2: Memory map. Available RAM is carved around reserved regions so + // Asterinas cannot reclaim the bootloader, kernel, cmdline, info buffer, + // or initramfs pages. + const BOOTLOADER_RESERVED_END: u64 = 0x10000; + + let entry_size = 24usize; + let mmap_hdr = offset; + offset += 16; + let mut entry_count = 0u32; + + let mut reserved: Vec<(u64, u64)> = Vec::new(); + if config.kernel_size > 0 { + let kernel_start = config.kernel_load_paddr; + reserved.push((kernel_start, kernel_start + config.kernel_size)); + } + reserved.push((info_gpa as u64, info_gpa as u64 + PAGE_SIZE as u64)); + if config.arch_config.cmdline_load_gpa != 0 + && config.arch_config.cmdline_load_gpa as u64 != info_gpa as u64 + { + let cmdline_gpa = config.arch_config.cmdline_load_gpa as u64; + reserved.push((cmdline_gpa, cmdline_gpa + PAGE_SIZE as u64)); + } + let initrd_gpa = config.arch_config.initrd_load_gpa as u64; + let initrd_size = config.arch_config.initrd_size as u64; + if initrd_size > 0 { + reserved.push((initrd_gpa, initrd_gpa + initrd_size)); + } + reserved.push((0x8000, BOOTLOADER_RESERVED_END)); + reserved.sort_unstable(); + + let mut available: Vec<(u64, u64)> = Vec::new(); + for r in config + .memory_regions() + .iter() + .filter(|r| r.mem_type == MEM_TYPE_RAM) + { + let start = r.virtual_start; + let end = start + r.size; + let mut cursor = start; + for &(reserved_start, reserved_end) in &reserved { + if reserved_end <= cursor || reserved_start >= end { + continue; + } + let s = reserved_start.max(cursor); + if s > cursor { + available.push((cursor, s.min(end))); + } + cursor = reserved_end.max(cursor); + if cursor >= end { + break; + } + } + if cursor < end { + available.push((cursor, end)); + } + } + + for (start, end) in available { + if start < LOW_MEM_TOP { + let s = start; + let e = end.min(LOW_MEM_TOP); + if e > s { + unsafe { + let p = info_ptr.add(offset); + *(p as *mut u64) = s; + *(p.add(8) as *mut u64) = e - s; + *(p.add(16) as *mut u32) = MB_MEM_AVAILABLE; + *(p.add(20) as *mut u32) = 0; + } + offset += entry_size; + entry_count += 1; + } + } + if end > LOW_MMIO_TOP { + let s = start.max(LOW_MMIO_TOP); + if end > s { + unsafe { + let p = info_ptr.add(offset); + *(p as *mut u64) = s; + *(p.add(8) as *mut u64) = end - s; + *(p.add(16) as *mut u32) = MB_MEM_AVAILABLE; + *(p.add(20) as *mut u32) = 0; + } + offset += entry_size; + entry_count += 1; + } + } + } + + let mmap_tag_size = (16 + entry_count as usize * entry_size) as u32; + unsafe { + *(info_ptr.add(mmap_hdr) as *mut u32) = TAG_MMAP; + *(info_ptr.add(mmap_hdr + 4) as *mut u32) = mmap_tag_size; + *(info_ptr.add(mmap_hdr + 8) as *mut u32) = entry_size as u32; + *(info_ptr.add(mmap_hdr + 12) as *mut u32) = 0; + } + offset = (offset + 7) & !7; + + // Tag 3: Command line (from pre-read buffer) + if cmdline_len > 0 { + let tag_size = ((8 + cmdline_len + 1 + 7) & !7) as u32; + unsafe { + *(info_ptr.add(offset) as *mut u32) = TAG_CMDLINE; + *(info_ptr.add(offset + 4) as *mut u32) = tag_size; + for i in 0..cmdline_len { + *info_ptr.add(offset + 8 + i) = cmdline_buf[i]; + } + *info_ptr.add(offset + 8 + cmdline_len) = 0; + } + offset += tag_size as usize; + } + + // Tag 4: Module (initramfs) + let initrd_gpa = config.arch_config.initrd_load_gpa as u64; + let initrd_size = config.arch_config.initrd_size as u64; + if initrd_size > 0 { + const TAG_MODULE: u32 = 3; + let mod_cmd = b"./initramfs.cpio.gz\0"; + let mod_cmd_len = mod_cmd.len(); + let tag_size = ((8 + 8 + mod_cmd_len + 7) & !7) as u32; + unsafe { + *(info_ptr.add(offset) as *mut u32) = TAG_MODULE; + *(info_ptr.add(offset + 4) as *mut u32) = tag_size; + *(info_ptr.add(offset + 8) as *mut u32) = initrd_gpa as u32; + *(info_ptr.add(offset + 12) as *mut u32) = (initrd_gpa + initrd_size) as u32; + for i in 0..mod_cmd_len { + *info_ptr.add(offset + 16 + i) = mod_cmd[i]; + } + } + offset += tag_size as usize; + } + + // End tag + unsafe { + *(info_ptr.add(offset) as *mut u32) = TAG_END; + *(info_ptr.add(offset + 4) as *mut u32) = 8; + } + offset += 8; + + // Header + unsafe { + *(info_ptr as *mut u32) = offset as u32; + *(info_ptr.add(4) as *mut u32) = 0; + } + + info!( + "Multiboot2 info: {} bytes, {} mmap entries, RAM {:#x}", + offset, entry_count, total_ram + ); + Ok(()) +} + /// copy kernel modules to the right place pub fn module_init(info_addr: usize) { println!("module_init"); + + const MAX_MODULES: usize = 16; + + #[derive(Clone, Copy)] + struct ModuleInfo { + start: usize, + end: usize, + dst: usize, + string_ptr: usize, + } + + let mut modules = [ModuleInfo { + start: 0, + end: 0, + dst: 0, + string_ptr: 0, + }; MAX_MODULES]; + let mut module_count = 0; + let mut cur = info_addr; let total_size = unsafe { *(cur as *const u32) } as usize; - - let mut cnt = 0; cur += 8; - while cur < info_addr + total_size { + while cur < info_addr + total_size && module_count < MAX_MODULES { let tag_type = unsafe { *(cur as *const u32) }; - let ptr = cur as *const multiboot_tag::Modules; - cur += ((unsafe { *((cur + 4) as *const u32) } as usize + 7) & (!7)); - if tag_type == multiboot_tag::END { break; } - if tag_type != multiboot_tag::MODULES { - continue; + if tag_type == multiboot_tag::MODULES { + let ptr = cur as *const multiboot_tag::Modules; + let module = unsafe { *ptr }; + let string_ptr = (ptr as usize) + size_of::(); + modules[module_count] = ModuleInfo { + start: module.mod_start as usize, + end: module.mod_end as usize, + dst: 0, // parse later + string_ptr, + }; + module_count += 1; } + cur += ((unsafe { *((cur + 4) as *const u32) } as usize + 7) & (!7)); + } - let module = unsafe { *ptr }; - let dst = unsafe { - usize::from_str_radix( - CStr::from_ptr(((ptr as usize) + size_of::()) as *const c_char) - .to_str() - .unwrap(), - 16, - ) - .unwrap() - }; - println!("module: {:#x?}, addr: {:#x?}", module, dst); - cnt += 1; + // parse dst + for i in 0..module_count { + let cstr = unsafe { CStr::from_ptr(modules[i].string_ptr as *const c_char) }; + modules[i].dst = usize::from_str_radix(cstr.to_str().unwrap(), 16).unwrap(); + println!( + "module: start={:#x}, end={:#x}, dst={:#x}", + modules[i].start, modules[i].end, modules[i].dst + ); + } - if dst == 0x0 { - continue; + // now move in order + let mut moved = [false; MAX_MODULES]; + let mut moved_count = 0; + while moved_count < module_count { + let mut found = false; + for i in 0..module_count { + if moved[i] { + continue; + } + let dst = modules[i].dst; + let dst_end = dst + (modules[i].end - modules[i].start); + let mut can_move = true; + for j in 0..module_count { + if moved[j] || i == j { + continue; + } + let start = modules[j].start; + let end = modules[j].end; + if dst < end && dst_end > start { + can_move = false; + break; + } + } + if can_move { + if modules[i].dst != 0 { + let size = modules[i].end - modules[i].start + 1; + let dst_end = modules[i].dst + size; + let overlaps_self = + modules[i].dst < modules[i].end && dst_end > modules[i].start; + unsafe { + if overlaps_self { + core::ptr::copy( + modules[i].start as *const u8, + modules[i].dst as *mut u8, + size, + ); + } else { + core::ptr::copy_nonoverlapping( + modules[i].start as *const u8, + modules[i].dst as *mut u8, + size, + ); + } + } + } + moved[i] = true; + moved_count += 1; + found = true; + break; + } + } + if !found { + panic!("Cannot move modules due to overlapping addresses"); } - - unsafe { - core::ptr::copy( - module.mod_start as *mut u8, - dst as *mut u8, - (module.mod_end - module.mod_start + 1) as usize, - ) - }; } - println!("module cnt: {:x}", cnt); + + println!("module cnt: {:x}", module_count); } diff --git a/src/arch/x86_64/cpu.rs b/src/arch/x86_64/cpu.rs index 97d69ccd5..5741dce7e 100644 --- a/src/arch/x86_64/cpu.rs +++ b/src/arch/x86_64/cpu.rs @@ -319,6 +319,13 @@ impl ArchCpu { self.vm_launch_guest_regs.rsi = rsi; } + pub fn set_multiboot_boot_regs(&mut self, multiboot_info_addr: u64, kernel_entry: u64) { + const MULTIBOOT2_MAGIC: u64 = 0x36D76289; + self.vm_launch_guest_regs.rax = MULTIBOOT2_MAGIC; + self.vm_launch_guest_regs.rbx = multiboot_info_addr; + self.vm_launch_guest_regs.rsi = kernel_entry; + } + fn activate_vmx(&mut self) -> HvResult { if self.vmx_on { return Ok(()); @@ -383,9 +390,11 @@ impl ArchCpu { Vmcs::clear(start_paddr)?; Vmcs::load(start_paddr)?; + // Setup VMCS control fields first (includes secondary controls like UNRESTRICTED_GUEST) + // This must be done before setup_vmcs_guest so that guest state is properly initialized + self.setup_vmcs_control()?; self.setup_vmcs_host(&self.host_stack_top as *const _ as usize)?; self.setup_vmcs_guest(entry, ROOT_ZONE_BOOT_STACK)?; - self.setup_vmcs_control()?; Ok(()) } diff --git a/src/arch/x86_64/cpuid.rs b/src/arch/x86_64/cpuid.rs index 989666d55..b76922752 100644 --- a/src/arch/x86_64/cpuid.rs +++ b/src/arch/x86_64/cpuid.rs @@ -21,6 +21,7 @@ pub enum CpuIdEax { VendorInfo = 0x0, FeatureInfo = 0x1, StructuredExtendedFeatureInfo = 0x7, + TscInfo = 0x15, ProcessorFrequencyInfo = 0x16, HypervisorInfo = 0x4000_0000, HypervisorFeatures = 0x4000_0001, diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs index 97f284e50..299aa0cbf 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_64/idt.rs @@ -24,6 +24,7 @@ const VECTOR_CNT: usize = 256; #[allow(non_snake_case)] pub mod IdtVector { + pub const I8042_KEYBOARD_VECTOR: u8 = 0x21; pub const VIRT_IPI_VECTOR: u8 = 0xef; pub const APIC_ERROR_VECTOR: u8 = 0xfc; pub const APIC_SPURIOUS_VECTOR: u8 = 0xfd; diff --git a/src/arch/x86_64/pio.rs b/src/arch/x86_64/pio.rs index 255e76ddb..229d41005 100644 --- a/src/arch/x86_64/pio.rs +++ b/src/arch/x86_64/pio.rs @@ -27,6 +27,7 @@ pub const UART_COM1_BASE_PORT: u16 = 0x3f8; pub const UART_COM1_PORT: Range = 0x3f8..0x400; pub const PCI_CONFIG_ADDR_PORT: Range = 0xcf8..0xcfc; pub const PCI_CONFIG_DATA_PORT: Range = 0xcfc..0xd00; +pub const I8042_PORT: Range = 0x60..0x65; static mut PIO_BITMAP_MAP: Option> = None; @@ -94,7 +95,7 @@ impl PortIoBitmap { // } // i8042, we won't use it, but intercept its ports might block linux init - bitmap.set_range_intercept(0x60..0x65, false); + bitmap.set_range_intercept(I8042_PORT, false); bitmap } diff --git a/src/arch/x86_64/trap.rs b/src/arch/x86_64/trap.rs index 865c8308b..7c7fccd4c 100644 --- a/src/arch/x86_64/trap.rs +++ b/src/arch/x86_64/trap.rs @@ -22,6 +22,7 @@ use crate::{ idt::{IdtStruct, IdtVector}, ipi, msr::Msr::{self, *}, + pio::I8042_PORT, s2pt::Stage2PageFaultInfo, vmcs::*, vmx::{VmxCrAccessInfo, VmxExitInfo, VmxExitReason, VmxInterruptInfo, VmxIoExitInfo}, @@ -102,11 +103,14 @@ pub fn arch_handle_trap(tf: &mut TrapFrame) { } fn handle_irq(vector: u8) { + let is_timer = vector == this_cpu_data().arch_cpu.virt_lapic.virt_timer_vector; + match vector { - IdtVector::VIRT_IPI_VECTOR => { + IdtVector::VIRT_IPI_VECTOR if !is_timer => { ipi::handle_virt_ipi(); } - IdtVector::APIC_SPURIOUS_VECTOR | IdtVector::APIC_ERROR_VECTOR => {} + IdtVector::I8042_KEYBOARD_VECTOR if !is_timer => {} + IdtVector::APIC_SPURIOUS_VECTOR | IdtVector::APIC_ERROR_VECTOR if !is_timer => {} _ => { if vector >= 0x20 && this_cpu_data().vcpu_state.is_running() { inject_vector(this_cpu_id(), vector, None, false); @@ -153,6 +157,12 @@ fn handle_cpuid(arch_cpu: &mut ArchCpu) -> HvResult { res } + CpuIdEax::TscInfo => CpuIdResult { + eax: 1, // Numerator for TSC frequency + ebx: 1, // Denominator for TSC frequency + ecx: hpet::get_tsc_freq_mhz().unwrap_or(0) * 1_000_000, // TSC frequency in Hz + edx: 0, // Reserved, typically 0 + }, CpuIdEax::ProcessorFrequencyInfo => { if let Some(freq_mhz) = hpet::get_tsc_freq_mhz() { CpuIdResult { @@ -215,6 +225,9 @@ fn handle_external_interrupt() -> HvResult { let int_info = VmxInterruptInfo::new()?; trace!("VM-exit: external interrupt: {:#x?}", int_info); assert!(int_info.valid); + if int_info.vector == 0x21 { + // info!("External interrupt: IRQ1 (keyboard)"); + } handle_irq(int_info.vector); Ok(()) } @@ -275,12 +288,14 @@ fn handle_io_instruction(arch_cpu: &mut ArchCpu, exit_info: &VmxExitInfo) -> HvR { handle_pci_config_port_write(&io_info, value); } else if UART_COM1_PORT.contains(&io_info.port) { - virt_console_io_write(io_info.port, value); - } else { - /* info!( - "unhandled port io write {:x} value: {:x}", - io_info.port, value - ); */ + if this_zone_id() == 0 { + virt_console_io_write(io_info.port, value); + } else { + virt_console_io_write(io_info.port, value); + // info!("zone1 uart write from {:x}: {:x}", io_info.port, value); + } + } else if I8042_PORT.contains(&io_info.port) { + // info!("unhandled port io write {:x} value: {:x}", io_info.port, value); } } else { if PCI_CONFIG_ADDR_PORT.contains(&io_info.port) @@ -288,7 +303,15 @@ fn handle_io_instruction(arch_cpu: &mut ArchCpu, exit_info: &VmxExitInfo) -> HvR { value = handle_pci_config_port_read(&io_info); } else if UART_COM1_PORT.contains(&io_info.port) { - value = virt_console_io_read(io_info.port); + if this_zone_id() == 0 { + value = virt_console_io_read(io_info.port); + } else { + value = 0xff; + value = virt_console_io_read(io_info.port); + // info!("zone1 uart read from {:x}: {:x}", io_info.port, value); + } + } else if I8042_PORT.contains(&io_info.port) { + value = 0xff; } else { // info!("unhandled port io read {:x}", io_info.port); value = 0x0; diff --git a/src/arch/x86_64/zone.rs b/src/arch/x86_64/zone.rs index d671fe515..6c4e53d4b 100644 --- a/src/arch/x86_64/zone.rs +++ b/src/arch/x86_64/zone.rs @@ -22,7 +22,7 @@ use crate::{ error::HvResult, memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, platform::MEM_TYPE_RESERVED, - zone::Zone, + zone::{zone_boot_mode, Zone}, }; use alloc::vec::Vec; @@ -80,6 +80,10 @@ impl Zone { )); } MEM_TYPE_VIRTIO => { + info!( + "Registering virtio mmio region: physical_start: {:#x}, size: {:#x}", + mem_region.physical_start, mem_region.size + ); inner.mmio_region_register( mem_region.physical_start as _, mem_region.size as _, @@ -101,17 +105,34 @@ impl Zone { /// called after cpu_set is initialized pub fn arch_zone_pre_configuration(&mut self, config: &HvZoneConfig) -> HvResult { + let zone_id = config.zone_id as usize; let inner = self.read(); - inner.cpu_set().iter().for_each(|cpuid| { - let cpu_data = get_cpu_data(cpuid); - // boot cpu - if cpuid == inner.cpu_set().first_cpu().unwrap() { - cpu_data.arch_cpu.set_boot_cpu_vm_launch_regs( - config.arch_config.kernel_entry_gpa as _, - config.arch_config.setup_load_gpa as _, - ); - } - }); + let boot_mode = zone_boot_mode(zone_id); + + if zone_id != 0 && boot_mode.multiboot_enabled != 0 { + info!("[ZONE{}] Using Multiboot2 boot mode", zone_id); + + inner.cpu_set().iter().for_each(|cpuid| { + let cpu_data = get_cpu_data(cpuid); + if cpuid == inner.cpu_set().first_cpu().unwrap() { + cpu_data.arch_cpu.set_multiboot_boot_regs( + boot_mode.multiboot_info_paddr as _, + config.arch_config.kernel_entry_gpa as _, + ); + } + }); + } else { + inner.cpu_set().iter().for_each(|cpuid| { + let cpu_data = get_cpu_data(cpuid); + // boot cpu + if cpuid == inner.cpu_set().first_cpu().unwrap() { + cpu_data.arch_cpu.set_boot_cpu_vm_launch_regs( + config.arch_config.kernel_entry_gpa as _, + config.arch_config.setup_load_gpa as _, + ); + } + }); + } drop(inner); set_msr_bitmap(config.zone_id as _); @@ -148,9 +169,17 @@ impl Zone { self.pci_bars_register(&config.pci_config); }*/ - boot::BootParams::fill(&config, inner.gpm_mut()); + let boot_mode = zone_boot_mode(self.id()); + if boot_mode.multiboot_enabled != 0 { + boot::multiboot2_info_fill( + config, + inner.gpm_mut(), + boot_mode.multiboot_info_paddr as _, + )?; + } else { + boot::BootParams::fill(&config, inner.gpm_mut()); + } acpi::copy_to_guest_memory_region(&config, &inner.cpu_set()); - Ok(()) } diff --git a/src/config.rs b/src/config.rs index 4f7901602..b3c556486 100644 --- a/src/config.rs +++ b/src/config.rs @@ -108,6 +108,14 @@ pub struct HvZoneConfig { pub alloc_pci_devs: [HvPciDevConfig; CONFIG_MAX_PCI_DEV], } +#[repr(C)] +#[derive(Debug, Clone, Copy, Default)] +pub struct HvZoneBootMode { + pub zone_id: u32, + pub multiboot_enabled: u32, + pub multiboot_info_paddr: u64, +} + impl HvZoneConfig { pub fn new( zone_id: u32, diff --git a/src/device/iommu/arm_smmu/smmu_hw.rs b/src/device/iommu/arm_smmu/smmu_hw.rs index 8081b75e1..4b431898a 100644 --- a/src/device/iommu/arm_smmu/smmu_hw.rs +++ b/src/device/iommu/arm_smmu/smmu_hw.rs @@ -194,6 +194,7 @@ impl LinearStreamTable { self.sid_max_bits } + #[allow(clippy::mut_from_ref)] fn ste(&self, sid: usize) -> &mut StreamTableEntry { let base = self.base + sid * STRTAB_STE_SIZE; unsafe { &mut *(base as *mut StreamTableEntry) } diff --git a/src/device/irqchip/pic/ioapic.rs b/src/device/irqchip/pic/ioapic.rs index 75c7b1607..4286698f1 100644 --- a/src/device/irqchip/pic/ioapic.rs +++ b/src/device/irqchip/pic/ioapic.rs @@ -16,12 +16,13 @@ use crate::{ arch::{ - acpi::{get_apic_id, get_cpu_id}, - cpu::this_cpu_id, + acpi::try_get_cpu_id, + cpu::{this_apic_id, this_cpu_id}, idt, ipi, mmio::MMIoDevice, zone::HvArchZoneConfig, }, + cpu_data::this_zone, device::irqchip::pic::inject_vector, error::HvResult, memory::{GuestPhysAddr, MMIOAccess}, @@ -95,6 +96,11 @@ impl VirtIoApic { mut reg => { reg -= IoApicReg::TABLE_BASE; let index = (reg >> 1) as usize; + // info!("ioapic read index: {:x}", index); + if this_zone_id() != 0 && index == 4 { + //FIXME: we do not allow non-root to use uart interrupt + return Ok(0xffff_ffff_ffff_ffff); + } if let Some(entry) = inner.rte.get(index) { if reg % 2 == 0 { Ok((*entry).get_bits(0..=31)) @@ -132,7 +138,26 @@ impl VirtIoApic { if reg % 2 == 0 { entry.set_bits(0..=31, value.get_bits(0..=31)); } else { - entry.set_bits(32..=63, value.get_bits(0..=31)); + let original_dest = value.get_bits(24..=31); + if let Some(dest_cpu) = try_get_cpu_id(original_dest as usize) { + if this_zone().read().cpu_set().contains_cpu(dest_cpu) { + entry.set_bits(56..=63, original_dest); + } else { + let dest = this_apic_id() as u64; + info!( + "redirect irq {:x} to cpu {:x} in another zone! entry: {:x?}", + index, dest, *entry + ); + entry.set_bits(56..=63, dest); + } + } else { + let dest = this_apic_id() as u64; + info!( + "redirect irq {:x} to cpu {:x} in another zone, unknown dest {:x}! entry: {:x?}", + index, dest, original_dest, *entry + ); + entry.set_bits(56..=63, dest); + } /*if zone_id == 0 { // info!("1 write {:x} entry: {:x?}", index, *entry); @@ -153,7 +178,7 @@ impl VirtIoApic { fn get_irq_cpu(&self, irq: usize, zone_id: usize) -> Option { let ioapic = self.inner.get(zone_id).unwrap(); if let Some(entry) = ioapic.lock().rte.get(irq) { - let dest = get_cpu_id(entry.get_bits(56..=63) as usize); + let dest = try_get_cpu_id(entry.get_bits(56..=63) as usize).unwrap_or_else(this_cpu_id); return Some(dest); } None @@ -164,10 +189,9 @@ impl VirtIoApic { let ioapic = self.inner.get(zone_id).unwrap(); if let Some(entry) = ioapic.lock().rte.get(irq) { // TODO: physical & logical mode - let dest = get_cpu_id(entry.get_bits(56..=63) as usize); + let dest = try_get_cpu_id(entry.get_bits(56..=63) as usize).unwrap_or_else(this_cpu_id); let masked = entry.get_bit(16); let vector = entry.get_bits(0..=7) as u8; - // info!("trigger hv: {:x} zone: {:x}", vector, zone_id); if !masked && vector >= 0x20 { inject_vector(dest, vector, None, allow_repeat); } diff --git a/src/device/irqchip/pic/lapic.rs b/src/device/irqchip/pic/lapic.rs index 8ba43c182..2ffd96742 100644 --- a/src/device/irqchip/pic/lapic.rs +++ b/src/device/irqchip/pic/lapic.rs @@ -28,7 +28,8 @@ use crate::{ }; use bit_field::BitField; use core::{ops::Range, u32}; -use x2apic::lapic::{LocalApic, LocalApicBuilder, TimerMode}; +use x2apic::lapic::{LocalApic, LocalApicBuilder}; +use x86::msr::wrmsr; pub struct VirtLocalApic { pub phys_lapic: LocalApic, @@ -96,7 +97,6 @@ impl VirtLocalApic { pub fn wrmsr(&mut self, msr: Msr, value: u64) -> HvResult { match msr { IA32_X2APIC_EOI => { - // info!("eoi"); pop_vector(this_cpu_id()); Ok(()) } @@ -106,28 +106,12 @@ impl VirtLocalApic { Ok(()) } IA32_X2APIC_LVT_TIMER => { + let value = value & 0xffff_ffff; + let vector = value.get_bits(0..=7); self.virt_lvt_timer_bits = value as u32; - let timer = value.get_bits(0..=7) as u8; - if timer != self.virt_timer_vector { - self.virt_timer_vector = timer; - self.phys_lapic = Self::new_phys_lapic( - timer as _, - IdtVector::APIC_ERROR_VECTOR as _, - IdtVector::APIC_SPURIOUS_VECTOR as _, - ) - } + self.virt_timer_vector = vector as u8; unsafe { - self.phys_lapic - .set_timer_mode(match value.get_bits(17..19) { - 0 => TimerMode::OneShot, - 1 => TimerMode::Periodic, - _ => TimerMode::TscDeadline, - }); - if value.get_bit(16) { - self.phys_lapic.disable_timer(); - } else { - self.phys_lapic.enable_timer(); - } + wrmsr(IA32_X2APIC_LVT_TIMER as u32, value); } Ok(()) } diff --git a/src/device/virtio_trampoline.rs b/src/device/virtio_trampoline.rs index dfa2e6a4b..4d4af6bbf 100644 --- a/src/device/virtio_trampoline.rs +++ b/src/device/virtio_trampoline.rs @@ -255,6 +255,7 @@ struct ReqAgent<'a> { } impl<'a> ReqAgent<'a> { + #[allow(clippy::mut_from_ref)] fn region(&self) -> &mut VirtioBridge { unsafe { &mut *(self.base as *mut VirtioBridge) } } @@ -294,6 +295,7 @@ pub struct ResAgent<'a> { } impl<'a> ResAgent<'a> { + #[allow(clippy::mut_from_ref)] fn region(&self) -> &mut VirtioBridge { unsafe { &mut *(self.base as *mut VirtioBridge) } } diff --git a/src/hypercall/mod.rs b/src/hypercall/mod.rs index ca4f33890..6ed59088f 100644 --- a/src/hypercall/mod.rs +++ b/src/hypercall/mod.rs @@ -17,7 +17,7 @@ #![allow(unreachable_patterns)] use crate::arch::cpu::get_target_cpu; -use crate::config::HvZoneConfig; +use crate::config::{HvZoneBootMode, HvZoneConfig}; use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM, MAX_WAIT_TIMES, PAGE_SIZE}; use crate::cpu_data::{get_cpu_data, PerCpu, VcpuState}; use crate::device::virtio_trampoline::{ @@ -27,7 +27,8 @@ use crate::device::virtio_trampoline::{ use crate::error::HvResult; use crate::pci::pci_config::GLOBAL_PCIE_LIST; use crate::zone::{ - add_zone, all_zones_info, find_zone, is_this_root_zone, remove_zone, zone_create, ZoneInfo, + add_zone, all_zones_info, clear_zone_boot_mode, find_zone, is_this_root_zone, remove_zone, + set_zone_boot_mode, zone_create, ZoneInfo, }; use crate::event::{ @@ -51,6 +52,7 @@ numeric_enum! { HvIvcInfo = 5, HvConfigCheck = 6, HvVirtioPCI = 7, + HvZoneSetBootMode = 8, } } pub const SGI_IPI_ID: u64 = 7; @@ -102,6 +104,7 @@ impl<'a> HyperCall<'a> { HyperCallCode::HvIvcInfo => self.hv_ivc_info(arg0), HyperCallCode::HvConfigCheck => self.hv_zone_config_check(arg0 as *mut u64), HyperCallCode::HvVirtioPCI => self.hv_virtio_pci(arg0, arg1), + HyperCallCode::HvZoneSetBootMode => self.hv_zone_set_boot_mode(arg0, arg1), _ => { warn!("hypercall id={} unsupported!", code as u64); Ok(0) @@ -179,13 +182,48 @@ impl<'a> HyperCall<'a> { HyperCallResult::Ok(0) } + pub fn hv_zone_set_boot_mode( + &mut self, + boot_mode_ipa: u64, + boot_mode_size: u64, + ) -> HyperCallResult { + if !is_this_root_zone() { + return hv_result_err!( + EPERM, + "Set zone boot mode over non-root zones: unsupported!" + ); + } + if boot_mode_size != core::mem::size_of::() as _ { + return hv_result_err!( + EINVAL, + format!( + "hv_zone_set_boot_mode: config size should be {} bytes, but got {}", + core::mem::size_of::(), + boot_mode_size + ) + ); + } + + let boot_mode_pa = self.hv_get_real_pa(boot_mode_ipa); + let boot_mode = unsafe { &*(boot_mode_pa as *const HvZoneBootMode) }; + if boot_mode.zone_id == 0 { + return hv_result_err!(EINVAL, "boot mode is not supported for the root zone"); + } + + set_zone_boot_mode(boot_mode.zone_id as usize, *boot_mode); + info!( + "hv_zone_set_boot_mode: zone_id={}, multiboot_enabled={}, info_paddr={:#x}", + boot_mode.zone_id, boot_mode.multiboot_enabled, boot_mode.multiboot_info_paddr + ); + HyperCallResult::Ok(0) + } + pub fn hv_zone_start(&mut self, config: &HvZoneConfig, config_size: u64) -> HyperCallResult { let config_ipa = config as *const HvZoneConfig as u64; let config_pa = self.hv_get_real_pa(config_ipa); let config = unsafe { &*(config_pa as *const HvZoneConfig) }; info!("hv_zone_start: config: {:#x?},pa:{:#x}", config, config_pa); - // return HyperCallResult::Ok(0); if !is_this_root_zone() { return hv_result_err!( EPERM, @@ -202,7 +240,9 @@ impl<'a> HyperCall<'a> { ) ); } - let zone = zone_create(config)?; + let zone_result = zone_create(config); + clear_zone_boot_mode(config.zone_id as usize); + let zone = zone_result?; let boot_cpu = zone.read().cpu_set().first_cpu().unwrap(); let target_data = get_cpu_data(boot_cpu as _); diff --git a/src/memory/mapper.rs b/src/memory/mapper.rs index 0b391bfed..42556b633 100644 --- a/src/memory/mapper.rs +++ b/src/memory/mapper.rs @@ -54,7 +54,6 @@ impl + Into + Copy> MemoryRegion { flags: MemFlags, ) -> Self { let start_vaddr = start_vaddr.into(); - let start_paddr = start_paddr; // bug: vaddr > paddr? let phys_virt_offset = start_vaddr.wrapping_sub(start_paddr); Self::new( diff --git a/src/zone.rs b/src/zone.rs index 1e2efefac..b487333d9 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -16,9 +16,9 @@ use alloc::sync::Arc; use alloc::vec::Vec; // use psci::error::INVALID_ADDRESS; -use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM}; +use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM, MAX_ZONE_NUM}; use crate::pci::pci_struct::VirtualRootComplex; -use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use spin::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; #[cfg(dwc_pcie)] use crate::pci::{config_accessors::dwc_atu::AtuConfig, PciConfigAddress}; @@ -27,7 +27,7 @@ use alloc::collections::btree_map::BTreeMap; use crate::arch::mm::new_s2_memory_set; use crate::arch::s2pt::Stage2PageTable; -use crate::config::{HvZoneConfig, CONFIG_NAME_MAXLEN}; +use crate::config::{HvZoneBootMode, HvZoneConfig, CONFIG_NAME_MAXLEN}; use crate::cpu_data::{get_cpu_data, this_zone, CpuSet}; use crate::error::HvResult; @@ -765,6 +765,29 @@ impl ZoneInner { } static ZONE_LIST: RwLock>> = RwLock::new(vec![]); +static ZONE_BOOT_MODES: Mutex<[Option; MAX_ZONE_NUM]> = + Mutex::new([None; MAX_ZONE_NUM]); + +pub fn set_zone_boot_mode(zone_id: usize, mode: HvZoneBootMode) { + if let Some(slot) = ZONE_BOOT_MODES.lock().get_mut(zone_id) { + *slot = Some(mode); + } +} + +pub fn zone_boot_mode(zone_id: usize) -> HvZoneBootMode { + ZONE_BOOT_MODES + .lock() + .get(zone_id) + .copied() + .flatten() + .unwrap_or_default() +} + +pub fn clear_zone_boot_mode(zone_id: usize) { + if let Some(slot) = ZONE_BOOT_MODES.lock().get_mut(zone_id) { + *slot = None; + } +} pub fn root_zone() -> Arc { ZONE_LIST.read().get(0).cloned().unwrap()