mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Bring both EFI and legacy to test
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
487e0cdd15
commit
432f0c34b0
21
.github/workflows/integration_test.yml
vendored
21
.github/workflows/integration_test.yml
vendored
@ -28,22 +28,23 @@ jobs:
|
||||
id: boot_test_microvm
|
||||
run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1
|
||||
|
||||
- name: Boot Test (Linux Boot Protocol)
|
||||
id: boot_test_linux
|
||||
run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_PROTOCOL=linux RELEASE_MODE=1
|
||||
- name: Boot Test (Linux Legacy 32-bit Boot Protocol)
|
||||
id: boot_test_linux_legacy32
|
||||
run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_PROTOCOL=linux-legacy32 RELEASE_MODE=1
|
||||
|
||||
- name: Syscall Test (MicroVM)
|
||||
id: syscall_test_microvm
|
||||
run: make run AUTO_TEST=syscall ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1
|
||||
- name: Boot Test (Linux EFI Handover Boot Protocol)
|
||||
id: syscall_test_linux_efi_handover64
|
||||
run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_PROTOCOL=linux-efi-handover64 RELEASE_MODE=1
|
||||
|
||||
- name: Syscall Test at Ext2 (Linux Boot Protocol)
|
||||
- name: Syscall Test at Ext2 (Linux EFI Handover Boot Protocol)
|
||||
id: syscall_test_at_ext2_linux
|
||||
run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_PROTOCOL=linux RELEASE_MODE=1
|
||||
run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_PROTOCOL=linux-efi-handover64 RELEASE_MODE=1
|
||||
|
||||
- name: Syscall Test at Ext2 (MicroVM)
|
||||
id: syscall_test_at_ext2_microvm
|
||||
run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1
|
||||
|
||||
- name: Regression Test (Linux Boot Protocol)
|
||||
- name: Regression Test (MicroVM)
|
||||
id: regression_test_linux
|
||||
run: make run AUTO_TEST=regression ENABLE_KVM=0 BOOT_PROTOCOL=linux RELEASE_MODE=1
|
||||
run: make run AUTO_TEST=regression ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1
|
||||
|
@ -10,7 +10,3 @@ bytemuck = { version = "1.14.0", features = ["derive"] }
|
||||
bitflags = "1.3"
|
||||
serde = { version = "1.0.192", features = ["derive"] }
|
||||
xmas-elf = "0.9.1"
|
||||
|
||||
[features]
|
||||
default = ["trojan64"]
|
||||
trojan64 = []
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! This crate is responsible for building the bzImage. It contains methods to build
|
||||
//! the wrapper binary and methods to build the bzImage.
|
||||
//!
|
||||
//! We should build the jinux kernel as a ELF file, and feed it to the builder to
|
||||
//! We should build the asterinas kernel as a ELF file, and feed it to the builder to
|
||||
//! generate the bzImage. The builder will generate the PE/COFF header for the wrapper
|
||||
//! and concatenate it to the ELF file to make the bzImage.
|
||||
//!
|
||||
@ -15,7 +15,7 @@ mod pe_header;
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ use mapping::{WrapperFileOffset, WrapperVA};
|
||||
///
|
||||
/// Interstingly, the resulting binary should be the same as the memory
|
||||
/// dump of the kernel setup header when it's loaded by the bootloader.
|
||||
fn trojan_to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
|
||||
fn wrapper_to_flat_binary(elf_file: &[u8]) -> Vec<u8> {
|
||||
let elf = xmas_elf::ElfFile::new(&elf_file).unwrap();
|
||||
let mut bin = Vec::<u8>::new();
|
||||
|
||||
@ -72,7 +72,7 @@ fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
|
||||
fn fill_legacy_header_fields(
|
||||
header: &mut [u8],
|
||||
kernel_len: usize,
|
||||
trojan_len: usize,
|
||||
wrapper_len: usize,
|
||||
payload_offset: WrapperVA,
|
||||
) {
|
||||
fill_header_field(
|
||||
@ -90,29 +90,44 @@ fn fill_legacy_header_fields(
|
||||
fill_header_field(
|
||||
header,
|
||||
0x260, /* init_size */
|
||||
&((trojan_len + kernel_len) as u32).to_le_bytes(),
|
||||
&((wrapper_len + kernel_len) as u32).to_le_bytes(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn make_bzimage(path: &Path, kernel_path: &Path, trojan_src: &Path, trojan_out: &Path) {
|
||||
#[cfg(feature = "trojan64")]
|
||||
let wrapper = build_trojan_with_arch(trojan_src, trojan_out, &TrojanBuildArch::X86_64);
|
||||
/// The type of the bzImage that we are building through `make_bzimage`.
|
||||
///
|
||||
/// Currently, Legacy32 and Efi64 are mutually exclusive.
|
||||
pub enum BzImageType {
|
||||
Legacy32,
|
||||
Efi64,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "trojan64"))]
|
||||
let wrapper = {
|
||||
let arch = trojan_src
|
||||
pub fn make_bzimage(
|
||||
image_path: &Path,
|
||||
kernel_path: &Path,
|
||||
image_type: BzImageType,
|
||||
wrapper_src: &Path,
|
||||
wrapper_out: &Path,
|
||||
) {
|
||||
let wrapper = match image_type {
|
||||
BzImageType::Legacy32 => {
|
||||
let arch = wrapper_src
|
||||
.join("x86_64-i386_pm-none.json")
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
build_trojan_with_arch(trojan_src, trojan_out, &TrojanBuildArch::Other(arch))
|
||||
build_wrapper_with_arch(wrapper_src, wrapper_out, &WrapperBuildArch::Other(arch))
|
||||
}
|
||||
BzImageType::Efi64 => {
|
||||
build_wrapper_with_arch(wrapper_src, wrapper_out, &WrapperBuildArch::X86_64)
|
||||
}
|
||||
};
|
||||
|
||||
let mut trojan_elf = Vec::new();
|
||||
let mut wrapper_elf = Vec::new();
|
||||
File::open(wrapper)
|
||||
.unwrap()
|
||||
.read_to_end(&mut trojan_elf)
|
||||
.read_to_end(&mut wrapper_elf)
|
||||
.unwrap();
|
||||
let mut wrapper = trojan_to_flat_binary(&trojan_elf);
|
||||
let mut wrapper = wrapper_to_flat_binary(&wrapper_elf);
|
||||
// Pad the header with 8-byte alignment.
|
||||
wrapper.resize((wrapper.len() + 7) & !7, 0x00);
|
||||
|
||||
@ -123,28 +138,32 @@ pub fn make_bzimage(path: &Path, kernel_path: &Path, trojan_src: &Path, trojan_o
|
||||
.unwrap();
|
||||
let payload = kernel;
|
||||
|
||||
let trojan_len = wrapper.len();
|
||||
let wrapper_len = wrapper.len();
|
||||
let payload_len = payload.len();
|
||||
let payload_offset = WrapperFileOffset::from(trojan_len);
|
||||
fill_legacy_header_fields(&mut wrapper, payload_len, trojan_len, payload_offset.into());
|
||||
let payload_offset = WrapperFileOffset::from(wrapper_len);
|
||||
fill_legacy_header_fields(
|
||||
&mut wrapper,
|
||||
payload_len,
|
||||
wrapper_len,
|
||||
payload_offset.into(),
|
||||
);
|
||||
|
||||
let mut kernel_image = File::create(path).unwrap();
|
||||
let mut kernel_image = File::create(image_path).unwrap();
|
||||
kernel_image.write_all(&wrapper).unwrap();
|
||||
kernel_image.write_all(&payload).unwrap();
|
||||
|
||||
let image_size = trojan_len + payload_len;
|
||||
let image_size = wrapper_len + payload_len;
|
||||
|
||||
if matches!(image_type, BzImageType::Efi64) {
|
||||
// Write the PE/COFF header to the start of the file.
|
||||
// Since the Linux boot header starts at 0x1f1, we can write the PE/COFF header directly to the
|
||||
// start of the file without overwriting the Linux boot header.
|
||||
let pe_header = pe_header::make_pe_coff_header(&trojan_elf, image_size);
|
||||
let pe_header = pe_header::make_pe_coff_header(&wrapper_elf, image_size);
|
||||
assert!(
|
||||
pe_header.header_at_zero.len() <= 0x1f1,
|
||||
"PE/COFF header is too large"
|
||||
);
|
||||
|
||||
#[cfg(feature = "trojan64")]
|
||||
{
|
||||
use std::io::{Seek, SeekFrom};
|
||||
kernel_image.seek(SeekFrom::Start(0)).unwrap();
|
||||
kernel_image.write_all(&pe_header.header_at_zero).unwrap();
|
||||
kernel_image
|
||||
@ -154,19 +173,15 @@ pub fn make_bzimage(path: &Path, kernel_path: &Path, trojan_src: &Path, trojan_o
|
||||
}
|
||||
}
|
||||
|
||||
// We need a custom target file for i386 but not for x86_64.
|
||||
// The compiler may warn us the X86_64 enum variant is not constructed
|
||||
// when we are building for i386, but we can ignore it.
|
||||
#[allow(dead_code)]
|
||||
enum TrojanBuildArch {
|
||||
enum WrapperBuildArch {
|
||||
X86_64,
|
||||
Other(PathBuf),
|
||||
}
|
||||
|
||||
/// Build the trojan binary.
|
||||
/// Build the wrapper binary.
|
||||
///
|
||||
/// It will return the path to the built trojan binary.
|
||||
fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &TrojanBuildArch) -> PathBuf {
|
||||
/// It will return the path to the built wrapper binary.
|
||||
fn build_wrapper_with_arch(source_dir: &Path, out_dir: &Path, arch: &WrapperBuildArch) -> PathBuf {
|
||||
if !out_dir.exists() {
|
||||
std::fs::create_dir_all(&out_dir).unwrap();
|
||||
}
|
||||
@ -185,8 +200,8 @@ fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &TrojanBuildA
|
||||
}
|
||||
cmd.arg("--package").arg("aster-boot-wrapper");
|
||||
cmd.arg("--target").arg(match arch {
|
||||
TrojanBuildArch::X86_64 => "x86_64-unknown-none",
|
||||
TrojanBuildArch::Other(path) => path.to_str().unwrap(),
|
||||
WrapperBuildArch::X86_64 => "x86_64-unknown-none",
|
||||
WrapperBuildArch::Other(path) => path.to_str().unwrap(),
|
||||
});
|
||||
cmd.arg("-Zbuild-std=core,alloc,compiler_builtins");
|
||||
cmd.arg("-Zbuild-std-features=compiler-builtins-mem");
|
||||
@ -207,14 +222,14 @@ fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &TrojanBuildA
|
||||
|
||||
// Get the path to the wrapper binary.
|
||||
let arch_name = match arch {
|
||||
TrojanBuildArch::X86_64 => "x86_64-unknown-none",
|
||||
TrojanBuildArch::Other(path) => path.file_stem().unwrap().to_str().unwrap(),
|
||||
WrapperBuildArch::X86_64 => "x86_64-unknown-none",
|
||||
WrapperBuildArch::Other(path) => path.file_stem().unwrap().to_str().unwrap(),
|
||||
};
|
||||
|
||||
let trojan_artifact = out_dir
|
||||
let wrapper_artifact = out_dir
|
||||
.join(arch_name)
|
||||
.join(profile)
|
||||
.join("aster-boot-wrapper");
|
||||
|
||||
trojan_artifact.to_owned()
|
||||
wrapper_artifact.to_owned()
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub fn load_elf(file: &[u8]) {
|
||||
|
||||
for ph in elf.program_iter() {
|
||||
let ProgramHeader::Ph64(program) = ph else {
|
||||
panic!("[setup] Unexpected program header type! Jinux should be 64-bit ELF binary.");
|
||||
panic!("[setup] Unexpected program header type! Asterinas should be 64-bit ELF binary.");
|
||||
};
|
||||
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||
load_segment(&elf, program);
|
||||
|
@ -122,10 +122,10 @@ fn efi_phase_runtime(
|
||||
|
||||
unsafe {
|
||||
use crate::console::{print_hex, print_str};
|
||||
print_str("[EFI stub] Entering Jinux entrypoint at ");
|
||||
print_hex(super::JINUX_ENTRY_POINT as u64);
|
||||
print_str("[EFI stub] Entering Asterinas entrypoint at ");
|
||||
print_hex(super::ASTER_ENTRY_POINT as u64);
|
||||
print_str("\n");
|
||||
}
|
||||
|
||||
unsafe { super::call_jinux_entrypoint(super::JINUX_ENTRY_POINT, boot_params_ptr as u64) }
|
||||
unsafe { super::call_aster_entrypoint(super::ASTER_ENTRY_POINT, boot_params_ptr as u64) }
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ global_asm!(include_str!("setup.S"));
|
||||
|
||||
use crate::console::{print_hex, print_str};
|
||||
|
||||
pub const JINUX_ENTRY_POINT: u32 = 0x8001000;
|
||||
pub const ASTER_ENTRY_POINT: u32 = 0x8001000;
|
||||
|
||||
#[export_name = "_trojan_entry_32"]
|
||||
extern "cdecl" fn trojan_entry(boot_params_ptr: u32) -> ! {
|
||||
@ -29,7 +29,7 @@ extern "cdecl" fn trojan_entry(boot_params_ptr: u32) -> ! {
|
||||
crate::loader::load_elf(payload);
|
||||
|
||||
// Safety: the entrypoint and the ptr is valid.
|
||||
unsafe { call_jinux_entrypoint(JINUX_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) };
|
||||
unsafe { call_aster_entrypoint(ASTER_ENTRY_POINT, boot_params_ptr.try_into().unwrap()) };
|
||||
}
|
||||
|
||||
pub const ASTER_ENTRY_POINT: u32 = 0x8001000;
|
||||
|
@ -7,6 +7,8 @@ use std::{fs::OpenOptions, io::Write, path::PathBuf, process::Command};
|
||||
///
|
||||
/// If argument `gdb_grub` is set true, it will run GRUB's gdb script.
|
||||
///
|
||||
/// Make sure to set GRUB_PREFIX to the actual GRUB you are using.
|
||||
///
|
||||
/// When debugging grub, the OVMF firmware will load the grub kernel at an
|
||||
/// address unknown at the moment. You should use the debug message from our
|
||||
/// custom built OVMF firmware and read the entrypoint address
|
||||
|
@ -1,4 +1,4 @@
|
||||
use aster_boot_wrapper_builder::make_bzimage;
|
||||
use aster_boot_wrapper_builder::{make_bzimage, BzImageType};
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
@ -64,9 +64,13 @@ pub const IOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
"ioh3420,id=pcie.0,chassis=1",
|
||||
];
|
||||
|
||||
// To test legacy boot, use the following:
|
||||
// pub const GRUB_PREFIX: &str = "/usr/local/grub";
|
||||
/// The default GRUB tools used.
|
||||
pub const GRUB_PREFIX: &str = "/usr";
|
||||
/// The GRUB version that defaults to use EFI handover. Which is the Debian APT version.
|
||||
pub const GRUB_PREFIX_EFI_HANDOVER: &str = "/usr";
|
||||
/// The GRUB version that uses Loadfile2 and has a fallback to use legacy boot. Which is the custom built upstream 2.12 verion.
|
||||
pub const GRUB_PREFIX_EFI_AND_LEGACY: &str = "/usr/local/grub";
|
||||
|
||||
pub const GRUB_VERSION: &str = "x86_64-efi";
|
||||
|
||||
pub fn create_bootdev_image(
|
||||
@ -92,17 +96,23 @@ pub fn create_bootdev_image(
|
||||
.unwrap();
|
||||
|
||||
let target_path = match protocol {
|
||||
BootProtocol::Linux => {
|
||||
let trojan_src = Path::new("framework/libs/boot-wrapper/wrapper");
|
||||
let trojan_out = Path::new("target/aster-boot-wrapper");
|
||||
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => {
|
||||
let image_type = match protocol {
|
||||
BootProtocol::LinuxLegacy32 => BzImageType::Legacy32,
|
||||
BootProtocol::LinuxEfiHandover64 => BzImageType::Efi64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let wrapper_src = Path::new("framework/libs/boot-wrapper/wrapper");
|
||||
let wrapper_out = Path::new("target/aster-boot-wrapper");
|
||||
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
|
||||
let target_path = iso_root.join("boot").join("asterinaz");
|
||||
println!("[aster-runner] Building bzImage.");
|
||||
make_bzimage(
|
||||
&target_path,
|
||||
&aster_path.as_path(),
|
||||
&trojan_src,
|
||||
&trojan_out,
|
||||
image_type,
|
||||
&wrapper_src,
|
||||
&wrapper_out,
|
||||
);
|
||||
target_path
|
||||
}
|
||||
@ -121,7 +131,13 @@ pub fn create_bootdev_image(
|
||||
|
||||
// Make the boot device CDROM image.
|
||||
let iso_path = target_dir.join(target_name.to_string() + ".iso");
|
||||
let grub_mkrescue_bin = PathBuf::from(GRUB_PREFIX).join("bin").join("grub-mkrescue");
|
||||
let grub_mkrescue_bin = match protocol {
|
||||
BootProtocol::LinuxLegacy32 => PathBuf::from(GRUB_PREFIX_EFI_AND_LEGACY),
|
||||
BootProtocol::LinuxEfiHandover64 => PathBuf::from(GRUB_PREFIX_EFI_HANDOVER),
|
||||
BootProtocol::Multiboot | BootProtocol::Multiboot2 => PathBuf::from(GRUB_PREFIX),
|
||||
}
|
||||
.join("bin")
|
||||
.join("grub-mkrescue");
|
||||
let mut cmd = std::process::Command::new(grub_mkrescue_bin.as_os_str());
|
||||
cmd.arg("--output").arg(&iso_path).arg(iso_root.as_os_str());
|
||||
if !cmd.status().unwrap().success() {
|
||||
@ -166,7 +182,7 @@ pub fn generate_grub_cfg(
|
||||
.replace("#GRUB_CMD_KERNEL#", "multiboot2")
|
||||
.replace("#KERNEL#", "/boot/atserinas")
|
||||
.replace("#GRUB_CMD_INITRAMFS#", "module2 --nounzip"),
|
||||
BootProtocol::Linux => buffer
|
||||
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => buffer
|
||||
.replace("#GRUB_CMD_KERNEL#", "linux")
|
||||
.replace("#KERNEL#", "/boot/asterinaz")
|
||||
.replace("#GRUB_CMD_INITRAMFS#", "initrd"),
|
||||
|
@ -32,7 +32,8 @@ enum BootMethod {
|
||||
pub enum BootProtocol {
|
||||
Multiboot,
|
||||
Multiboot2,
|
||||
Linux,
|
||||
LinuxLegacy32,
|
||||
LinuxEfiHandover64,
|
||||
}
|
||||
/// The CLI of this runner.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -56,7 +57,8 @@ struct Args {
|
||||
/// Boot protocol. Can be one of the following items:
|
||||
/// - `multiboot`;
|
||||
/// - `multiboot2`;
|
||||
/// - `linux`.
|
||||
/// - `linux-legacy32`;
|
||||
/// - `linux-efi-handover64`.
|
||||
#[arg(long, value_enum, default_value_t = BootProtocol::Multiboot2)]
|
||||
boot_protocol: BootProtocol,
|
||||
|
||||
|
Reference in New Issue
Block a user