mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 18:03:25 +00:00
Rename trojan to wrapper and add docs
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
12d01ca1e4
commit
e71c2701d6
16
framework/libs/boot-wrapper/builder/Cargo.toml
Normal file
16
framework/libs/boot-wrapper/builder/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "aster-boot-wrapper-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"
|
||||
|
||||
[features]
|
||||
default = ["trojan64"]
|
||||
trojan64 = []
|
220
framework/libs/boot-wrapper/builder/src/lib.rs
Normal file
220
framework/libs/boot-wrapper/builder/src/lib.rs
Normal file
@ -0,0 +1,220 @@
|
||||
//! The linux boot wrapper builder.
|
||||
//!
|
||||
//! 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
|
||||
//! 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.
|
||||
//!
|
||||
//! The wrapper should be built into the ELF target and we convert it to a flat binary
|
||||
//! in the builder.
|
||||
|
||||
mod mapping;
|
||||
mod pe_header;
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use xmas_elf::program::SegmentData;
|
||||
|
||||
use mapping::{WrapperFileOffset, WrapperVA};
|
||||
|
||||
/// 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 program in elf.program_iter() {
|
||||
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(WrapperFileOffset::from(WrapperVA::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,
|
||||
trojan_len: usize,
|
||||
payload_offset: WrapperVA,
|
||||
) {
|
||||
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 */
|
||||
&((trojan_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);
|
||||
|
||||
#[cfg(not(feature = "trojan64"))]
|
||||
let wrapper = {
|
||||
let arch = trojan_src
|
||||
.join("x86_64-i386_pm-none.json")
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
build_trojan_with_arch(trojan_src, trojan_out, &TrojanBuildArch::Other(arch))
|
||||
};
|
||||
|
||||
let mut trojan_elf = Vec::new();
|
||||
File::open(wrapper)
|
||||
.unwrap()
|
||||
.read_to_end(&mut trojan_elf)
|
||||
.unwrap();
|
||||
let mut wrapper = trojan_to_flat_binary(&trojan_elf);
|
||||
// Pad the header with 8-byte alignment.
|
||||
wrapper.resize((wrapper.len() + 7) & !7, 0x00);
|
||||
|
||||
let mut kernel = Vec::new();
|
||||
File::open(kernel_path)
|
||||
.unwrap()
|
||||
.read_to_end(&mut kernel)
|
||||
.unwrap();
|
||||
let payload = kernel;
|
||||
|
||||
let trojan_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 mut kernel_image = File::create(path).unwrap();
|
||||
kernel_image.write_all(&wrapper).unwrap();
|
||||
kernel_image.write_all(&payload).unwrap();
|
||||
|
||||
let image_size = trojan_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(&trojan_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
|
||||
.seek(SeekFrom::Start(usize::from(pe_header.relocs.0) as u64))
|
||||
.unwrap();
|
||||
kernel_image.write_all(&pe_header.relocs.1).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
X86_64,
|
||||
Other(PathBuf),
|
||||
}
|
||||
|
||||
/// Build the trojan 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 {
|
||||
if !out_dir.exists() {
|
||||
std::fs::create_dir_all(&out_dir).unwrap();
|
||||
}
|
||||
let out_dir = std::fs::canonicalize(out_dir).unwrap();
|
||||
|
||||
// Relocations are fewer in release mode. That's why the release mode is more stable than
|
||||
// the debug mode.
|
||||
let profile = "release";
|
||||
|
||||
let cargo = std::env::var("CARGO").unwrap();
|
||||
let mut cmd = std::process::Command::new(cargo);
|
||||
cmd.current_dir(source_dir);
|
||||
cmd.arg("build");
|
||||
if profile == "release" {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
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(),
|
||||
});
|
||||
cmd.arg("-Zbuild-std=core,alloc,compiler_builtins");
|
||||
cmd.arg("-Zbuild-std-features=compiler-builtins-mem");
|
||||
// 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 mut child = cmd.spawn().unwrap();
|
||||
let status = child.wait().unwrap();
|
||||
if !status.success() {
|
||||
panic!(
|
||||
"Failed to build linux x86 setup header:\n\tcommand `{:?}`\n\treturned {}",
|
||||
cmd, status
|
||||
);
|
||||
}
|
||||
|
||||
// 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(),
|
||||
};
|
||||
|
||||
let trojan_artifact = out_dir
|
||||
.join(arch_name)
|
||||
.join(profile)
|
||||
.join("aster-boot-wrapper");
|
||||
|
||||
trojan_artifact.to_owned()
|
||||
}
|
103
framework/libs/boot-wrapper/builder/src/mapping.rs
Normal file
103
framework/libs/boot-wrapper/builder/src/mapping.rs
Normal file
@ -0,0 +1,103 @@
|
||||
//! In the wrapper, VA - SETUP32_LMA == FileOffset - LEGACY_SETUP_SEC_SIZE.
|
||||
//! And the addresses are specified in the ELF file.
|
||||
//!
|
||||
//! This module centralizes the conversion between VA and FileOffset.
|
||||
|
||||
use std::{
|
||||
cmp::PartialOrd,
|
||||
convert::From,
|
||||
ops::{Add, 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 WrapperVA {
|
||||
addr: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub struct WrapperFileOffset {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl From<usize> for WrapperVA {
|
||||
fn from(addr: usize) -> Self {
|
||||
Self { addr }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapperVA> for usize {
|
||||
fn from(va: WrapperVA) -> Self {
|
||||
va.addr
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for WrapperVA {
|
||||
type Output = usize;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.addr - rhs.addr
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for WrapperVA {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self {
|
||||
addr: self.addr + rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for WrapperFileOffset {
|
||||
fn from(offset: usize) -> Self {
|
||||
Self { offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapperFileOffset> for usize {
|
||||
fn from(offset: WrapperFileOffset) -> Self {
|
||||
offset.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for WrapperFileOffset {
|
||||
type Output = usize;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.offset - rhs.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for WrapperFileOffset {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self {
|
||||
offset: self.offset + rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapperVA> for WrapperFileOffset {
|
||||
fn from(va: WrapperVA) -> Self {
|
||||
Self {
|
||||
offset: va.addr + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapperFileOffset> for WrapperVA {
|
||||
fn from(offset: WrapperFileOffset) -> Self {
|
||||
Self {
|
||||
addr: offset.offset + SETUP32_LMA - LEGACY_SETUP_SEC_SIZE,
|
||||
}
|
||||
}
|
||||
}
|
458
framework/libs/boot-wrapper/builder/src/pe_header.rs
Normal file
458
framework/libs/boot-wrapper/builder/src/pe_header.rs
Normal file
@ -0,0 +1,458 @@
|
||||
//! 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::{WrapperFileOffset, WrapperVA, 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
|
||||
}
|
||||
|
||||
#[derive(Zeroable, Pod, Serialize, Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
struct Pe32PlusOptDataDirEnt {
|
||||
/// The RVA is the address of the table relative to the base address of the image when the table is loaded.
|
||||
rva: u32,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl Pe32PlusOptDataDirEnt {
|
||||
fn none() -> Self {
|
||||
Self { rva: 0, size: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// The data directories in the PE32+ optional header.
|
||||
///
|
||||
/// The `data_dirs` number field in the PE32+ optional header is just an illusion that you can choose to have a
|
||||
/// subset of the data directories. The actual number of data directories is fixed to 16 and you can only ignore
|
||||
/// data directories at the end of the list. We ignore data directories after the 8th as what Linux do.
|
||||
#[derive(Zeroable, Pod, Serialize, Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
struct Pe32PlusOptDataDirs {
|
||||
export_table: Pe32PlusOptDataDirEnt,
|
||||
import_table: Pe32PlusOptDataDirEnt,
|
||||
resource_table: Pe32PlusOptDataDirEnt,
|
||||
exception_table: Pe32PlusOptDataDirEnt,
|
||||
certificate_table: Pe32PlusOptDataDirEnt,
|
||||
base_relocation_table: Pe32PlusOptDataDirEnt,
|
||||
}
|
||||
|
||||
impl Pe32PlusOptDataDirs {
|
||||
fn num_dirs() -> usize {
|
||||
size_of::<Self>() / size_of::<Pe32PlusOptDataDirEnt>()
|
||||
}
|
||||
}
|
||||
|
||||
// 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 text: Range<WrapperVA>,
|
||||
pub data: Range<WrapperVA>,
|
||||
pub bss: Range<WrapperVA>,
|
||||
/// All the readonly but loaded sections.
|
||||
pub rodata: Range<WrapperVA>,
|
||||
}
|
||||
|
||||
impl TrojanSectionAddrInfo {
|
||||
fn from(elf: &xmas_elf::ElfFile) -> Self {
|
||||
let mut text_start = None;
|
||||
let mut text_end = None;
|
||||
let mut data_start = None;
|
||||
let mut data_end = None;
|
||||
let mut bss_start = None;
|
||||
let mut bss_end = None;
|
||||
let mut rodata_start = None;
|
||||
let mut rodata_end = None;
|
||||
for program in elf.program_iter() {
|
||||
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||
let offset = WrapperVA::from(program.virtual_addr() as usize);
|
||||
let length = program.mem_size() as usize;
|
||||
if program.flags().is_execute() {
|
||||
text_start = Some(offset);
|
||||
text_end = Some(offset + length);
|
||||
} else if program.flags().is_write() {
|
||||
data_start = Some(offset);
|
||||
data_end = Some(offset + program.file_size() as usize);
|
||||
bss_start = Some(offset + program.file_size() as usize);
|
||||
bss_end = Some(offset + length);
|
||||
} else if program.flags().is_read() {
|
||||
rodata_start = Some(offset);
|
||||
rodata_end = Some(offset + length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
text: WrapperVA::from(text_start.unwrap())..WrapperVA::from(text_end.unwrap()),
|
||||
data: WrapperVA::from(data_start.unwrap())..WrapperVA::from(data_end.unwrap()),
|
||||
bss: WrapperVA::from(bss_start.unwrap())..WrapperVA::from(bss_end.unwrap()),
|
||||
rodata: WrapperVA::from(rodata_start.unwrap())..WrapperVA::from(rodata_end.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_virt_size(&self) -> usize {
|
||||
self.text.end - self.text.start
|
||||
}
|
||||
|
||||
fn text_file_size(&self) -> usize {
|
||||
WrapperFileOffset::from(self.text.end) - WrapperFileOffset::from(self.text.start)
|
||||
}
|
||||
|
||||
fn data_virt_size(&self) -> usize {
|
||||
self.data.end - self.data.start
|
||||
}
|
||||
|
||||
fn data_file_size(&self) -> usize {
|
||||
WrapperFileOffset::from(self.data.end) - WrapperFileOffset::from(self.data.start)
|
||||
}
|
||||
|
||||
fn bss_virt_size(&self) -> usize {
|
||||
self.bss.end - self.bss.start
|
||||
}
|
||||
|
||||
fn rodata_virt_size(&self) -> usize {
|
||||
self.rodata.end - self.rodata.start
|
||||
}
|
||||
|
||||
fn rodata_file_size(&self) -> usize {
|
||||
WrapperFileOffset::from(self.rodata.end) - WrapperFileOffset::from(self.rodata.start)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrojanPeCoffHeaderBuf {
|
||||
pub header_at_zero: Vec<u8>,
|
||||
pub relocs: (WrapperFileOffset, 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();
|
||||
|
||||
// The EFI application loader requires a relocation section.
|
||||
let relocs = vec![];
|
||||
// The place where we put the stub, must be after the legacy header and before 0x1000.
|
||||
let reloc_offset = WrapperFileOffset::from(0x500);
|
||||
|
||||
// PE header
|
||||
let mut pe_hdr = PeHdr {
|
||||
magic: PE_MAGIC,
|
||||
machine: PeMachineType::Amd64 as u16,
|
||||
sections: 0, // this field will be modified later
|
||||
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: Pe32PlusOptDataDirs::num_dirs() as u32,
|
||||
};
|
||||
|
||||
let pe_opt_hdr_data_dirs = Pe32PlusOptDataDirs {
|
||||
export_table: Pe32PlusOptDataDirEnt::none(),
|
||||
import_table: Pe32PlusOptDataDirEnt::none(),
|
||||
resource_table: Pe32PlusOptDataDirEnt::none(),
|
||||
exception_table: Pe32PlusOptDataDirEnt::none(),
|
||||
certificate_table: Pe32PlusOptDataDirEnt::none(),
|
||||
base_relocation_table: Pe32PlusOptDataDirEnt {
|
||||
rva: usize::from(reloc_offset) as u32,
|
||||
size: relocs.len() as u32,
|
||||
},
|
||||
};
|
||||
|
||||
let addr_info = TrojanSectionAddrInfo::from(&elf);
|
||||
|
||||
// PE section headers
|
||||
let mut sec_hdrs = Vec::<PeSectionHdr>::new();
|
||||
// .reloc
|
||||
sec_hdrs.push(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(WrapperVA::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,
|
||||
});
|
||||
// .text
|
||||
sec_hdrs.push(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(WrapperFileOffset::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,
|
||||
});
|
||||
// .data
|
||||
sec_hdrs.push(PeSectionHdr {
|
||||
name: [b'.', b'd', b'a', b't', b'a', 0, 0, 0],
|
||||
virtual_size: addr_info.data_virt_size() as u32,
|
||||
virtual_address: usize::from(addr_info.data.start) as u32,
|
||||
raw_data_size: addr_info.data_file_size() as u32,
|
||||
data_addr: usize::from(WrapperFileOffset::from(addr_info.data.start)) as u32,
|
||||
relocs: 0,
|
||||
line_numbers: 0,
|
||||
num_relocs: 0,
|
||||
num_lin_numbers: 0,
|
||||
flags: (PeSectionHdrFlags::CNT_INITIALIZED_DATA
|
||||
| PeSectionHdrFlags::MEM_READ
|
||||
| PeSectionHdrFlags::MEM_WRITE)
|
||||
.bits
|
||||
| PeSectionHdrFlagsAlign::_16Bytes as u32,
|
||||
});
|
||||
// .bss
|
||||
sec_hdrs.push(PeSectionHdr {
|
||||
name: [b'.', b'b', b's', b's', 0, 0, 0, 0],
|
||||
virtual_size: addr_info.bss_virt_size() as u32,
|
||||
virtual_address: usize::from(addr_info.bss.start) as u32,
|
||||
raw_data_size: 0,
|
||||
data_addr: 0,
|
||||
relocs: 0,
|
||||
line_numbers: 0,
|
||||
num_relocs: 0,
|
||||
num_lin_numbers: 0,
|
||||
flags: (PeSectionHdrFlags::CNT_UNINITIALIZED_DATA
|
||||
| PeSectionHdrFlags::MEM_READ
|
||||
| PeSectionHdrFlags::MEM_WRITE)
|
||||
.bits
|
||||
| PeSectionHdrFlagsAlign::_16Bytes as u32,
|
||||
});
|
||||
// .rodata
|
||||
sec_hdrs.push(PeSectionHdr {
|
||||
name: [b'.', b'r', b'o', b'd', b'a', b't', b'a', 0],
|
||||
virtual_size: addr_info.rodata_virt_size() as u32,
|
||||
virtual_address: usize::from(addr_info.rodata.start) as u32,
|
||||
raw_data_size: addr_info.rodata_file_size() as u32,
|
||||
data_addr: usize::from(WrapperFileOffset::from(addr_info.rodata.start)) as u32,
|
||||
relocs: 0,
|
||||
line_numbers: 0,
|
||||
num_relocs: 0,
|
||||
num_lin_numbers: 0,
|
||||
flags: (PeSectionHdrFlags::CNT_INITIALIZED_DATA | PeSectionHdrFlags::MEM_READ).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
|
||||
pe_hdr.sections = sec_hdrs.len() as u16;
|
||||
bin.extend_from_slice(bytemuck::bytes_of(&pe_hdr));
|
||||
// Write the PE32+ optional header
|
||||
bin.extend_from_slice(bytemuck::bytes_of(&pe_opt_hdr));
|
||||
bin.extend_from_slice(bytemuck::bytes_of(&pe_opt_hdr_data_dirs));
|
||||
// Write the PE section headers
|
||||
for sec_hdr in sec_hdrs {
|
||||
bin.extend_from_slice(bytemuck::bytes_of(&sec_hdr));
|
||||
}
|
||||
|
||||
TrojanPeCoffHeaderBuf {
|
||||
header_at_zero: bin,
|
||||
relocs: (reloc_offset, relocs),
|
||||
}
|
||||
}
|
8
framework/libs/boot-wrapper/linux-boot-params/Cargo.toml
Normal file
8
framework/libs/boot-wrapper/linux-boot-params/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "linux_boot_params"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
293
framework/libs/boot-wrapper/linux-boot-params/src/lib.rs
Normal file
293
framework/libs/boot-wrapper/linux-boot-params/src/lib.rs
Normal file
@ -0,0 +1,293 @@
|
||||
//! The Linux Boot Protocol boot_params definition.
|
||||
//!
|
||||
//! The bootloader will deliver the address of the `BootParams` struct
|
||||
//! as the argument of the kernel entrypoint. So we must define a Linux
|
||||
//! ABI compatible struct in Rust, despite that most of the fields are
|
||||
//! currently not needed by Asterinas.
|
||||
//!
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct ScreenInfo {
|
||||
pub orig_x: u8, /* 0x00 */
|
||||
pub orig_y: u8, /* 0x01 */
|
||||
pub ext_mem_k: u16, /* 0x02 */
|
||||
pub orig_video_page: u16, /* 0x04 */
|
||||
pub orig_video_mode: u8, /* 0x06 */
|
||||
pub orig_video_cols: u8, /* 0x07 */
|
||||
pub flags: u8, /* 0x08 */
|
||||
pub unused2: u8, /* 0x09 */
|
||||
pub orig_video_ega_bx: u16, /* 0x0a */
|
||||
pub unused3: u16, /* 0x0c */
|
||||
pub orig_video_lines: u8, /* 0x0e */
|
||||
pub orig_video_is_vga: u8, /* 0x0f */
|
||||
pub orig_video_points: u16, /* 0x10 */
|
||||
|
||||
/* VESA graphic mode -- linear frame buffer */
|
||||
pub lfb_width: u16, /* 0x12 */
|
||||
pub lfb_height: u16, /* 0x14 */
|
||||
pub lfb_depth: u16, /* 0x16 */
|
||||
pub lfb_base: u32, /* 0x18 */
|
||||
pub lfb_size: u32, /* 0x1c */
|
||||
pub cl_magic: u16,
|
||||
pub cl_offset: u16, /* 0x20 */
|
||||
pub lfb_linelength: u16, /* 0x24 */
|
||||
pub red_size: u8, /* 0x26 */
|
||||
pub red_pos: u8, /* 0x27 */
|
||||
pub green_size: u8, /* 0x28 */
|
||||
pub green_pos: u8, /* 0x29 */
|
||||
pub blue_size: u8, /* 0x2a */
|
||||
pub blue_pos: u8, /* 0x2b */
|
||||
pub rsvd_size: u8, /* 0x2c */
|
||||
pub rsvd_pos: u8, /* 0x2d */
|
||||
pub vesapm_seg: u16, /* 0x2e */
|
||||
pub vesapm_off: u16, /* 0x30 */
|
||||
pub pages: u16, /* 0x32 */
|
||||
pub vesa_attributes: u16, /* 0x34 */
|
||||
pub capabilities: u32, /* 0x36 */
|
||||
pub ext_lfb_base: u32, /* 0x3a */
|
||||
pub _reserved: [u8; 2], /* 0x3e */
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct ApmBiosInfo {
|
||||
pub version: u16,
|
||||
pub cseg: u16,
|
||||
pub offset: u32,
|
||||
pub cseg_16: u16,
|
||||
pub dseg: u16,
|
||||
pub flags: u16,
|
||||
pub cseg_len: u16,
|
||||
pub cseg_16_len: u16,
|
||||
pub dseg_len: u16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct IstInfo {
|
||||
pub signature: u32,
|
||||
pub command: u32,
|
||||
pub event: u32,
|
||||
pub perf_level: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct SysDescTable {
|
||||
pub length: u16,
|
||||
pub table: [u8; 14],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct OlpcOfwHeader {
|
||||
pub ofw_magic: u32, /* OFW signature */
|
||||
pub ofw_version: u32,
|
||||
pub cif_handler: u32, /* callback into OFW */
|
||||
pub irq_desc_table: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct EdidInfo {
|
||||
pub dummy: [u8; 128],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct EfiInfo {
|
||||
pub efi_loader_signature: u32,
|
||||
pub efi_systab: u32,
|
||||
pub efi_memdesc_size: u32,
|
||||
pub efi_memdesc_version: u32,
|
||||
pub efi_memmap: u32,
|
||||
pub efi_memmap_size: u32,
|
||||
pub efi_systab_hi: u32,
|
||||
pub efi_memmap_hi: u32,
|
||||
}
|
||||
|
||||
/// Magic stored in SetupHeader.header.
|
||||
pub const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448;
|
||||
|
||||
/// Linux Boot Protocol Header.
|
||||
///
|
||||
/// Originally defined in the linux source tree:
|
||||
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct SetupHeader {
|
||||
pub setup_sects: u8,
|
||||
pub root_flags: u16,
|
||||
pub syssize: u32,
|
||||
pub ram_size: u16,
|
||||
pub vid_mode: u16,
|
||||
pub root_dev: u16,
|
||||
pub boot_flag: u16,
|
||||
pub jump: u16,
|
||||
pub header: u32,
|
||||
pub version: u16,
|
||||
pub realmode_swtch: u32,
|
||||
pub start_sys_seg: u16,
|
||||
pub kernel_version: u16,
|
||||
pub type_of_loader: u8,
|
||||
pub loadflags: u8,
|
||||
pub setup_move_size: u16,
|
||||
pub code32_start: u32,
|
||||
pub ramdisk_image: u32,
|
||||
pub ramdisk_size: u32,
|
||||
pub bootsect_kludge: u32,
|
||||
pub heap_end_ptr: u16,
|
||||
pub ext_loader_ver: u8,
|
||||
pub ext_loader_type: u8,
|
||||
pub cmd_line_ptr: u32,
|
||||
pub initrd_addr_max: u32,
|
||||
pub kernel_alignment: u32,
|
||||
pub relocatable_kernel: u8,
|
||||
pub min_alignment: u8,
|
||||
pub xloadflags: u16,
|
||||
pub cmdline_size: u32,
|
||||
pub hardware_subarch: u32,
|
||||
pub hardware_subarch_data: u64,
|
||||
pub payload_offset: u32,
|
||||
pub payload_length: u32,
|
||||
pub setup_data: u64,
|
||||
pub pref_address: u64,
|
||||
pub init_size: u32,
|
||||
pub handover_offset: u32,
|
||||
pub kernel_info_offset: u32,
|
||||
}
|
||||
|
||||
/// The E820 types known to the kernel.
|
||||
///
|
||||
/// Originally defined in the linux source tree:
|
||||
/// `linux/arch/x86/include/asm/e820/types.h`
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum E820Type {
|
||||
Ram = 1,
|
||||
Reserved = 2,
|
||||
Acpi = 3,
|
||||
Nvs = 4,
|
||||
Unusable = 5,
|
||||
Pmem = 7,
|
||||
/*
|
||||
* This is a non-standardized way to represent ADR or
|
||||
* NVDIMM regions that persist over a reboot.
|
||||
*
|
||||
* The kernel will ignore their special capabilities
|
||||
* unless the CONFIG_X86_PMEM_LEGACY=y option is set.
|
||||
*
|
||||
* ( Note that older platforms also used 6 for the same
|
||||
* type of memory, but newer versions switched to 12 as
|
||||
* 6 was assigned differently. Some time they will learn... )
|
||||
*/
|
||||
Pram = 12,
|
||||
/*
|
||||
* Special-purpose memory is indicated to the system via the
|
||||
* EFI_MEMORY_SP attribute. Define an e820 translation of this
|
||||
* memory type for the purpose of reserving this range and
|
||||
* marking it with the IORES_DESC_SOFT_RESERVED designation.
|
||||
*/
|
||||
SoftReserved = 0xefffffff,
|
||||
/*
|
||||
* Reserved RAM used by the kernel itself if
|
||||
* CONFIG_INTEL_TXT=y is enabled, memory of this type
|
||||
* will be included in the S3 integrity calculation
|
||||
* and so should not include any memory that the BIOS
|
||||
* might alter over the S3 transition:
|
||||
*/
|
||||
ReservedKern = 128,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct BootE820Entry {
|
||||
pub addr: u64,
|
||||
pub size: u64,
|
||||
pub typ: E820Type,
|
||||
}
|
||||
|
||||
const E820_MAX_ENTRIES_ZEROPAGE: usize = 128;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct EddDeviceParams {
|
||||
// TODO: We currently have no plans to support the edd device,
|
||||
// and we need unnamed fields (Rust RFC 2102) to implement this
|
||||
// FFI neatly. So we put a dummy implementation here conforming
|
||||
// to the BootParams struct ABI.
|
||||
pub _dummy: [u8; (0xeec - 0xd00) / 6 - 8],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct EddInfo {
|
||||
pub device: u8,
|
||||
pub version: u8,
|
||||
pub interface_support: u16,
|
||||
pub legacy_max_cylinder: u16,
|
||||
pub legacy_max_head: u8,
|
||||
pub legacy_sectors_per_track: u8,
|
||||
pub params: EddDeviceParams,
|
||||
}
|
||||
|
||||
const EDD_MBR_SIG_MAX: usize = 16;
|
||||
const EDDMAXNR: usize = 6;
|
||||
|
||||
/// Linux 32/64-bit Boot Protocol parameter struct.
|
||||
///
|
||||
/// Originally defined in the linux source tree:
|
||||
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct BootParams {
|
||||
pub screen_info: ScreenInfo, /* 0x000 */
|
||||
pub apm_bios_info: ApmBiosInfo, /* 0x040 */
|
||||
pub _pad2: [u8; 4], /* 0x054 */
|
||||
pub tboot_addr: u64, /* 0x058 */
|
||||
pub ist_info: IstInfo, /* 0x060 */
|
||||
pub acpi_rsdp_addr: u64, /* 0x070 */
|
||||
pub _pad3: [u8; 8], /* 0x078 */
|
||||
pub hd0_info: [u8; 16], /* obsolete! 0x080 */
|
||||
pub hd1_info: [u8; 16], /* obsolete! 0x090 */
|
||||
pub sys_desc_table: SysDescTable, /* obsolete! 0x0a0 */
|
||||
pub olpc_ofw_header: OlpcOfwHeader, /* 0x0b0 */
|
||||
pub ext_ramdisk_image: u32, /* 0x0c0 */
|
||||
pub ext_ramdisk_size: u32, /* 0x0c4 */
|
||||
pub ext_cmd_line_ptr: u32, /* 0x0c8 */
|
||||
pub _pad4: [u8; 112], /* 0x0cc */
|
||||
pub cc_blob_address: u32, /* 0x13c */
|
||||
pub edid_info: EdidInfo, /* 0x140 */
|
||||
pub efi_info: EfiInfo, /* 0x1c0 */
|
||||
pub alt_mem_k: u32, /* 0x1e0 */
|
||||
pub scratch: u32, /* Scratch field! 0x1e4 */
|
||||
pub e820_entries: u8, /* 0x1e8 */
|
||||
pub eddbuf_entries: u8, /* 0x1e9 */
|
||||
pub edd_mbr_sig_buf_entries: u8, /* 0x1ea */
|
||||
pub kbd_status: u8, /* 0x1eb */
|
||||
pub secure_boot: u8, /* 0x1ec */
|
||||
pub _pad5: [u8; 2], /* 0x1ed */
|
||||
/*
|
||||
* The sentinel is set to a nonzero value (0xff) in header.S.
|
||||
*
|
||||
* A bootloader is supposed to only take setup_header and put
|
||||
* it into a clean boot_params buffer. If it turns out that
|
||||
* it is clumsy or too generous with the buffer, it most
|
||||
* probably will pick up the sentinel variable too. The fact
|
||||
* that this variable then is still 0xff will let kernel
|
||||
* know that some variables in boot_params are invalid and
|
||||
* kernel should zero out certain portions of boot_params.
|
||||
*/
|
||||
pub sentinel: u8, /* 0x1ef */
|
||||
pub _pad6: [u8; 1], /* 0x1f0 */
|
||||
pub hdr: SetupHeader, /* setup header 0x1f1 */
|
||||
pub _pad7: [u8; 0x290 - 0x1f1 - core::mem::size_of::<SetupHeader>()],
|
||||
pub edd_mbr_sig_buffer: [u32; EDD_MBR_SIG_MAX], /* 0x290 */
|
||||
pub e820_table: [BootE820Entry; E820_MAX_ENTRIES_ZEROPAGE], /* 0x2d0 */
|
||||
pub _pad8: [u8; 48], /* 0xcd0 */
|
||||
pub eddbuf: [EddInfo; EDDMAXNR], /* 0xd00 */
|
||||
pub _pad9: [u8; 276], /* 0xeec */
|
||||
}
|
9
framework/libs/boot-wrapper/wrapper/.cargo/config.toml
Normal file
9
framework/libs/boot-wrapper/wrapper/.cargo/config.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[target.x86_64-unknown-none]
|
||||
rustflags = [
|
||||
"-Ccode-model=kernel",
|
||||
"-Crelocation-model=pie",
|
||||
"-Ctarget-feature=+crt-static",
|
||||
"-Zplt=yes",
|
||||
"-Zrelax-elf-relocations=yes",
|
||||
"-Zrelro-level=full",
|
||||
]
|
23
framework/libs/boot-wrapper/wrapper/Cargo.toml
Normal file
23
framework/libs/boot-wrapper/wrapper/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "aster-boot-wrapper"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
linux_boot_params = { path = "../linux-boot-params" }
|
||||
uart_16550 = "0.3.0"
|
||||
xmas-elf = "0.8.0"
|
||||
|
||||
[target.x86_64-unknown-none.dependencies]
|
||||
bitflags = "2.4.1"
|
||||
log = "0.4.20"
|
||||
uefi = "0.26.0"
|
||||
uefi-services = "0.23.0"
|
||||
x86_64 = "0.14.11"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
debug_print = []
|
15
framework/libs/boot-wrapper/wrapper/build.rs
Normal file
15
framework/libs/boot-wrapper/wrapper/build.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let target_arch = std::env::var("TARGET").unwrap();
|
||||
let linker_script = if target_arch == "x86_64-unknown-none" {
|
||||
source_dir.join("src/x86/amd64_efi/linker.ld")
|
||||
} else if target_arch == "x86_64-i386_pm-none" {
|
||||
source_dir.join("src/x86/legacy_i386/linker.ld")
|
||||
} else {
|
||||
panic!("Unsupported target_arch: {}", target_arch);
|
||||
};
|
||||
println!("cargo:rerun-if-changed={}", linker_script.display());
|
||||
println!("cargo:rustc-link-arg=-T{}", linker_script.display());
|
||||
}
|
89
framework/libs/boot-wrapper/wrapper/src/console.rs
Normal file
89
framework/libs/boot-wrapper/wrapper/src/console.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
use uart_16550::SerialPort;
|
||||
|
||||
struct Stdout {
|
||||
serial_port: SerialPort,
|
||||
}
|
||||
|
||||
static mut STDOUT: Stdout = Stdout {
|
||||
serial_port: unsafe { SerialPort::new(0x0) },
|
||||
};
|
||||
|
||||
/// safety: this function must only be called once
|
||||
pub unsafe fn init() {
|
||||
STDOUT = Stdout::init();
|
||||
}
|
||||
|
||||
impl Stdout {
|
||||
/// safety: this function must only be called once
|
||||
pub unsafe fn init() -> Self {
|
||||
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
|
||||
serial_port.init();
|
||||
Self { serial_port }
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.serial_port.write_str(s).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This is used when dyn Trait is not supported or fmt::Arguments is fragile to use in PIE.
|
||||
///
|
||||
/// Safety: init() must be called before print_str() and there should be no race conditions.
|
||||
pub unsafe fn print_str(s: &str) {
|
||||
STDOUT.write_str(s).unwrap();
|
||||
}
|
||||
|
||||
unsafe fn print_char(c: char) {
|
||||
STDOUT.serial_port.send(c as u8);
|
||||
}
|
||||
|
||||
/// This is used when dyn Trait is not supported or fmt::Arguments is fragile to use in PIE.
|
||||
///
|
||||
/// Safety: init() must be called before print_hex() and there should be no race conditions.
|
||||
pub unsafe fn print_hex(n: u64) {
|
||||
print_str("0x");
|
||||
for i in (0..16).rev() {
|
||||
let digit = (n >> (i * 4)) & 0xf;
|
||||
if digit < 10 {
|
||||
print_char((b'0' + digit as u8) as char);
|
||||
} else {
|
||||
print_char((b'A' + (digit - 10) as u8) as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Figure out why fmt::Arguments wont work even if relocations are applied.
|
||||
// We just settle on simple print functions for now.
|
||||
/*--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Glue code for print!() and println!() macros.
|
||||
///
|
||||
/// Safety: init() must be called before print_fmt() and there should be no race conditions.
|
||||
pub unsafe fn print_fmt(args: fmt::Arguments) {
|
||||
STDOUT.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
unsafe {
|
||||
$crate::console::print_fmt(format_args!($fmt $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
unsafe {
|
||||
$crate::console::print_fmt(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*------------------------------------------------------------------------------------------------*/
|
61
framework/libs/boot-wrapper/wrapper/src/loader.rs
Normal file
61
framework/libs/boot-wrapper/wrapper/src/loader.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use xmas_elf::program::{ProgramHeader, SegmentData};
|
||||
|
||||
/// TODO: remove this and use copy_from_slice instead
|
||||
///
|
||||
/// We use a custom memcpy because the standard library's compiler's builtin memcpy
|
||||
/// fails for some unknown reason.
|
||||
unsafe fn memcpy(dst: *mut u8, src: *const u8, size: usize) {
|
||||
let mut i = 0;
|
||||
while i < size {
|
||||
*dst.add(i) = *src.add(i);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_elf(file: &[u8]) {
|
||||
let elf = xmas_elf::ElfFile::new(file).unwrap();
|
||||
|
||||
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.");
|
||||
};
|
||||
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||
load_segment(&elf, program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHeader64) {
|
||||
let SegmentData::Undefined(header_data) = program.get_data(&file).unwrap() else {
|
||||
panic!("[setup] Unexpected segment data type!");
|
||||
};
|
||||
// Safety: the physical address from the ELF file is valid
|
||||
let dst_slice = unsafe {
|
||||
core::slice::from_raw_parts_mut(program.physical_addr as *mut u8, program.mem_size as usize)
|
||||
};
|
||||
/* crate::println!(
|
||||
"[setup loader debug] loading ELF segment at {:#x}, size = {:#x}",
|
||||
program.physical_addr,
|
||||
program.mem_size,
|
||||
); */
|
||||
#[cfg(feature = "debug_print")]
|
||||
unsafe {
|
||||
use crate::console::{print_hex, print_str};
|
||||
print_str("[setup loader debug] loading ELF segment at ");
|
||||
print_hex(program.physical_addr as u64);
|
||||
print_str(", size = ");
|
||||
print_hex(program.mem_size as u64);
|
||||
print_str("\n");
|
||||
}
|
||||
// Safety: the ELF file is valid
|
||||
// dst_slice[..program.file_size as usize].copy_from_slice(header_data);
|
||||
unsafe {
|
||||
memcpy(
|
||||
dst_slice.as_mut_ptr(),
|
||||
header_data.as_ptr(),
|
||||
program.file_size as usize,
|
||||
);
|
||||
}
|
||||
let zero_slice = &mut dst_slice[program.file_size as usize..];
|
||||
zero_slice.fill(0);
|
||||
}
|
42
framework/libs/boot-wrapper/wrapper/src/main.rs
Normal file
42
framework/libs/boot-wrapper/wrapper/src/main.rs
Normal file
@ -0,0 +1,42 @@
|
||||
//! The linux boot wrapper binary.
|
||||
//!
|
||||
//! With respect to the format of the bzImage, we design our boot wrapper in the similar
|
||||
//! role as the setup code in the linux kernel. The setup code is responsible for
|
||||
//! initializing the machine state, decompressing and loading the kernel image into memory.
|
||||
//! So does our boot wrapper.
|
||||
//!
|
||||
//! The boot wrapper code is concatenated to the bzImage, and it contains both the linux
|
||||
//! boot header and the PE/COFF header to be a valid UEFI image. The wrapper also supports
|
||||
//! the legacy 32 bit boot protocol, but the support for the legacy boot protocol does not
|
||||
//! co-exist with the UEFI boot protocol. Users can choose either one of them. By specifying
|
||||
//! the target as `x86_64-unknown-none` it supports UEFI protocols. And if the target is
|
||||
//! `x86_64-i386_pm-none` it supports the legacy boot protocol.
|
||||
//!
|
||||
//! The building process of the bzImage and the generation of the PE/COFF header is done
|
||||
//! by the linux-boot-wrapper-builder crate. And the code of the wrapper is in this crate.
|
||||
//! You should compile this crate using the functions provided in the builder.
|
||||
//!
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use linux_boot_params::BootParams;
|
||||
|
||||
mod console;
|
||||
mod loader;
|
||||
|
||||
// Unfortunately, the entrypoint is not defined here in the main.rs file.
|
||||
// See the exported functions in the x86 module for details.
|
||||
mod x86;
|
||||
|
||||
fn get_payload(boot_params: &BootParams) -> &'static [u8] {
|
||||
let hdr = &boot_params.hdr;
|
||||
// The payload_offset field is not recorded in the relocation table, so we need to
|
||||
// calculate the loaded offset manually.
|
||||
let loaded_offset = x86::relocation::get_image_loaded_offset();
|
||||
let payload_offset = (loaded_offset + hdr.payload_offset as isize) as usize;
|
||||
let payload_length = hdr.payload_length as usize;
|
||||
// Safety: the payload_offset and payload_length is valid if we assume that the
|
||||
// boot_params struct is correct.
|
||||
unsafe { core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize) }
|
||||
}
|
131
framework/libs/boot-wrapper/wrapper/src/x86/amd64_efi/efi.rs
Normal file
131
framework/libs/boot-wrapper/wrapper/src/x86/amd64_efi/efi.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use uefi::{
|
||||
data_types::Handle,
|
||||
proto::loaded_image::LoadedImage,
|
||||
table::{boot::MemoryMap, Boot, Runtime, SystemTable},
|
||||
};
|
||||
|
||||
use linux_boot_params::BootParams;
|
||||
|
||||
#[export_name = "efi_stub_entry"]
|
||||
extern "sysv64" fn efi_stub_entry(handle: Handle, mut system_table: SystemTable<Boot>) -> ! {
|
||||
unsafe {
|
||||
system_table.boot_services().set_image_handle(handle);
|
||||
}
|
||||
uefi_services::init(&mut system_table).unwrap();
|
||||
|
||||
// Suppress TODO warning.
|
||||
#[allow(unreachable_code)]
|
||||
efi_phase_boot(
|
||||
handle,
|
||||
system_table,
|
||||
todo!("Use EFI boot services to fill boot params"),
|
||||
);
|
||||
}
|
||||
|
||||
#[export_name = "efi_handover_entry"]
|
||||
extern "sysv64" fn efi_handover_entry(
|
||||
handle: Handle,
|
||||
mut system_table: SystemTable<Boot>,
|
||||
boot_params: *mut BootParams,
|
||||
) -> ! {
|
||||
unsafe {
|
||||
system_table.boot_services().set_image_handle(handle);
|
||||
}
|
||||
uefi_services::init(&mut system_table).unwrap();
|
||||
|
||||
efi_phase_boot(handle, system_table, boot_params)
|
||||
}
|
||||
|
||||
fn efi_phase_boot(
|
||||
handle: Handle,
|
||||
system_table: SystemTable<Boot>,
|
||||
boot_params: *mut BootParams,
|
||||
) -> ! {
|
||||
// Safety: this init function is only called once.
|
||||
unsafe { crate::console::init() };
|
||||
|
||||
// Safety: this is the right time to apply relocations.
|
||||
unsafe { crate::x86::relocation::apply_rela_dyn_relocations() };
|
||||
|
||||
uefi_services::println!("[EFI stub] Relocations applied.");
|
||||
|
||||
uefi_services::println!("[EFI stub] Loading payload.");
|
||||
let payload = unsafe { crate::get_payload(&*boot_params) };
|
||||
crate::loader::load_elf(payload);
|
||||
|
||||
uefi_services::println!("[EFI stub] Exiting EFI boot services.");
|
||||
let memory_type = {
|
||||
let boot_services = system_table.boot_services();
|
||||
let Ok(loaded_image) = boot_services.open_protocol_exclusive::<LoadedImage>(handle) else {
|
||||
panic!("Failed to open LoadedImage protocol");
|
||||
};
|
||||
loaded_image.data_type().clone()
|
||||
};
|
||||
let (system_table, memory_map) = system_table.exit_boot_services(memory_type);
|
||||
|
||||
efi_phase_runtime(system_table, memory_map, boot_params);
|
||||
}
|
||||
|
||||
fn efi_phase_runtime(
|
||||
_system_table: SystemTable<Runtime>,
|
||||
memory_map: MemoryMap<'static>,
|
||||
boot_params_ptr: *mut BootParams,
|
||||
) -> ! {
|
||||
unsafe {
|
||||
crate::console::print_str("[EFI stub] Entered runtime services.\n");
|
||||
}
|
||||
|
||||
let boot_params = unsafe { &mut *boot_params_ptr };
|
||||
|
||||
// Write memory map to e820 table in boot_params.
|
||||
let e820_table = &mut boot_params.e820_table;
|
||||
let mut e820_entries = 0;
|
||||
for md in memory_map.entries() {
|
||||
if e820_entries >= e820_table.len() || e820_entries >= 128 {
|
||||
break;
|
||||
}
|
||||
e820_table[e820_entries] = linux_boot_params::BootE820Entry {
|
||||
addr: md.phys_start as u64,
|
||||
size: md.page_count as u64 * 4096,
|
||||
typ: match md.ty {
|
||||
uefi::table::boot::MemoryType::CONVENTIONAL => linux_boot_params::E820Type::Ram,
|
||||
uefi::table::boot::MemoryType::RESERVED => linux_boot_params::E820Type::Reserved,
|
||||
uefi::table::boot::MemoryType::ACPI_RECLAIM => linux_boot_params::E820Type::Acpi,
|
||||
uefi::table::boot::MemoryType::ACPI_NON_VOLATILE => {
|
||||
linux_boot_params::E820Type::Nvs
|
||||
}
|
||||
_ => linux_boot_params::E820Type::Reserved,
|
||||
},
|
||||
};
|
||||
e820_entries += 1;
|
||||
}
|
||||
boot_params.e820_entries = e820_entries as u8;
|
||||
|
||||
#[cfg(feature = "debug_print")]
|
||||
unsafe {
|
||||
use crate::console::{print_hex, print_str};
|
||||
print_str("[EFI stub debug] Memory map:\n");
|
||||
for md in memory_map.entries() {
|
||||
// crate::println!(" [{:#x}] {:#x} ({:#x})", md.ty.0, md.phys_start, md.page_count);
|
||||
print_str(" [");
|
||||
print_hex(md.ty.0 as u64);
|
||||
print_str("]");
|
||||
print_hex(md.phys_start);
|
||||
print_str("(size=");
|
||||
print_hex(md.page_count);
|
||||
print_str(")");
|
||||
print_str("{flags=");
|
||||
print_hex(md.att.bits());
|
||||
print_str("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
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("\n");
|
||||
}
|
||||
|
||||
unsafe { super::call_jinux_entrypoint(super::JINUX_ENTRY_POINT, boot_params_ptr as u64) }
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
// The compatibility file for the Linux x86 Boot Protocol.
|
||||
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
|
||||
// more information on the Linux x86 Boot Protocol.
|
||||
|
||||
// Some of the fields filled with a 0xab* values should be filled
|
||||
// by the torjan builder.
|
||||
// Asterinas will use only a few of these fields, and some of them
|
||||
// are filled by the loader and will be read by Asterinas.
|
||||
|
||||
.section ".header", "a"
|
||||
CODE32_START = 0x100000
|
||||
SETUP_SECTS = 7 # so that the legacy setup could occupy a page
|
||||
SETUP_SECTS_SIZE = 0x200 * (SETUP_SECTS + 1)
|
||||
.code16
|
||||
.org 0x01f1
|
||||
hdr_start:
|
||||
setup_sects: .byte SETUP_SECTS
|
||||
root_flags: .word 1
|
||||
syssize: .long 0
|
||||
ram_size: .word 0
|
||||
vid_mode: .word 0xfffd
|
||||
root_dev: .word 0
|
||||
boot_flag: .word 0xAA55
|
||||
jump: .byte 0xeb
|
||||
jump_addr: .byte hdr_end-jump_addr
|
||||
magic: .ascii "HdrS"
|
||||
.word 0x020f
|
||||
realmode_swtch: .word 0, 0
|
||||
start_sys_seg: .word 0
|
||||
.word 0
|
||||
type_of_loader: .byte 0
|
||||
loadflags: .byte (1 << 0)
|
||||
setup_move_size: .word 0
|
||||
code32_start: .long CODE32_START
|
||||
ramdisk_image: .long 0
|
||||
ramdisk_size: .long 0
|
||||
bootsect_kludge: .long 0
|
||||
heap_end_ptr: .word 65535
|
||||
ext_loader_ver: .byte 0
|
||||
ext_loader_type: .byte 0
|
||||
cmd_line_ptr: .long 0
|
||||
initrd_addr_max: .long 0x7fffffff
|
||||
kernel_alignment: .long 0x1000000
|
||||
relocatable_kernel: .byte 0
|
||||
min_alignment: .byte 0x10
|
||||
xloadflags: .word 0b01111 # all handover protocols except kexec
|
||||
cmdline_size: .long 4096-1
|
||||
hardware_subarch: .long 0
|
||||
hardware_subarch_data: .quad 0
|
||||
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder
|
||||
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder
|
||||
setup_data: .quad 0
|
||||
pref_address: .quad CODE32_START - SETUP_SECTS_SIZE
|
||||
init_size: .long 0xabababab # at 0x260/4, to be filled by the builder
|
||||
# The handover_offset should be efi_handover_setup_entry - CODE32_START - 0x200
|
||||
# But we use ABI workaround to avoid the relocation of efi_handover_setup_entry
|
||||
handover_offset: .long 0x10
|
||||
kernel_info_offset: .long 0
|
||||
hdr_end:
|
@ -0,0 +1,55 @@
|
||||
ENTRY(efi_handover_setup_entry)
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
|
||||
SETUP32_LMA = 0x100000;
|
||||
BOOTSECT_SIZE = 0x1000;
|
||||
BOOTSECT_START = SETUP32_LMA - BOOTSECT_SIZE;
|
||||
|
||||
LEGACY_STACK_SIZE = 0x1000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BOOTSECT_START;
|
||||
.header : { KEEP(*(.header)) }
|
||||
|
||||
. = SETUP32_LMA;
|
||||
.setup : { KEEP(*(.setup)) }
|
||||
|
||||
.text : { *(.text .text.*) }
|
||||
|
||||
.data : { *(.data .data.*) }
|
||||
.bss : {
|
||||
PROVIDE(__bss_start = .);
|
||||
*(.bss .bss.*) *(COMMON)
|
||||
PROVIDE(__bss_end = .);
|
||||
}
|
||||
|
||||
.got.plt : {
|
||||
*(.got.plt .got.plt.*)
|
||||
}
|
||||
.dynamic : {
|
||||
*(.dynamic .dynamic.*)
|
||||
}
|
||||
|
||||
.rodata : { *(.rodata .rodata.*) }
|
||||
|
||||
.eh_frame : {
|
||||
*(.eh_frame .eh_frame.*)
|
||||
}
|
||||
.eh_frame_hdr : {
|
||||
*(.eh_frame_hdr .eh_frame_hdr.*)
|
||||
}
|
||||
|
||||
.rela.dyn : {
|
||||
PROVIDE(__rela_dyn_start = .);
|
||||
*(.rela.dyn .rela.dyn.*)
|
||||
PROVIDE(__rela_dyn_end = .);
|
||||
}
|
||||
|
||||
.rela.plt : {
|
||||
*(.rela.plt .rela.plt.*)
|
||||
}
|
||||
|
||||
.comment : { *(.comment) }
|
||||
}
|
17
framework/libs/boot-wrapper/wrapper/src/x86/amd64_efi/mod.rs
Normal file
17
framework/libs/boot-wrapper/wrapper/src/x86/amd64_efi/mod.rs
Normal file
@ -0,0 +1,17 @@
|
||||
mod efi;
|
||||
|
||||
use core::arch::{asm, global_asm};
|
||||
|
||||
global_asm!(include_str!("header.S"));
|
||||
|
||||
global_asm!(include_str!("setup.S"));
|
||||
|
||||
pub const ASTER_ENTRY_POINT: u32 = 0x8001200;
|
||||
|
||||
unsafe fn call_aster_entrypoint(entrypoint: u64, boot_params_ptr: u64) -> ! {
|
||||
asm!("mov rsi, {}", in(reg) boot_params_ptr as u64);
|
||||
asm!("mov rax, {}", in(reg) entrypoint as u64);
|
||||
asm!("jmp rax");
|
||||
|
||||
unreachable!();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
.section ".setup", "ax"
|
||||
.code64
|
||||
// start_of_setup32 should be loaded at CODE32_START, which is our base.
|
||||
.global start_of_setup32
|
||||
start_of_setup32:
|
||||
|
||||
// `efi_handover_setup_entry64` should be at efi_handover_setup_entry32 + 0x200, but
|
||||
// we could provide the 32 bit dummy entry point as the 64 bit entry point - 0x200
|
||||
// since we do not provide 32-bit entry point in the x86_64 specific implementation.
|
||||
.org 0x210
|
||||
.global efi_handover_setup_entry
|
||||
efi_handover_setup_entry:
|
||||
// The 3 parameters of is stored in rdi, rsi and rdx (sysv64).
|
||||
// Do not use them.
|
||||
|
||||
// Setup the stack.
|
||||
lea rsp, [rip + setup_stack_top]
|
||||
lea rax, [rip + halt]
|
||||
push rax # the return address
|
||||
mov rbp, rsp
|
||||
add rbp, -4
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
.extern efi_handover_entry
|
||||
lea rax, [rip + efi_handover_entry]
|
||||
call rax
|
||||
|
||||
// Unreachable here.
|
||||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
// A small stack for the setup code.
|
||||
.section .data
|
||||
.align 0x1000 / 8
|
||||
.global setup_stack
|
||||
setup_stack:
|
||||
.skip 0x1000
|
||||
.global setup_stack_top
|
||||
setup_stack_top:
|
@ -0,0 +1,56 @@
|
||||
// The compatibility file for the Linux x86 Boot Protocol.
|
||||
// See https://www.kernel.org/doc/html/v5.6/x86/boot.html for
|
||||
// more information on the Linux x86 Boot Protocol.
|
||||
|
||||
// Some of the fields filled with a 0xab* values should be filled
|
||||
// by the torjan builder.
|
||||
// Asterinas will use only a few of these fields, and some of them
|
||||
// are filled by the loader and will be read by Asterinas.
|
||||
|
||||
.section ".header", "a"
|
||||
CODE32_START = 0x100000
|
||||
SETUP_SECTS = 7 # so that the legacy setup could occupy a page
|
||||
.code16
|
||||
.org 0x01f1
|
||||
hdr_start:
|
||||
setup_sects: .byte SETUP_SECTS
|
||||
root_flags: .word 1
|
||||
syssize: .long 0
|
||||
ram_size: .word 0
|
||||
vid_mode: .word 0xfffd
|
||||
root_dev: .word 0
|
||||
boot_flag: .word 0xAA55
|
||||
jump: .byte 0xeb
|
||||
jump_addr: .byte hdr_end-jump_addr
|
||||
magic: .ascii "HdrS"
|
||||
.word 0x020f
|
||||
realmode_swtch: .word 0, 0
|
||||
start_sys_seg: .word 0
|
||||
.word 0
|
||||
type_of_loader: .byte 0
|
||||
loadflags: .byte (1 << 0)
|
||||
setup_move_size: .word 0
|
||||
code32_start: .long CODE32_START
|
||||
ramdisk_image: .long 0
|
||||
ramdisk_size: .long 0
|
||||
bootsect_kludge: .long 0
|
||||
heap_end_ptr: .word 65535
|
||||
ext_loader_ver: .byte 0
|
||||
ext_loader_type: .byte 0
|
||||
cmd_line_ptr: .long 0
|
||||
initrd_addr_max: .long 0x7fffffff
|
||||
kernel_alignment: .long 0x1000000
|
||||
relocatable_kernel: .byte 0
|
||||
min_alignment: .byte 0x10
|
||||
xloadflags: .word 0
|
||||
cmdline_size: .long 4096-1
|
||||
hardware_subarch: .long 0
|
||||
hardware_subarch_data: .quad 0
|
||||
payload_offset: .long 0xabababab # at 0x248/4, to be filled by the builder
|
||||
payload_length: .long 0xabababab # at 0x24c/4, to be filled by the builder
|
||||
setup_data: .quad 0
|
||||
pref_address: .quad CODE32_START - 0x200 * (SETUP_SECTS + 1);
|
||||
init_size: .long 0xabababab # at 0x260/4, to be filled by the builder
|
||||
handover_offset: .long 0
|
||||
kernel_info_offset: .long 0
|
||||
hdr_end:
|
@ -0,0 +1,36 @@
|
||||
ENTRY(start_of_setup32)
|
||||
OUTPUT_ARCH(i386:x86)
|
||||
OUTPUT_FORMAT(elf32-i386)
|
||||
|
||||
SETUP32_LMA = 0x100000;
|
||||
BOOTSECT_SIZE = 0x1000;
|
||||
BOOTSECT_START = SETUP32_LMA - BOOTSECT_SIZE;
|
||||
|
||||
LEGACY_STACK_SIZE = 0x1000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BOOTSECT_START;
|
||||
.header : { KEEP(*(.header)) }
|
||||
|
||||
. = SETUP32_LMA;
|
||||
.setup : { KEEP(*(.setup)) }
|
||||
.stack : { *(.stack) }
|
||||
|
||||
.text : { *(.text .text.*) }
|
||||
.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.*)
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
use linux_boot_params::BootParams;
|
||||
|
||||
use core::arch::{asm, global_asm};
|
||||
|
||||
global_asm!(include_str!("header.S"));
|
||||
|
||||
global_asm!(include_str!("setup.S"));
|
||||
|
||||
use crate::console::{print_hex, print_str};
|
||||
|
||||
pub const JINUX_ENTRY_POINT: u32 = 0x8001000;
|
||||
|
||||
#[export_name = "_trojan_entry_32"]
|
||||
extern "cdecl" fn trojan_entry(boot_params_ptr: u32) -> ! {
|
||||
// Safety: this init function is only called once.
|
||||
unsafe { crate::console::init() };
|
||||
|
||||
// println!("[setup] bzImage loaded at {:#x}", x86::relocation::get_image_loaded_offset());
|
||||
unsafe {
|
||||
print_str("[setup] bzImage loaded at ");
|
||||
print_hex(crate::x86::relocation::get_image_loaded_offset() as u64);
|
||||
print_str("\n");
|
||||
}
|
||||
|
||||
// Safety: the boot_params_ptr is a valid pointer to be borrowed.
|
||||
let boot_params = unsafe { &*(boot_params_ptr as *const BootParams) };
|
||||
// Safety: the payload_offset and payload_length is valid.
|
||||
let payload = crate::get_payload(boot_params);
|
||||
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()) };
|
||||
}
|
||||
|
||||
pub const ASTER_ENTRY_POINT: u32 = 0x8001000;
|
||||
|
||||
unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> ! {
|
||||
asm!("mov esi, {}", in(reg) boot_params_ptr);
|
||||
asm!("mov eax, {}", in(reg) entrypoint);
|
||||
asm!("jmp eax");
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// 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_top
|
||||
mov esp, eax
|
||||
mov eax, offset halt
|
||||
push eax # the return address
|
||||
mov ebp, esp
|
||||
add ebp, -4
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
.extern _trojan_entry_32
|
||||
push esi # the boot_params pointer
|
||||
call _trojan_entry_32
|
||||
|
||||
// Unreachable here.
|
||||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
// A small stack for the 32-bit code.
|
||||
.section ".stack", "aw"
|
||||
.align 8
|
||||
.space 0x1000
|
||||
__stack_top:
|
11
framework/libs/boot-wrapper/wrapper/src/x86/mod.rs
Normal file
11
framework/libs/boot-wrapper/wrapper/src/x86/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
mod amd64_efi;
|
||||
} else if #[cfg(target_arch = "x86")] {
|
||||
mod legacy_i386;
|
||||
} else {
|
||||
compile_error!("Unsupported target_arch");
|
||||
}
|
||||
}
|
||||
|
||||
pub mod relocation;
|
106
framework/libs/boot-wrapper/wrapper/src/x86/relocation.rs
Normal file
106
framework/libs/boot-wrapper/wrapper/src/x86/relocation.rs
Normal file
@ -0,0 +1,106 @@
|
||||
// This is enforced in the linker script.
|
||||
const START_OF_SETUP32_VA: usize = 0x100000;
|
||||
|
||||
/// The wrapper is a position-independent executable. We can get the loaded base
|
||||
/// address from the symbol.
|
||||
#[inline]
|
||||
pub fn get_image_loaded_offset() -> isize {
|
||||
extern "C" {
|
||||
fn start_of_setup32();
|
||||
}
|
||||
start_of_setup32 as isize - START_OF_SETUP32_VA as isize
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
struct Elf64Rela {
|
||||
r_offset: u64,
|
||||
r_info: u64,
|
||||
r_addend: i64,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn get_rela_array() -> &'static [Elf64Rela] {
|
||||
extern "C" {
|
||||
fn __rela_dyn_start();
|
||||
fn __rela_dyn_end();
|
||||
}
|
||||
let start = __rela_dyn_start as *const Elf64Rela;
|
||||
let end = __rela_dyn_end as *const Elf64Rela;
|
||||
// FIXME: 2023/11/29
|
||||
// There should be a Rust compiler bug that makes the calculation of len incorrect.
|
||||
// The most sound implementation only works in debug mode.
|
||||
// let len = unsafe { end.offset_from(start) } as usize;
|
||||
// The inline asm solution is a workaround.
|
||||
let len = unsafe {
|
||||
let len: usize;
|
||||
core::arch::asm!("
|
||||
mov {len}, {end}
|
||||
sub {len}, {start}
|
||||
",
|
||||
len = out(reg) len,
|
||||
end = in(reg) end,
|
||||
start = in(reg) start,
|
||||
);
|
||||
len / core::mem::size_of::<Elf64Rela>() as usize
|
||||
};
|
||||
#[cfg(feature = "debug_print")]
|
||||
unsafe {
|
||||
use crate::console::{print_hex, print_str};
|
||||
print_str("[EFI stub debug] .rela.dyn section size = ");
|
||||
print_hex(len as u64);
|
||||
print_str("; __rela_dyn_start = ");
|
||||
print_hex(start as u64);
|
||||
print_str(", __rela_dyn_end = ");
|
||||
print_hex(end as u64);
|
||||
print_str("\n");
|
||||
}
|
||||
// Safety: the linker will ensure that the symbols are valid.
|
||||
unsafe { core::slice::from_raw_parts(start as *const Elf64Rela, len) }
|
||||
}
|
||||
|
||||
const R_X86_64_RELATIVE: u32 = 8;
|
||||
|
||||
/// Apply the relocations in the `.rela.dyn` section.
|
||||
///
|
||||
/// The function will enable dyn Trait objects to work since they rely on vtable pointers. Vtable
|
||||
/// won't work without relocations.
|
||||
///
|
||||
/// We currently support R_X86_64_RELATIVE relocations only. And this type of relocation seems to
|
||||
/// be the only existing type if we compile Rust code to PIC ELF binaries.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function will modify the memory pointed by the relocations. And the Rust memory safety
|
||||
/// mechanisms are not aware of these kind of modification. Failure to do relocations will cause
|
||||
/// dyn Trait objects to break.
|
||||
#[allow(unused)]
|
||||
pub unsafe fn apply_rela_dyn_relocations() {
|
||||
let image_loaded_offset = get_image_loaded_offset();
|
||||
let relas = get_rela_array();
|
||||
for rela in relas {
|
||||
let r_type = (rela.r_info & 0xffffffff) as u32;
|
||||
let _r_sym = (rela.r_info >> 32) as usize;
|
||||
let r_addend = rela.r_addend;
|
||||
let r_offset = rela.r_offset as usize;
|
||||
let target = (image_loaded_offset + r_offset as isize) as usize;
|
||||
#[cfg(feature = "debug_print")]
|
||||
unsafe {
|
||||
use crate::console::{print_hex, print_str};
|
||||
print_str("[EFI stub debug] Applying relocation at offset ");
|
||||
print_hex(r_offset as u64);
|
||||
print_str(", type = ");
|
||||
print_hex(r_type as u64);
|
||||
print_str(", addend = ");
|
||||
print_hex(r_addend as u64);
|
||||
print_str("\n");
|
||||
}
|
||||
match r_type {
|
||||
R_X86_64_RELATIVE => {
|
||||
let value = (image_loaded_offset as i64 + r_addend) as usize;
|
||||
*(target as *mut usize) = value;
|
||||
}
|
||||
_ => {
|
||||
panic!("Unknown relocation type: {}", r_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
framework/libs/boot-wrapper/wrapper/x86_64-i386_pm-none.json
Normal file
20
framework/libs/boot-wrapper/wrapper/x86_64-i386_pm-none.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"llvm-target": "i386-unknown-none",
|
||||
"data-layout": "e-m:e-i32:32-f80:128-n8:16:32-S128-p:32:32",
|
||||
"cpu": "i386",
|
||||
"arch": "x86",
|
||||
"dynamic-linking": false,
|
||||
"executables": true,
|
||||
"linker-flavor": "ld.lld",
|
||||
"linker": "rust-lld",
|
||||
"max-atomic-width": 64,
|
||||
"position-independent-executables": false,
|
||||
"disable-redzone": true,
|
||||
"target-c-int-width": "32",
|
||||
"target-pointer-width": "32",
|
||||
"target-endian": "little",
|
||||
"panic-strategy": "abort",
|
||||
"os": "none",
|
||||
"relocation-model": "static",
|
||||
"features": "+soft-float,-sse,-mmx"
|
||||
}
|
Reference in New Issue
Block a user