mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-25 18:33:24 +00:00
Support microvm boot option
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
66a8b404c2
commit
d9fcb486c0
5
Makefile
5
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
|
||||
|
88
build/src/machine/default.rs
Normal file
88
build/src/machine/default.rs
Normal file
@ -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();
|
||||
}
|
74
build/src/machine/microvm.rs
Normal file
74
build/src/machine/microvm.rs
Normal file
@ -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
|
||||
}
|
2
build/src/machine/mod.rs
Normal file
2
build/src/machine/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod default;
|
||||
pub mod microvm;
|
@ -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);
|
||||
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());
|
||||
|
@ -8,10 +8,9 @@ use super::io_port::IoPort;
|
||||
pub static CMOS_ADDRESS: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x70) };
|
||||
pub static CMOS_DATA: IoPort<u8, ReadOnlyAccess> = unsafe { IoPort::new(0x71) };
|
||||
|
||||
pub fn get_century() -> u8 {
|
||||
const DEFAULT_21_CENTURY: u8 = 50;
|
||||
pub fn get_century_register() -> Option<u8> {
|
||||
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::<Fadt>(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<SystemTime> = 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 {
|
||||
|
Reference in New Issue
Block a user