diff --git a/Makefile b/Makefile index b176588ac..399b9a0ab 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ AUTO_SYSCALL_TEST ?= 0 BUILD_SYSCALL_TEST ?= 0 EMULATE_IOMMU ?= 0 +RUN_MICROVM ?= 0 ENABLE_KVM ?= 1 INTEL_TDX ?= 0 # End of Make arguments @@ -28,6 +29,10 @@ ifeq ($(EMULATE_IOMMU), 1) CARGO_KRUN_ARGS += --emulate-iommu endif +ifeq ($(RUN_MICROVM), 1) +CARGO_KRUN_ARGS += --run-microvm +endif + ifeq ($(AUTO_SYSCALL_TEST), 1) BUILD_SYSCALL_TEST := 1 endif diff --git a/build/src/machine/default.rs b/build/src/machine/default.rs new file mode 100644 index 000000000..b2dd348bc --- /dev/null +++ b/build/src/machine/default.rs @@ -0,0 +1,88 @@ +use std::{ + fs, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +pub const MACHINE_ARGS: &[&str] = &["-machine", "q35,kernel-irqchip=split"]; + +pub const NOIOMMU_DEVICE_ARGS: &[&str] = &[ + "-device", + "virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off", + "-device", + "virtio-keyboard-pci,disable-legacy=on,disable-modern=off", + "-device", + "virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off", +]; + +pub const IOMMU_DEVICE_ARGS: &[&str] = &[ + "-device", + "virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device", + "virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device", + "virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device", + "intel-iommu,intremap=on,device-iotlb=on", + "-device", + "ioh3420,id=pcie.0,chassis=1", +]; + +pub fn create_bootdev_image(path: PathBuf, kcmdline: &str) -> String { + let dir = path.parent().unwrap(); + let name = path.file_name().unwrap().to_str().unwrap().to_string(); + let iso_path = dir.join(name + ".iso").to_str().unwrap().to_string(); + + // Clean up the image directory + if Path::new("target/iso_root").exists() { + fs::remove_dir_all("target/iso_root").unwrap(); + } + + // Copy the needed files into an ISO image. + fs::create_dir_all("target/iso_root/boot/grub").unwrap(); + + fs::copy(path.as_os_str(), "target/iso_root/boot/jinux").unwrap(); + generate_grub_cfg( + "build/grub/grub.cfg.template", + "target/iso_root/boot/grub/grub.cfg", + kcmdline, + ); + fs::copy( + "regression/build/initramfs.cpio.gz", + "target/iso_root/boot/initramfs.cpio.gz", + ) + .unwrap(); + + // Make the boot device .iso image + let status = std::process::Command::new("grub-mkrescue") + .arg("-o") + .arg(&iso_path) + .arg("target/iso_root") + .status() + .unwrap(); + + if !status.success() { + panic!("Failed to create boot iso image.") + } + + iso_path +} + +fn generate_grub_cfg(template_filename: &str, target_filename: &str, kcmdline: &str) { + let mut buffer = String::new(); + + // Read the contents of the file + fs::File::open(template_filename) + .unwrap() + .read_to_string(&mut buffer) + .unwrap(); + + // Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value + let replaced_content = buffer.replace("#KERNEL_COMMAND_LINE#", kcmdline); + + // Write the modified content back to the file + fs::File::create(target_filename) + .unwrap() + .write_all(replaced_content.as_bytes()) + .unwrap(); +} diff --git a/build/src/machine/microvm.rs b/build/src/machine/microvm.rs new file mode 100644 index 000000000..8359329ac --- /dev/null +++ b/build/src/machine/microvm.rs @@ -0,0 +1,74 @@ +use std::{ + fs::OpenOptions, + io::{Seek, SeekFrom, Write}, + path::PathBuf, + process::Command, +}; + +pub const MACHINE_ARGS: &[&str] = &[ + "-machine", + "microvm,pit=on,pic=off,rtc=on", + "-nodefaults", + "-no-user-config", +]; + +pub const DEVICE_ARGS: &[&str] = &[ + "-device", + "virtio-blk-device,drive=x0", + "-device", + "virtio-keyboard-device", + "-device", + "virtio-net-device,netdev=net01", +]; + +pub fn create_bootdev_image(path: PathBuf) -> String { + let dir = path.parent().unwrap(); + let name = path.file_name().unwrap().to_str().unwrap().to_string(); + let elf_path = dir.join(name.clone()).to_str().unwrap().to_string(); + let strip_elf_path = dir + .join(name.clone() + ".stripped.elf") + .to_str() + .unwrap() + .to_string(); + + // We use rust-strip to reduce the kernel image size. + let status = Command::new("rust-strip") + .arg(&elf_path) + .arg("-o") + .arg(&strip_elf_path) + .status(); + + match status { + Ok(status) => { + if !status.success() { + panic!("Failed to strip kernel elf."); + } + } + Err(err) => match err.kind() { + std::io::ErrorKind::NotFound => panic!( + "Not find rust-strip command, + try `cargo install cargo-binutils` and then rerun." + ), + _ => panic!("Strip kernel elf failed, err:{:#?}", err), + }, + } + + // Because QEMU denies a x86_64 multiboot ELF file (GRUB2 accept it, btw), + // modify `em_machine` to pretend to be an x86 (32-bit) ELF image, + // + // https://github.com/qemu/qemu/blob/950c4e6c94b15cd0d8b63891dddd7a8dbf458e6a/hw/i386/multiboot.c#L197 + // Set EM_386 (0x0003) to em_machine. + let mut file = OpenOptions::new() + .read(true) + .write(true) + .open(&strip_elf_path) + .unwrap(); + + let bytes: [u8; 2] = [0x03, 0x00]; + + file.seek(SeekFrom::Start(18)).unwrap(); + file.write_all(&bytes).unwrap(); + file.flush().unwrap(); + + strip_elf_path +} diff --git a/build/src/machine/mod.rs b/build/src/machine/mod.rs new file mode 100644 index 000000000..96f90c775 --- /dev/null +++ b/build/src/machine/mod.rs @@ -0,0 +1,2 @@ +pub mod default; +pub mod microvm; diff --git a/build/src/main.rs b/build/src/main.rs index e4548ddf0..20b599ac9 100644 --- a/build/src/main.rs +++ b/build/src/main.rs @@ -6,14 +6,17 @@ //! boot device image. Then it invokes QEMU to boot Jinux. //! +pub mod machine; + use std::{ - fs::{self, OpenOptions}, - io::{Read, Write}, + fs::OpenOptions, path::{Path, PathBuf}, process::Command, }; -use clap::{builder::Str, Parser}; +use clap::Parser; + +use crate::machine::{default, microvm}; /// The CLI of this runner. #[derive(Parser, Debug)] @@ -35,12 +38,14 @@ struct Args { /// Emulate Intel IOMMU by QEMU. #[arg(long, default_value_t = false)] emulate_iommu: bool, + + /// Run Jinux as microvm mode. + #[arg(long, default_value_t = false)] + run_microvm: bool, } -const COMMON_ARGS: &[&str] = &[ +pub const COMMON_ARGS: &[&str] = &[ "--no-reboot", - "-machine", - "q35,kernel-irqchip=split", "-cpu", "Icelake-Server,+x2apic", "-m", @@ -60,54 +65,52 @@ const COMMON_ARGS: &[&str] = &[ "filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", ]; -const COMMON_DEVICE_ARGS: &[&str] = &[ - "-device", - "virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off", - "-device", - "virtio-keyboard-pci,disable-legacy=on,disable-modern=off", - "-device", - "virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off", -]; - -const IOMMU_DEVICE_ARGS: &[&str] = &[ - "-device", - "virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", - "-device", - "virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", - "-device", - "virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", - "-device", - "intel-iommu,intremap=on,device-iotlb=on", - "-device", - "ioh3420,id=pcie.0,chassis=1", -]; - fn main() { let args = Args::parse(); let mut qemu_cmd = Command::new("qemu-system-x86_64"); - let mut qemu_args = COMMON_ARGS.clone().to_vec(); + let mut qemu_args = COMMON_ARGS.to_vec(); + if args.enable_kvm { qemu_args.push("-enable-kvm"); } - if args.emulate_iommu { - qemu_args.extend(IOMMU_DEVICE_ARGS.clone().to_vec().iter()); + // Specify machine type + if args.run_microvm { + qemu_args.extend(microvm::MACHINE_ARGS.to_vec().iter()); } else { - qemu_args.extend(COMMON_DEVICE_ARGS.clone().to_vec().iter()); + qemu_args.extend(default::MACHINE_ARGS.to_vec().iter()); + } + // Add device arguments + if args.run_microvm { + qemu_args.extend(microvm::DEVICE_ARGS.to_vec().iter()) + } else if args.emulate_iommu { + qemu_args.extend(default::IOMMU_DEVICE_ARGS.to_vec().iter()); + } else { + qemu_args.extend(default::NOIOMMU_DEVICE_ARGS.to_vec().iter()); } let fs_image = create_fs_image(args.path.as_path()); qemu_args.push("-drive"); qemu_args.push(fs_image.as_str()); - let bootdev_image = create_bootdev_image(args.path, &args.kcmdline); - qemu_cmd.arg("-cdrom"); - qemu_cmd.arg(bootdev_image.as_str()); + if args.run_microvm { + let image = microvm::create_bootdev_image(args.path); + qemu_cmd.arg("-kernel"); + qemu_cmd.arg(image.as_str()); + qemu_cmd.arg("-append"); + qemu_cmd.arg(&args.kcmdline); + qemu_cmd.arg("-initrd"); + qemu_cmd.arg("regression/build/initramfs.cpio.gz"); + } else { + let bootdev_image = default::create_bootdev_image(args.path, &args.kcmdline); + qemu_cmd.arg("-cdrom"); + qemu_cmd.arg(bootdev_image.as_str()); + } qemu_cmd.args(qemu_args); - println!("running:{:?}", qemu_cmd); + println!("running:{:#?}", qemu_cmd); let exit_status = qemu_cmd.status().unwrap(); if !exit_status.success() { @@ -122,66 +125,7 @@ fn main() { } } -fn generate_grub_cfg(template_filename: &str, target_filename: &str, kcmdline: &str) { - let mut buffer = String::new(); - - // Read the contents of the file - fs::File::open(template_filename) - .unwrap() - .read_to_string(&mut buffer) - .unwrap(); - - // Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value - let replaced_content = buffer.replace("#KERNEL_COMMAND_LINE#", kcmdline); - - // Write the modified content back to the file - fs::File::create(target_filename) - .unwrap() - .write_all(replaced_content.as_bytes()) - .unwrap(); -} - -fn create_bootdev_image(path: PathBuf, kcmdline: &str) -> String { - let dir = path.parent().unwrap(); - let name = path.file_name().unwrap().to_str().unwrap().to_string(); - let iso_path = dir.join(name + ".iso").to_str().unwrap().to_string(); - - // Clean up the image directory - if Path::new("target/iso_root").exists() { - fs::remove_dir_all("target/iso_root").unwrap(); - } - - // Copy the needed files into an ISO image. - fs::create_dir_all("target/iso_root/boot/grub").unwrap(); - - fs::copy(path.as_os_str(), "target/iso_root/boot/jinux").unwrap(); - generate_grub_cfg( - "build/grub/grub.cfg.template", - "target/iso_root/boot/grub/grub.cfg", - kcmdline, - ); - fs::copy( - "regression/build/initramfs.cpio.gz", - "target/iso_root/boot/initramfs.cpio.gz", - ) - .unwrap(); - - // Make the boot device .iso image - let status = std::process::Command::new("grub-mkrescue") - .arg("-o") - .arg(&iso_path) - .arg("target/iso_root") - .status() - .unwrap(); - - if !status.success() { - panic!("Failed to create boot iso image.") - } - - iso_path -} - -fn create_fs_image(path: &Path) -> String { +pub fn create_fs_image(path: &Path) -> String { let mut fs_img_path = path.parent().unwrap().to_str().unwrap().to_string(); fs_img_path.push_str("/fs.img"); let path = Path::new(fs_img_path.as_str()); diff --git a/framework/jinux-frame/src/arch/x86/device/cmos.rs b/framework/jinux-frame/src/arch/x86/device/cmos.rs index 0d2867caf..891616918 100644 --- a/framework/jinux-frame/src/arch/x86/device/cmos.rs +++ b/framework/jinux-frame/src/arch/x86/device/cmos.rs @@ -8,10 +8,9 @@ use super::io_port::IoPort; pub static CMOS_ADDRESS: IoPort = unsafe { IoPort::new(0x70) }; pub static CMOS_DATA: IoPort = unsafe { IoPort::new(0x71) }; -pub fn get_century() -> u8 { - const DEFAULT_21_CENTURY: u8 = 50; +pub fn get_century_register() -> Option { if !ACPI_TABLES.is_completed() { - return DEFAULT_21_CENTURY; + return None; } unsafe { match ACPI_TABLES @@ -20,15 +19,8 @@ pub fn get_century() -> u8 { .lock() .get_sdt::(Signature::FADT) { - Ok(a) => { - let century = a.unwrap().century; - if century == 0 { - DEFAULT_21_CENTURY - } else { - century - } - } - Err(er) => DEFAULT_21_CENTURY, + Ok(a) => Some(a.unwrap().century), + Err(er) => None, } } } diff --git a/services/comps/time/src/lib.rs b/services/comps/time/src/lib.rs index ab23b034f..bb9c69c4d 100644 --- a/services/comps/time/src/lib.rs +++ b/services/comps/time/src/lib.rs @@ -48,7 +48,6 @@ impl SystemTime { self.year = get_cmos(0x09) as u16; let century_register = CENTURY_REGISTER.load(Relaxed); - if century_register != 0 { self.century = get_cmos(century_register); } @@ -58,7 +57,6 @@ impl SystemTime { /// ref:https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers pub(crate) fn convert_bcd_to_binary(&mut self, register_b: u8) { if register_b & 0x04 == 0 { - let century_register = CENTURY_REGISTER.load(Relaxed); self.second = (self.second & 0x0F) + ((self.second / 16) * 10); self.minute = (self.minute & 0x0F) + ((self.minute / 16) * 10); self.hour = @@ -66,8 +64,12 @@ impl SystemTime { self.day = (self.day & 0x0F) + ((self.day / 16) * 10); self.month = (self.month & 0x0F) + ((self.month / 16) * 10); self.year = (self.year & 0x0F) + ((self.year / 16) * 10); - if century_register != 0 { + if CENTURY_REGISTER.load(Relaxed) != 0 { self.century = (self.century & 0x0F) + ((self.century / 16) * 10); + } else { + // 2000 ~ 2099 + const DEFAULT_21_CENTURY: u8 = 20; + self.century = DEFAULT_21_CENTURY; } } } @@ -82,12 +84,7 @@ impl SystemTime { /// convert raw year (10, 20 etc.) to real year (2010, 2020 etc.) pub(crate) fn modify_year(&mut self) { - let century_register = CENTURY_REGISTER.load(Relaxed); - if century_register != 0 { - self.year += self.century as u16 * 100; - } else { - panic!("century register not exists"); - } + self.year += self.century as u16 * 100; } } diff --git a/services/comps/time/src/rtc.rs b/services/comps/time/src/rtc.rs index dfa44744f..45924da8b 100644 --- a/services/comps/time/src/rtc.rs +++ b/services/comps/time/src/rtc.rs @@ -3,7 +3,7 @@ use core::sync::atomic::Ordering::Relaxed; use crate::SystemTime; -use jinux_frame::arch::x86::device::cmos::{get_century, CMOS_ADDRESS, CMOS_DATA}; +use jinux_frame::arch::x86::device::cmos::{get_century_register, CMOS_ADDRESS, CMOS_DATA}; use jinux_frame::sync::Mutex; pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0); @@ -11,7 +11,10 @@ pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0); static READ_TIME: Mutex = Mutex::new(SystemTime::zero()); pub fn init() { - CENTURY_REGISTER.store(get_century(), Relaxed); + let Some(century_register) = get_century_register() else { + return; + }; + CENTURY_REGISTER.store(century_register, Relaxed); } pub fn get_cmos(reg: u8) -> u8 {