mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 00:43:24 +00:00
Introduce the boot trojan
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ddca4fb2fc
commit
953ff66fcc
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -417,6 +417,26 @@ dependencies = [
|
|||||||
"spin 0.9.8",
|
"spin 0.9.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck_derive"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
@ -734,12 +754,6 @@ version = "0.28.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glob"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1252,9 +1266,23 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.183"
|
version = "1.0.192"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.192"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
@ -1667,6 +1695,15 @@ dependencies = [
|
|||||||
"zero",
|
"zero",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmas-elf"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42c49817e78342f7f30a181573d82ff55b88a35f86ccaf07fc64b3008f56d1c6"
|
||||||
|
dependencies = [
|
||||||
|
"zero",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zero"
|
name = "zero"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -36,6 +36,8 @@ members = [
|
|||||||
"framework/aster-frame",
|
"framework/aster-frame",
|
||||||
"framework/aster-frame/src/arch/x86/boot/linux_boot/setup",
|
"framework/aster-frame/src/arch/x86/boot/linux_boot/setup",
|
||||||
"framework/libs/align_ext",
|
"framework/libs/align_ext",
|
||||||
|
"framework/libs/boot-trojan/builder",
|
||||||
|
"framework/libs/boot-trojan/trojan",
|
||||||
"framework/libs/ktest",
|
"framework/libs/ktest",
|
||||||
"framework/libs/tdx-guest",
|
"framework/libs/tdx-guest",
|
||||||
"services/comps/block",
|
"services/comps/block",
|
||||||
|
5
Makefile
5
Makefile
@ -44,10 +44,6 @@ CARGO_KRUN_ARGS += -- '$(KERNEL_CMDLINE) -- $(INIT_CMDLINE)'
|
|||||||
CARGO_KRUN_ARGS += --boot-method="$(BOOT_METHOD)"
|
CARGO_KRUN_ARGS += --boot-method="$(BOOT_METHOD)"
|
||||||
CARGO_KRUN_ARGS += --boot-protocol="$(BOOT_PROTOCOL)"
|
CARGO_KRUN_ARGS += --boot-protocol="$(BOOT_PROTOCOL)"
|
||||||
|
|
||||||
ifeq ($(RELEASE_MODE), 1)
|
|
||||||
CARGO_KRUN_ARGS += --release-mode
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(EMULATE_IOMMU), 1)
|
ifeq ($(EMULATE_IOMMU), 1)
|
||||||
CARGO_KRUN_ARGS += --emulate-iommu
|
CARGO_KRUN_ARGS += --emulate-iommu
|
||||||
endif
|
endif
|
||||||
@ -89,6 +85,7 @@ export CARGO := cargo
|
|||||||
USERMODE_TESTABLE := \
|
USERMODE_TESTABLE := \
|
||||||
runner \
|
runner \
|
||||||
framework/libs/align_ext \
|
framework/libs/align_ext \
|
||||||
|
framework/libs/boot-trojan/builder \
|
||||||
framework/libs/ktest \
|
framework/libs/ktest \
|
||||||
framework/libs/ktest-proc-macro \
|
framework/libs/ktest-proc-macro \
|
||||||
services/libs/cpio-decoder \
|
services/libs/cpio-decoder \
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
ENTRY(start_of_setup32)
|
|
||||||
OUTPUT_ARCH(i386:x86)
|
|
||||||
OUTPUT_FORMAT(elf32-i386)
|
|
||||||
|
|
||||||
SETUP32_LMA = 0x100000;
|
|
||||||
|
|
||||||
SECTIONS
|
|
||||||
{
|
|
||||||
. = SETUP32_LMA - 0x1000;
|
|
||||||
.header : { KEEP(*(.header)) }
|
|
||||||
|
|
||||||
. = SETUP32_LMA;
|
|
||||||
.header.text : { KEEP(*(.header)) }
|
|
||||||
.stack : { KEEP(*(.stack)) }
|
|
||||||
|
|
||||||
.text : { *(.text .text.*) }
|
|
||||||
.rodata : { *(.rodata .rodata.*) }
|
|
||||||
|
|
||||||
.data : { *(.data .data.*) }
|
|
||||||
.bss : {
|
|
||||||
__bss = .;
|
|
||||||
*(.bss .bss.*) *(COMMON)
|
|
||||||
__bss_end = .;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eh_frame : {
|
|
||||||
*(.eh_frame .eh_frame.*)
|
|
||||||
}
|
|
||||||
.eh_frame_hdr : {
|
|
||||||
*(.eh_frame_hdr .eh_frame_hdr.*)
|
|
||||||
}
|
|
||||||
}
|
|
12
framework/libs/boot-trojan/builder/Cargo.toml
Normal file
12
framework/libs/boot-trojan/builder/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "aster-boot-trojan-builder"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytemuck = { version = "1.14.0", features = ["derive"] }
|
||||||
|
bitflags = "1.3"
|
||||||
|
serde = { version = "1.0.192", features = ["derive"] }
|
||||||
|
xmas-elf = "0.9.1"
|
165
framework/libs/boot-trojan/builder/src/lib.rs
Normal file
165
framework/libs/boot-trojan/builder/src/lib.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
mod mapping;
|
||||||
|
mod pe_header;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Seek, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use xmas_elf::program::{ProgramHeader, SegmentData};
|
||||||
|
|
||||||
|
use mapping::{TrojanFileOffset, TrojanVA};
|
||||||
|
|
||||||
|
/// We need a flat binary which satisfies PA delta == File delta, and objcopy
|
||||||
|
/// does not satisfy us well, so we should parse the ELF and do our own
|
||||||
|
/// objcopy job.
|
||||||
|
///
|
||||||
|
/// 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> {
|
||||||
|
let elf = xmas_elf::ElfFile::new(&elf_file).unwrap();
|
||||||
|
let mut bin = Vec::<u8>::new();
|
||||||
|
|
||||||
|
for ph in elf.program_iter() {
|
||||||
|
let ProgramHeader::Ph32(program) = ph else {
|
||||||
|
panic!("Unexpected program header type");
|
||||||
|
};
|
||||||
|
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||||
|
let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
|
||||||
|
panic!("Unexpected segment data type");
|
||||||
|
};
|
||||||
|
let dst_file_offset = usize::from(TrojanFileOffset::from(TrojanVA::from(
|
||||||
|
program.virtual_addr as usize,
|
||||||
|
)));
|
||||||
|
let dst_file_length = program.file_size as usize;
|
||||||
|
if bin.len() < dst_file_offset + dst_file_length {
|
||||||
|
bin.resize(dst_file_offset + dst_file_length, 0);
|
||||||
|
}
|
||||||
|
let dest_slice = bin[dst_file_offset..dst_file_offset + dst_file_length].as_mut();
|
||||||
|
dest_slice.copy_from_slice(header_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bin
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function sould be used when generating the Linux x86 Boot setup header.
|
||||||
|
/// Some fields in the Linux x86 Boot setup header should be filled after assembled.
|
||||||
|
/// And the filled fields must have the bytes with values of 0xAB. See
|
||||||
|
/// `framework/aster-frame/src/arch/x86/boot/linux_boot/setup/src/header.S` for more
|
||||||
|
/// info on this mechanism.
|
||||||
|
fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
|
||||||
|
let size = value.len();
|
||||||
|
assert_eq!(
|
||||||
|
&header[offset..offset + size],
|
||||||
|
vec![0xABu8; size].as_slice(),
|
||||||
|
"The field {:#x} to be filled must be marked with 0xAB",
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
header[offset..offset + size].copy_from_slice(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_legacy_header_fields(
|
||||||
|
header: &mut [u8],
|
||||||
|
kernel_len: usize,
|
||||||
|
header_len: usize,
|
||||||
|
payload_offset: TrojanVA,
|
||||||
|
) {
|
||||||
|
fill_header_field(
|
||||||
|
header,
|
||||||
|
0x248, /* payload_offset */
|
||||||
|
&(usize::from(payload_offset) as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_header_field(
|
||||||
|
header,
|
||||||
|
0x24C, /* payload_length */
|
||||||
|
&(kernel_len as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_header_field(
|
||||||
|
header,
|
||||||
|
0x260, /* init_size */
|
||||||
|
&((header_len + kernel_len) as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_bzimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> {
|
||||||
|
let mut header_elf_file = Vec::new();
|
||||||
|
File::open(header_path)?.read_to_end(&mut header_elf_file)?;
|
||||||
|
let mut header = trojan_to_flat_binary(&header_elf_file);
|
||||||
|
// Pad the Linux boot header to let the payload starts with 8-byte alignment.
|
||||||
|
header.resize((header.len() + 7) & !7, 0x00);
|
||||||
|
|
||||||
|
let mut kernel = Vec::new();
|
||||||
|
File::open(kernel_path)?.read_to_end(&mut kernel)?;
|
||||||
|
let payload = kernel;
|
||||||
|
|
||||||
|
let header_len = header.len();
|
||||||
|
let payload_len = payload.len();
|
||||||
|
let payload_offset = TrojanFileOffset::from(header_len);
|
||||||
|
fill_legacy_header_fields(&mut header, payload_len, header_len, payload_offset.into());
|
||||||
|
|
||||||
|
let mut kernel_image = File::create(path)?;
|
||||||
|
kernel_image.write_all(&header)?;
|
||||||
|
kernel_image.write_all(&payload)?;
|
||||||
|
|
||||||
|
let image_size = header_len + payload_len;
|
||||||
|
|
||||||
|
// 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(&header_elf_file, image_size);
|
||||||
|
assert!(
|
||||||
|
pe_header.header_at_zero.len() <= 0x1f1,
|
||||||
|
"PE/COFF header is too large"
|
||||||
|
);
|
||||||
|
|
||||||
|
// FIXME: Oops, EFI hanover stucks, so I removed the pe header to let grub go through the legacy path.
|
||||||
|
kernel_image.seek(std::io::SeekFrom::Start(0))?;
|
||||||
|
// kernel_image.write_all(&pe_header.header_at_zero)?;
|
||||||
|
kernel_image.seek(std::io::SeekFrom::Start(
|
||||||
|
usize::from(pe_header.relocs.0) as u64
|
||||||
|
))?;
|
||||||
|
// kernel_image.write_all(&pe_header.relocs.1)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_linux_setup_header_from_trojan(
|
||||||
|
source_dir: &Path,
|
||||||
|
out_dir: &Path,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
// Build the setup header to ELF.
|
||||||
|
let target_json = source_dir.join("x86_64-i386_protected_mode.json");
|
||||||
|
|
||||||
|
let cargo = std::env::var("CARGO").unwrap();
|
||||||
|
let mut cmd = std::process::Command::new(cargo);
|
||||||
|
cmd.arg("install").arg("aster-boot-trojan");
|
||||||
|
cmd.arg("--debug");
|
||||||
|
cmd.arg("--locked");
|
||||||
|
cmd.arg("--path").arg(source_dir.to_str().unwrap());
|
||||||
|
cmd.arg("--target").arg(target_json.as_os_str());
|
||||||
|
cmd.arg("-Zbuild-std=core,compiler_builtins");
|
||||||
|
cmd.arg("-Zbuild-std-features=compiler-builtins-mem");
|
||||||
|
// Specify the installation root.
|
||||||
|
cmd.arg("--root").arg(out_dir.as_os_str());
|
||||||
|
// Specify the build target directory to avoid cargo running
|
||||||
|
// into a deadlock reading the workspace files.
|
||||||
|
cmd.arg("--target-dir").arg(out_dir.as_os_str());
|
||||||
|
cmd.env_remove("RUSTFLAGS");
|
||||||
|
cmd.env_remove("CARGO_ENCODED_RUSTFLAGS");
|
||||||
|
let output = cmd.output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
std::io::stdout().write_all(&output.stdout).unwrap();
|
||||||
|
std::io::stderr().write_all(&output.stderr).unwrap();
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to build linux x86 setup header:\n\tcommand `{:?}`\n\treturned {}",
|
||||||
|
cmd, output.status
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
76
framework/libs/boot-trojan/builder/src/mapping.rs
Normal file
76
framework/libs/boot-trojan/builder/src/mapping.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//! In the trojan, VA - SETUP32_LMA == FileOffset - LEGACY_SETUP_SEC_SIZE.
|
||||||
|
//! And the addresses are specified in the ELF file.
|
||||||
|
|
||||||
|
use std::{cmp::PartialOrd, convert::From, ops::Sub};
|
||||||
|
|
||||||
|
// We chose the legacy setup sections to be 7 so that the setup header
|
||||||
|
// is page-aligned and the legacy setup section size would be 0x1000.
|
||||||
|
pub const LEGACY_SETUP_SECS: usize = 7;
|
||||||
|
pub const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1);
|
||||||
|
pub const SETUP32_LMA: usize = 0x100000;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
|
pub struct TrojanVA {
|
||||||
|
addr: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
|
pub struct TrojanFileOffset {
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for TrojanVA {
|
||||||
|
fn from(addr: usize) -> Self {
|
||||||
|
Self { addr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TrojanVA> for usize {
|
||||||
|
fn from(va: TrojanVA) -> Self {
|
||||||
|
va.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for TrojanVA {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
self.addr - rhs.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for TrojanFileOffset {
|
||||||
|
fn from(offset: usize) -> Self {
|
||||||
|
Self { offset }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TrojanFileOffset> for usize {
|
||||||
|
fn from(offset: TrojanFileOffset) -> Self {
|
||||||
|
offset.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for TrojanFileOffset {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
self.offset - rhs.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TrojanVA> for TrojanFileOffset {
|
||||||
|
fn from(va: TrojanVA) -> Self {
|
||||||
|
Self {
|
||||||
|
offset: va.addr + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TrojanFileOffset> for TrojanVA {
|
||||||
|
fn from(offset: TrojanFileOffset) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: offset.offset + SETUP32_LMA - LEGACY_SETUP_SEC_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
353
framework/libs/boot-trojan/builder/src/pe_header.rs
Normal file
353
framework/libs/boot-trojan/builder/src/pe_header.rs
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
//! Big zImage PE/COFF header generation.
|
||||||
|
//!
|
||||||
|
//! The definition of the PE/COFF header is in the Microsoft PE/COFF specification:
|
||||||
|
//! https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
|
||||||
|
//!
|
||||||
|
//! The reference to the Linux PE header definition:
|
||||||
|
//! https://github.com/torvalds/linux/blob/master/include/linux/pe.h
|
||||||
|
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use std::{mem::size_of, ops::Range};
|
||||||
|
|
||||||
|
use crate::mapping::{TrojanFileOffset, TrojanVA, LEGACY_SETUP_SEC_SIZE, SETUP32_LMA};
|
||||||
|
|
||||||
|
// The MS-DOS header.
|
||||||
|
const MZ_MAGIC: u16 = 0x5a4d; // "MZ"
|
||||||
|
|
||||||
|
// The `magic` field in PE header.
|
||||||
|
const PE_MAGIC: u32 = 0x00004550;
|
||||||
|
|
||||||
|
// The `machine` field choices in PE header. Not exhaustive.
|
||||||
|
#[derive(Serialize, Clone, Copy)]
|
||||||
|
#[repr(u16)]
|
||||||
|
enum PeMachineType {
|
||||||
|
Amd64 = 0x8664,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `flags` field choices in PE header.
|
||||||
|
bitflags::bitflags! {
|
||||||
|
struct PeFlags: u16 {
|
||||||
|
const RELOCS_STRIPPED = 1;
|
||||||
|
const EXECUTABLE_IMAGE = 1 << 1;
|
||||||
|
const LINE_NUMS_STRIPPED = 1 << 2;
|
||||||
|
const LOCAL_SYMS_STRIPPED = 1 << 3;
|
||||||
|
const AGGRESIVE_WS_TRIM = 1 << 4;
|
||||||
|
const LARGE_ADDRESS_AWARE = 1 << 5;
|
||||||
|
const SIXTEEN_BIT_MACHINE = 1 << 6;
|
||||||
|
const BYTES_REVERSED_LO = 1 << 7;
|
||||||
|
const THIRTY_TWO_BIT_MACHINE = 1 << 8;
|
||||||
|
const DEBUG_STRIPPED = 1 << 9;
|
||||||
|
const REMOVABLE_RUN_FROM_SWAP = 1 << 10;
|
||||||
|
const NET_RUN_FROM_SWAP = 1 << 11;
|
||||||
|
const SYSTEM = 1 << 12;
|
||||||
|
const DLL = 1 << 13;
|
||||||
|
const UP_SYSTEM_ONLY = 1 << 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Zeroable, Pod, Serialize, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct PeHdr {
|
||||||
|
magic: u32, // PE magic
|
||||||
|
machine: u16, // machine type
|
||||||
|
sections: u16, // number of sections
|
||||||
|
timestamp: u32, // time_t
|
||||||
|
symbol_table: u32, // symbol table offset
|
||||||
|
symbols: u32, // number of symbols
|
||||||
|
opt_hdr_size: u16, // size of optional header
|
||||||
|
flags: u16, // flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `magic` field in the PE32+ optional header.
|
||||||
|
const PE32PLUS_OPT_HDR_MAGIC: u16 = 0x020b;
|
||||||
|
|
||||||
|
// The `subsys` field choices in the PE32+ optional header. Not exhaustive.
|
||||||
|
#[derive(Serialize, Clone, Copy)]
|
||||||
|
#[repr(u16)]
|
||||||
|
enum PeImageSubsystem {
|
||||||
|
EfiApplication = 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Zeroable, Pod, Serialize, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct Pe32PlusOptHdr {
|
||||||
|
magic: u16, // file type
|
||||||
|
ld_major: u8, // linker major version
|
||||||
|
ld_minor: u8, // linker minor version
|
||||||
|
text_size: u32, // size of text section(s)
|
||||||
|
data_size: u32, // size of data section(s)
|
||||||
|
bss_size: u32, // size of bss section(s)
|
||||||
|
entry_point: u32, // file offset of entry point
|
||||||
|
code_base: u32, // relative code addr in ram
|
||||||
|
image_base: u64, // preferred load address
|
||||||
|
section_align: u32, // alignment in bytes
|
||||||
|
file_align: u32, // file alignment in bytes
|
||||||
|
os_major: u16, // major OS version
|
||||||
|
os_minor: u16, // minor OS version
|
||||||
|
image_major: u16, // major image version
|
||||||
|
image_minor: u16, // minor image version
|
||||||
|
subsys_major: u16, // major subsystem version
|
||||||
|
subsys_minor: u16, // minor subsystem version
|
||||||
|
win32_version: u32, // reserved, must be 0
|
||||||
|
image_size: u32, // image size
|
||||||
|
header_size: u32, // header size rounded up to file_align
|
||||||
|
csum: u32, // checksum
|
||||||
|
subsys: u16, // subsystem
|
||||||
|
dll_flags: u16, // more flags!
|
||||||
|
stack_size_req: u64, // amt of stack requested
|
||||||
|
stack_size: u64, // amt of stack required
|
||||||
|
heap_size_req: u64, // amt of heap requested
|
||||||
|
heap_size: u64, // amt of heap required
|
||||||
|
loader_flags: u32, // reserved, must be 0
|
||||||
|
data_dirs: u32, // number of data dir entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `flags` field choices in the PE section header.
|
||||||
|
// Excluding the alignment flags, which is not bitflags.
|
||||||
|
bitflags::bitflags! {
|
||||||
|
struct PeSectionHdrFlags: u32 {
|
||||||
|
const CNT_CODE = 1 << 5;
|
||||||
|
const CNT_INITIALIZED_DATA = 1 << 6;
|
||||||
|
const CNT_UNINITIALIZED_DATA = 1 << 7;
|
||||||
|
const LNK_INFO = 1 << 9;
|
||||||
|
const LNK_REMOVE = 1 << 11;
|
||||||
|
const LNK_COMDAT = 1 << 12;
|
||||||
|
const GPREL = 1 << 15;
|
||||||
|
const MEM_PURGEABLE = 1 << 16;
|
||||||
|
const LNK_NRELOC_OVFL = 1 << 24;
|
||||||
|
const MEM_DISCARDABLE = 1 << 25;
|
||||||
|
const MEM_NOT_CACHED = 1 << 26;
|
||||||
|
const MEM_NOT_PAGED = 1 << 27;
|
||||||
|
const MEM_SHARED = 1 << 28;
|
||||||
|
const MEM_EXECUTE = 1 << 29;
|
||||||
|
const MEM_READ = 1 << 30;
|
||||||
|
const MEM_WRITE = 1 << 31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `flags` field choices in the PE section header.
|
||||||
|
#[derive(Serialize, Clone, Copy)]
|
||||||
|
#[repr(u32)]
|
||||||
|
enum PeSectionHdrFlagsAlign {
|
||||||
|
_1Bytes = 0x00100000,
|
||||||
|
_2Bytes = 0x00200000,
|
||||||
|
_4Bytes = 0x00300000,
|
||||||
|
_8Bytes = 0x00400000,
|
||||||
|
_16Bytes = 0x00500000,
|
||||||
|
_32Bytes = 0x00600000,
|
||||||
|
_64Bytes = 0x00700000,
|
||||||
|
_128Bytes = 0x00800000,
|
||||||
|
_256Bytes = 0x00900000,
|
||||||
|
_512Bytes = 0x00A00000,
|
||||||
|
_1024Bytes = 0x00B00000,
|
||||||
|
_2048Bytes = 0x00C00000,
|
||||||
|
_4096Bytes = 0x00D00000,
|
||||||
|
_8192Bytes = 0x00E00000,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Zeroable, Pod, Serialize, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct PeSectionHdr {
|
||||||
|
name: [u8; 8], // name or "/12\0" string tbl offset
|
||||||
|
virtual_size: u32, // size of loaded section in RAM
|
||||||
|
virtual_address: u32, // relative virtual address
|
||||||
|
raw_data_size: u32, // size of the section
|
||||||
|
data_addr: u32, // file pointer to first page of sec
|
||||||
|
relocs: u32, // file pointer to relocation entries
|
||||||
|
line_numbers: u32, // line numbers!
|
||||||
|
num_relocs: u16, // number of relocations
|
||||||
|
num_lin_numbers: u16, // srsly.
|
||||||
|
flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TrojanSectionAddrInfo {
|
||||||
|
pub setup: Range<TrojanVA>,
|
||||||
|
pub text: Range<TrojanVA>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrojanSectionAddrInfo {
|
||||||
|
fn from(elf: &xmas_elf::ElfFile) -> Self {
|
||||||
|
let mut setup_start = None;
|
||||||
|
let mut setup_end = None;
|
||||||
|
let mut text_start = None;
|
||||||
|
let mut text_end = None;
|
||||||
|
for section in elf.section_iter() {
|
||||||
|
match elf.get_shstr(section.name()) {
|
||||||
|
Ok(s) => {
|
||||||
|
if s == ".setup" {
|
||||||
|
setup_start = Some(section.address() as usize);
|
||||||
|
setup_end = Some(section.address() as usize + section.size() as usize);
|
||||||
|
} else if s == ".text" {
|
||||||
|
text_start = Some(section.address() as usize);
|
||||||
|
text_end = Some(section.address() as usize + section.size() as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
panic!("Error: {:#?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
matches!(setup_start, Some(SETUP32_LMA)),
|
||||||
|
"setup_start: {:#x?}",
|
||||||
|
setup_start
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
setup: TrojanVA::from(setup_start.unwrap())..TrojanVA::from(setup_end.unwrap()),
|
||||||
|
text: TrojanVA::from(text_start.unwrap())..TrojanVA::from(text_end.unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_virt_size(&self) -> usize {
|
||||||
|
self.setup.end - self.setup.start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_file_size(&self) -> usize {
|
||||||
|
TrojanFileOffset::from(self.setup.end) - TrojanFileOffset::from(self.setup.start)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_virt_size(&self) -> usize {
|
||||||
|
self.text.end - self.text.start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_file_size(&self) -> usize {
|
||||||
|
TrojanFileOffset::from(self.text.end) - TrojanFileOffset::from(self.text.start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TrojanPeCoffHeaderBuf {
|
||||||
|
pub header_at_zero: Vec<u8>,
|
||||||
|
pub relocs: (TrojanFileOffset, Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> TrojanPeCoffHeaderBuf {
|
||||||
|
let elf = xmas_elf::ElfFile::new(setup_elf).unwrap();
|
||||||
|
let mut bin = Vec::<u8>::new();
|
||||||
|
|
||||||
|
// PE header
|
||||||
|
let pe_hdr = PeHdr {
|
||||||
|
magic: PE_MAGIC,
|
||||||
|
machine: PeMachineType::Amd64 as u16,
|
||||||
|
sections: 3, // please set this field according to the number of sections added in the pe header
|
||||||
|
timestamp: 0,
|
||||||
|
symbol_table: 0,
|
||||||
|
symbols: 1, // I don't know why, Linux header.S says it's 1
|
||||||
|
opt_hdr_size: size_of::<Pe32PlusOptHdr>() as u16,
|
||||||
|
flags: (PeFlags::EXECUTABLE_IMAGE | PeFlags::DEBUG_STRIPPED | PeFlags::LINE_NUMS_STRIPPED)
|
||||||
|
.bits,
|
||||||
|
};
|
||||||
|
|
||||||
|
let elf_text_hdr = elf.find_section_by_name(".text").unwrap();
|
||||||
|
|
||||||
|
// PE32+ optional header
|
||||||
|
let pe_opt_hdr = Pe32PlusOptHdr {
|
||||||
|
magic: PE32PLUS_OPT_HDR_MAGIC,
|
||||||
|
ld_major: 0x02, // there's no linker to this extent, we do linking by ourselves
|
||||||
|
ld_minor: 0x14,
|
||||||
|
text_size: elf_text_hdr.size() as u32,
|
||||||
|
data_size: 0, // data size is irrelevant
|
||||||
|
bss_size: 0, // bss size is irrelevant
|
||||||
|
entry_point: elf.header.pt2.entry_point() as u32,
|
||||||
|
code_base: elf_text_hdr.address() as u32,
|
||||||
|
image_base: SETUP32_LMA as u64 - LEGACY_SETUP_SEC_SIZE as u64,
|
||||||
|
section_align: 0x20,
|
||||||
|
file_align: 0x20,
|
||||||
|
os_major: 0,
|
||||||
|
os_minor: 0,
|
||||||
|
image_major: 0x3, // see linux/pe.h for more info
|
||||||
|
image_minor: 0,
|
||||||
|
subsys_major: 0,
|
||||||
|
subsys_minor: 0,
|
||||||
|
win32_version: 0,
|
||||||
|
image_size: image_size as u32,
|
||||||
|
header_size: LEGACY_SETUP_SEC_SIZE as u32,
|
||||||
|
csum: 0,
|
||||||
|
subsys: PeImageSubsystem::EfiApplication as u16,
|
||||||
|
dll_flags: 0,
|
||||||
|
stack_size_req: 0,
|
||||||
|
stack_size: 0,
|
||||||
|
heap_size_req: 0,
|
||||||
|
heap_size: 0,
|
||||||
|
loader_flags: 0,
|
||||||
|
data_dirs: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let addr_info = TrojanSectionAddrInfo::from(&elf);
|
||||||
|
|
||||||
|
// PE section headers
|
||||||
|
let pe_setup_hdr = PeSectionHdr {
|
||||||
|
name: [b'.', b's', b'e', b't', b'u', b'p', 0, 0],
|
||||||
|
virtual_size: addr_info.setup_virt_size() as u32,
|
||||||
|
virtual_address: usize::from(addr_info.setup.start) as u32,
|
||||||
|
raw_data_size: addr_info.setup_file_size() as u32,
|
||||||
|
data_addr: usize::from(TrojanFileOffset::from(addr_info.setup.start)) as u32,
|
||||||
|
relocs: 0,
|
||||||
|
line_numbers: 0,
|
||||||
|
num_relocs: 0,
|
||||||
|
num_lin_numbers: 0,
|
||||||
|
flags: (PeSectionHdrFlags::CNT_CODE
|
||||||
|
| PeSectionHdrFlags::MEM_READ
|
||||||
|
| PeSectionHdrFlags::MEM_EXECUTE
|
||||||
|
| PeSectionHdrFlags::MEM_DISCARDABLE)
|
||||||
|
.bits
|
||||||
|
| PeSectionHdrFlagsAlign::_16Bytes as u32,
|
||||||
|
};
|
||||||
|
// The EFI application loader requires a relocation section
|
||||||
|
let reloc_offset = TrojanFileOffset::from(0x500); // must be after the legacy header and before 0x1000
|
||||||
|
let relocs = vec![];
|
||||||
|
let pe_reloc_hdr = PeSectionHdr {
|
||||||
|
name: [b'.', b'r', b'e', b'l', b'o', b'c', 0, 0],
|
||||||
|
virtual_size: relocs.len() as u32,
|
||||||
|
virtual_address: usize::from(TrojanVA::from(reloc_offset)) as u32,
|
||||||
|
raw_data_size: relocs.len() as u32,
|
||||||
|
data_addr: usize::from(reloc_offset) as u32,
|
||||||
|
relocs: 0,
|
||||||
|
line_numbers: 0,
|
||||||
|
num_relocs: 0,
|
||||||
|
num_lin_numbers: 0,
|
||||||
|
flags: (PeSectionHdrFlags::CNT_INITIALIZED_DATA
|
||||||
|
| PeSectionHdrFlags::MEM_READ
|
||||||
|
| PeSectionHdrFlags::MEM_DISCARDABLE)
|
||||||
|
.bits
|
||||||
|
| PeSectionHdrFlagsAlign::_1Bytes as u32,
|
||||||
|
};
|
||||||
|
let pe_text_hdr = PeSectionHdr {
|
||||||
|
name: [b'.', b't', b'e', b'x', b't', 0, 0, 0],
|
||||||
|
virtual_size: addr_info.text_virt_size() as u32,
|
||||||
|
virtual_address: usize::from(addr_info.text.start) as u32,
|
||||||
|
raw_data_size: addr_info.text_file_size() as u32,
|
||||||
|
data_addr: usize::from(TrojanFileOffset::from(addr_info.text.start)) as u32,
|
||||||
|
relocs: 0,
|
||||||
|
line_numbers: 0,
|
||||||
|
num_relocs: 0,
|
||||||
|
num_lin_numbers: 0,
|
||||||
|
flags: (PeSectionHdrFlags::CNT_CODE
|
||||||
|
| PeSectionHdrFlags::MEM_READ
|
||||||
|
| PeSectionHdrFlags::MEM_EXECUTE)
|
||||||
|
.bits
|
||||||
|
| PeSectionHdrFlagsAlign::_16Bytes as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the MS-DOS header
|
||||||
|
bin.extend_from_slice(&MZ_MAGIC.to_le_bytes());
|
||||||
|
// Write the MS-DOS stub at 0x3c
|
||||||
|
bin.extend_from_slice(&[0x0; 0x3c - 0x2]);
|
||||||
|
// Write the PE header offset, the header is right after the offset field
|
||||||
|
bin.extend_from_slice(&(0x3cu32 + size_of::<u32>() as u32).to_le_bytes());
|
||||||
|
|
||||||
|
// Write the PE header
|
||||||
|
bin.extend_from_slice(bytemuck::bytes_of(&pe_hdr));
|
||||||
|
// Write the PE32+ optional header
|
||||||
|
bin.extend_from_slice(bytemuck::bytes_of(&pe_opt_hdr));
|
||||||
|
// Write the PE section headers
|
||||||
|
bin.extend_from_slice(bytemuck::bytes_of(&pe_setup_hdr));
|
||||||
|
bin.extend_from_slice(bytemuck::bytes_of(&pe_reloc_hdr));
|
||||||
|
bin.extend_from_slice(bytemuck::bytes_of(&pe_text_hdr));
|
||||||
|
|
||||||
|
TrojanPeCoffHeaderBuf {
|
||||||
|
header_at_zero: bin,
|
||||||
|
relocs: (reloc_offset, relocs),
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "aster-frame-x86-boot-linux-setup"
|
name = "aster-boot-trojan"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
52
framework/libs/boot-trojan/trojan/linker.ld
Normal file
52
framework/libs/boot-trojan/trojan/linker.ld
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
ENTRY(start_of_setup64)
|
||||||
|
OUTPUT_ARCH(i386:x86)
|
||||||
|
OUTPUT_FORMAT(elf32-i386)
|
||||||
|
|
||||||
|
SETUP32_LMA = 0x100000;
|
||||||
|
BOOTSECT_SIZE = 0x1000;
|
||||||
|
BOOTSECT_START = SETUP32_LMA - BOOTSECT_SIZE;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = BOOTSECT_START;
|
||||||
|
.header : { KEEP(*(.header)) }
|
||||||
|
|
||||||
|
. = SETUP32_LMA;
|
||||||
|
.setup : {
|
||||||
|
PROVIDE(__setup_start = .);
|
||||||
|
KEEP(*(.header))
|
||||||
|
PROVIDE(__setup_end = .);
|
||||||
|
}
|
||||||
|
.stack : { KEEP(*(.stack)) }
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
PROVIDE(__text_start = .);
|
||||||
|
*(.text .text.*)
|
||||||
|
PROVIDE(__text_end = .);
|
||||||
|
}
|
||||||
|
.rodata : { *(.rodata .rodata.*) }
|
||||||
|
|
||||||
|
.data : { *(.data .data.*) }
|
||||||
|
.bss : {
|
||||||
|
PROVIDE(__bss_start = .);
|
||||||
|
*(.bss .bss.*) *(COMMON)
|
||||||
|
PROVIDE(__bss_end = .);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh_frame : {
|
||||||
|
*(.eh_frame .eh_frame.*)
|
||||||
|
}
|
||||||
|
.eh_frame_hdr : {
|
||||||
|
*(.eh_frame_hdr .eh_frame_hdr.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.symtab : {
|
||||||
|
*(.symtab .symtab.*)
|
||||||
|
}
|
||||||
|
.strtab : {
|
||||||
|
*(.strtab .strtab.*)
|
||||||
|
}
|
||||||
|
.shstrtab : {
|
||||||
|
*(.shstrtab .shstrtab.*)
|
||||||
|
}
|
||||||
|
}
|
@ -2,22 +2,19 @@
|
|||||||
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
|
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
|
||||||
// more information on the Linux x86 Boot Protocol.
|
// more information on the Linux x86 Boot Protocol.
|
||||||
|
|
||||||
.section ".header", "a"
|
|
||||||
|
|
||||||
// The Linux x86 Boot Protocol header.
|
|
||||||
//
|
|
||||||
// Some of the fields filled with a 0xab* values should be filled
|
// Some of the fields filled with a 0xab* values should be filled
|
||||||
// by the runner, which is the only tool after building and can
|
// by the runner, which is the only tool after building and can
|
||||||
// access the info of the payload.
|
// access the info of the payload.
|
||||||
// Asterinas will use only a few of these fields, and some of them
|
// Asterinas will use only a few of these fields, and some of them
|
||||||
// are filled by the loader and will be read by Asterinas.
|
// are filled by the loader and will be read by Asterinas.
|
||||||
|
|
||||||
|
.section ".header", "a"
|
||||||
CODE32_START = 0x100000
|
CODE32_START = 0x100000
|
||||||
|
SETUP_SECTS = 7 # so that the legacy setup could occupy a page
|
||||||
.code16
|
.code16
|
||||||
.org 0x01f1
|
.org 0x01f1
|
||||||
hdr_start:
|
hdr_start:
|
||||||
setup_sects: .byte 7 # so that the legacy setup could occupy a page
|
setup_sects: .byte SETUP_SECTS
|
||||||
root_flags: .word 1
|
root_flags: .word 1
|
||||||
syssize: .long 0
|
syssize: .long 0
|
||||||
ram_size: .word 0
|
ram_size: .word 0
|
||||||
@ -46,47 +43,15 @@ initrd_addr_max: .long 0x7fffffff
|
|||||||
kernel_alignment: .long 0x1000000
|
kernel_alignment: .long 0x1000000
|
||||||
relocatable_kernel: .byte 0
|
relocatable_kernel: .byte 0
|
||||||
min_alignment: .byte 0x10
|
min_alignment: .byte 0x10
|
||||||
xloadflags: .word 0 # none of the flags supported
|
xloadflags: .word 0b01111 # all handover protocols except kexec
|
||||||
cmdline_size: .long 4096-1
|
cmdline_size: .long 4096-1
|
||||||
hardware_subarch: .long 0
|
hardware_subarch: .long 0
|
||||||
hardware_subarch_data: .quad 0
|
hardware_subarch_data: .quad 0
|
||||||
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the runner
|
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the runner
|
||||||
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the runner
|
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the runner
|
||||||
setup_data: .quad 0
|
setup_data: .quad 0
|
||||||
pref_address: .quad 0
|
pref_address: .quad CODE32_START - 0x200 * (SETUP_SECTS + 1);
|
||||||
init_size: .long 0xabababab # at 0x260/4, to be filled by the runner
|
init_size: .long 0xabababab # at 0x260/4, to be filled by the runner
|
||||||
handover_offset: .long 0
|
handover_offset: .long start_of_setup32
|
||||||
kernel_info_offset: .long 0
|
kernel_info_offset: .long 0
|
||||||
hdr_end:
|
hdr_end:
|
||||||
// End of header.
|
|
||||||
|
|
||||||
// 32-bit setup code starts here, and will be loaded at CODE32_START.
|
|
||||||
.section ".header.text", "ax"
|
|
||||||
.code32
|
|
||||||
.global start_of_setup32
|
|
||||||
start_of_setup32:
|
|
||||||
mov eax, offset stack_bottom
|
|
||||||
mov esp, eax
|
|
||||||
mov eax, offset halt
|
|
||||||
push eax # the return address
|
|
||||||
mov ebp, esp
|
|
||||||
add ebp, -4
|
|
||||||
push ebp
|
|
||||||
mov ebp, esp
|
|
||||||
|
|
||||||
.extern _rust_setup_entry
|
|
||||||
push esi # the boot_params pointer
|
|
||||||
call _rust_setup_entry
|
|
||||||
|
|
||||||
// Unreachable here.
|
|
||||||
halt:
|
|
||||||
hlt
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
// A small stack for the setup code.
|
|
||||||
.section ".stack", "aw"
|
|
||||||
SETUP_STACK_SIZE = 0x1000
|
|
||||||
.align 16
|
|
||||||
stack_top:
|
|
||||||
.skip SETUP_STACK_SIZE
|
|
||||||
stack_bottom:
|
|
@ -9,6 +9,8 @@ use core::arch::{asm, global_asm};
|
|||||||
|
|
||||||
global_asm!(include_str!("header.S"));
|
global_asm!(include_str!("header.S"));
|
||||||
|
|
||||||
|
global_asm!(include_str!("setup.S"));
|
||||||
|
|
||||||
unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! {
|
unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! {
|
||||||
asm!("mov esi, {}", in(reg) boot_params_ptr);
|
asm!("mov esi, {}", in(reg) boot_params_ptr);
|
||||||
asm!("mov eax, {}", in(reg) entrypoint);
|
asm!("mov eax, {}", in(reg) entrypoint);
|
39
framework/libs/boot-trojan/trojan/src/setup.S
Normal file
39
framework/libs/boot-trojan/trojan/src/setup.S
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 32-bit setup code starts here, and will be loaded at CODE32_START.
|
||||||
|
.section ".setup", "ax"
|
||||||
|
.code32
|
||||||
|
.global start_of_setup32
|
||||||
|
start_of_setup32:
|
||||||
|
mov eax, offset stack_bottom
|
||||||
|
mov esp, eax
|
||||||
|
mov eax, offset halt
|
||||||
|
push eax # the return address
|
||||||
|
mov ebp, esp
|
||||||
|
add ebp, -4
|
||||||
|
push ebp
|
||||||
|
mov ebp, esp
|
||||||
|
|
||||||
|
.extern _rust_setup_entry
|
||||||
|
push esi # the boot_params pointer
|
||||||
|
call _rust_setup_entry
|
||||||
|
|
||||||
|
// Unreachable here.
|
||||||
|
halt:
|
||||||
|
hlt
|
||||||
|
jmp halt
|
||||||
|
|
||||||
|
.code64
|
||||||
|
.global start_of_setup64
|
||||||
|
.org 0x200
|
||||||
|
start_of_setup64:
|
||||||
|
// Unreachable here.
|
||||||
|
halt64:
|
||||||
|
hlt
|
||||||
|
jmp halt64
|
||||||
|
|
||||||
|
// A small stack for the legacy setup code.
|
||||||
|
.section ".stack", "aw"
|
||||||
|
SETUP_STACK_SIZE = 0x1000
|
||||||
|
.align 16
|
||||||
|
stack_top:
|
||||||
|
.skip SETUP_STACK_SIZE
|
||||||
|
stack_bottom:
|
@ -7,6 +7,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.32"
|
anyhow = "1.0.32"
|
||||||
clap = { version = "4.3.19", features = ["derive"] }
|
clap = { version = "4.3.19", features = ["derive"] }
|
||||||
glob = "0.3.1"
|
aster-boot-trojan-builder = { path = "../framework/libs/boot-trojan/builder" }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
xmas-elf = "0.8.0"
|
xmas-elf = "0.8.0"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
mod linux_boot;
|
use aster_boot_trojan_builder::{build_linux_setup_header_from_trojan, make_bzimage};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
@ -8,8 +8,6 @@ use std::{
|
|||||||
|
|
||||||
use crate::BootProtocol;
|
use crate::BootProtocol;
|
||||||
|
|
||||||
use glob::glob;
|
|
||||||
|
|
||||||
macro_rules! ovmf_prefix {
|
macro_rules! ovmf_prefix {
|
||||||
() => {
|
() => {
|
||||||
// There are 3 optional OVMF builds at your service in the dev image
|
// There are 3 optional OVMF builds at your service in the dev image
|
||||||
@ -70,13 +68,12 @@ pub const GRUB_PREFIX: &str = "/usr/local/grub";
|
|||||||
pub const GRUB_VERSION: &str = "x86_64-efi";
|
pub const GRUB_VERSION: &str = "x86_64-efi";
|
||||||
|
|
||||||
pub fn create_bootdev_image(
|
pub fn create_bootdev_image(
|
||||||
atser_path: PathBuf,
|
aster_path: PathBuf,
|
||||||
initramfs_path: PathBuf,
|
initramfs_path: PathBuf,
|
||||||
grub_cfg: String,
|
grub_cfg: String,
|
||||||
protocol: BootProtocol,
|
protocol: BootProtocol,
|
||||||
release_mode: bool,
|
|
||||||
) -> PathBuf {
|
) -> PathBuf {
|
||||||
let target_dir = atser_path.parent().unwrap();
|
let target_dir = aster_path.parent().unwrap();
|
||||||
let iso_root = target_dir.join("iso_root");
|
let iso_root = target_dir.join("iso_root");
|
||||||
|
|
||||||
// Clear or make the iso dir.
|
// Clear or make the iso dir.
|
||||||
@ -94,26 +91,22 @@ pub fn create_bootdev_image(
|
|||||||
|
|
||||||
let target_path = match protocol {
|
let target_path = match protocol {
|
||||||
BootProtocol::Linux => {
|
BootProtocol::Linux => {
|
||||||
// Find the setup header in the build script output directory.
|
let trojan_install_dir = Path::new("target/-boot-trojan");
|
||||||
let bs_out_dir = if release_mode {
|
build_linux_setup_header_from_trojan(
|
||||||
glob("target/x86_64-custom/release/build/aster-frame-*").unwrap()
|
Path::new("framework/libs/boot-trojan/trojan"),
|
||||||
} else {
|
trojan_install_dir,
|
||||||
glob("target/x86_64-custom/debug/build/aster-frame-*").unwrap()
|
)
|
||||||
};
|
.unwrap();
|
||||||
let header_path = Path::new(bs_out_dir.into_iter().next().unwrap().unwrap().as_path())
|
let header_path = trojan_install_dir.join("bin").join("aster-boot-trojan");
|
||||||
.join("out")
|
|
||||||
.join("bin")
|
|
||||||
.join("aster-frame-x86-boot-linux-setup");
|
|
||||||
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
|
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
|
||||||
let target_path = iso_root.join("boot").join("asterinaz");
|
let target_path = iso_root.join("boot").join("asterinaz");
|
||||||
linux_boot::make_bzimage(&target_path, &atser_path.as_path(), &header_path.as_path())
|
make_bzimage(&target_path, &aster_path.as_path(), &header_path.as_path()).unwrap();
|
||||||
.unwrap();
|
|
||||||
target_path
|
target_path
|
||||||
}
|
}
|
||||||
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
|
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
|
||||||
// Copy the kernel image to the boot directory.
|
// Copy the kernel image to the boot directory.
|
||||||
let target_path = iso_root.join("boot").join("atserinas");
|
let target_path = iso_root.join("boot").join("atserinas");
|
||||||
fs::copy(&atser_path, &target_path).unwrap();
|
fs::copy(&aster_path, &target_path).unwrap();
|
||||||
target_path
|
target_path
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -1,98 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{Read, Write},
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
use xmas_elf::program::{ProgramHeader, SegmentData};
|
|
||||||
|
|
||||||
// We chose the legacy setup sections to be 7 so that the setup header
|
|
||||||
// is page-aligned and the legacy setup section size would be 0x1000.
|
|
||||||
const LEGACY_SETUP_SECS: usize = 7;
|
|
||||||
const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1);
|
|
||||||
const SETUP32_LMA: usize = 0x100000;
|
|
||||||
|
|
||||||
/// We need a binary which satisfies `LMA == File_Offset`, and objcopy
|
|
||||||
/// does not satisfy us well, so we should parse the ELF and do our own
|
|
||||||
/// objcopy job.
|
|
||||||
///
|
|
||||||
/// 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 header_to_raw_binary(elf_file: &[u8]) -> Vec<u8> {
|
|
||||||
let elf = xmas_elf::ElfFile::new(&elf_file).unwrap();
|
|
||||||
let mut bin = Vec::<u8>::new();
|
|
||||||
|
|
||||||
for ph in elf.program_iter() {
|
|
||||||
let ProgramHeader::Ph32(program) = ph else {
|
|
||||||
panic!("Unexpected program header type");
|
|
||||||
};
|
|
||||||
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
|
||||||
let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
|
|
||||||
panic!("Unexpected segment data type");
|
|
||||||
};
|
|
||||||
let dst_file_offset =
|
|
||||||
program.virtual_addr as usize + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA;
|
|
||||||
let dst_file_length = program.file_size as usize;
|
|
||||||
if bin.len() < dst_file_offset + dst_file_length {
|
|
||||||
bin.resize(dst_file_offset + dst_file_length, 0);
|
|
||||||
}
|
|
||||||
let dest_slice = bin[dst_file_offset..dst_file_offset + dst_file_length].as_mut();
|
|
||||||
dest_slice.copy_from_slice(header_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bin
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function sould be used when generating the Linux x86 Boot setup header.
|
|
||||||
/// Some fields in the Linux x86 Boot setup header should be filled after assembled.
|
|
||||||
/// And the filled fields must have the bytes with values of 0xAB. See
|
|
||||||
/// `framework/aster-frame/src/arch/x86/boot/linux_boot/setup/src/header.S` for more
|
|
||||||
/// info on this mechanism.
|
|
||||||
fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
|
|
||||||
let size = value.len();
|
|
||||||
assert_eq!(
|
|
||||||
&header[offset..offset + size],
|
|
||||||
vec![0xABu8; size].as_slice()
|
|
||||||
);
|
|
||||||
header[offset..offset + size].copy_from_slice(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_bzimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> {
|
|
||||||
let mut header = Vec::new();
|
|
||||||
File::open(header_path)?.read_to_end(&mut header)?;
|
|
||||||
let mut header = header_to_raw_binary(&header);
|
|
||||||
// Pad the header to let the payload starts with 8-byte alignment.
|
|
||||||
header.resize((header.len() + 7) & !7, 0x00);
|
|
||||||
|
|
||||||
let mut kernel = Vec::new();
|
|
||||||
File::open(kernel_path)?.read_to_end(&mut kernel)?;
|
|
||||||
|
|
||||||
let header_len = header.len();
|
|
||||||
let kernel_len = kernel.len();
|
|
||||||
|
|
||||||
let payload_offset = header_len - LEGACY_SETUP_SEC_SIZE + SETUP32_LMA;
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x248, /* payload_offset */
|
|
||||||
&(payload_offset as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x24C, /* payload_length */
|
|
||||||
&(kernel_len as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x260, /* init_size */
|
|
||||||
&((header_len + kernel_len) as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut kernel_image = File::create(path)?;
|
|
||||||
kernel_image.write_all(&header)?;
|
|
||||||
kernel_image.write_all(&kernel)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -79,10 +79,6 @@ struct Args {
|
|||||||
/// Run a GDB client instead of running the kernel.
|
/// Run a GDB client instead of running the kernel.
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
run_gdb_client: bool,
|
run_gdb_client: bool,
|
||||||
|
|
||||||
/// Run in the release mode.
|
|
||||||
#[arg(long, default_value_t = false)]
|
|
||||||
release_mode: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const COMMON_ARGS: &[&str] = &[
|
pub const COMMON_ARGS: &[&str] = &[
|
||||||
@ -201,7 +197,6 @@ fn main() {
|
|||||||
initramfs_path,
|
initramfs_path,
|
||||||
grub_cfg,
|
grub_cfg,
|
||||||
args.boot_protocol,
|
args.boot_protocol,
|
||||||
args.release_mode,
|
|
||||||
);
|
);
|
||||||
qemu_cmd.arg("-cdrom");
|
qemu_cmd.arg("-cdrom");
|
||||||
qemu_cmd.arg(bootdev_image.as_os_str());
|
qemu_cmd.arg(bootdev_image.as_os_str());
|
||||||
|
Reference in New Issue
Block a user