Add EFI stub

This commit is contained in:
Zhang Junyang 2023-11-19 16:26:55 +08:00 committed by Tate, Hongliang Tian
parent acf4a057d9
commit 32e62080ce
29 changed files with 883 additions and 460 deletions

73
Cargo.lock generated
View File

@ -956,6 +956,10 @@ dependencies = [
"rle-decode-fast",
]
[[package]]
name = "linux_boot_params"
version = "0.1.0"
[[package]]
name = "lock_api"
version = "0.4.10"
@ -968,9 +972,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.19"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lru"
@ -1028,7 +1032,7 @@ dependencies = [
"derive_more",
"log",
"ptr_meta",
"uefi-raw",
"uefi-raw 0.3.0",
]
[[package]]
@ -1496,6 +1500,41 @@ dependencies = [
"x86",
]
[[package]]
name = "ucs2"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8"
dependencies = [
"bit_field",
]
[[package]]
name = "uefi"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ead9f748a4646479b850add36b527113a80e80a7e0f44d7b0334291850dcc5"
dependencies = [
"bitflags 2.3.3",
"log",
"ptr_meta",
"ucs2",
"uefi-macros",
"uefi-raw 0.5.0",
"uguid",
]
[[package]]
name = "uefi-macros"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26a7b1c2c808c3db854a54d5215e3f7e7aaf5dcfbce095598cba6af29895695d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]]
name = "uefi-raw"
version = "0.3.0"
@ -1508,10 +1547,32 @@ dependencies = [
]
[[package]]
name = "uguid"
version = "2.0.1"
name = "uefi-raw"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16dfbd255defbd727b3a30e8950695d2e6d045841ee250ff0f1f7ced17917f8d"
checksum = "864ac69eadd877bfb34e7814be1928122ed0057d9f975169a56ee496aa7bdfd7"
dependencies = [
"bitflags 2.3.3",
"ptr_meta",
"uguid",
]
[[package]]
name = "uefi-services"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a79fcb420624743c895bad0f9480fbc2f64e7c8d8611fb1ada6bdd799942feb4"
dependencies = [
"cfg-if",
"log",
"uefi",
]
[[package]]
name = "uguid"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef516f0806c5f61da6aa95125d0eb2d91cc95b2df426c06bde8be657282aee5"
[[package]]
name = "unicode-ident"

View File

@ -86,6 +86,7 @@ USERMODE_TESTABLE := \
runner \
framework/libs/align_ext \
framework/libs/boot-trojan/builder \
framework/libs/boot-trojan/linux-boot-params \
framework/libs/ktest \
framework/libs/ktest-proc-macro \
services/libs/cpio-decoder \

View File

@ -10,6 +10,7 @@ align_ext = { path = "../libs/align_ext" }
bit_field = "0.10.1"
bitflags = "1.3"
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
linux_boot_params = { path = "../libs/boot-trojan/linux-boot-params" }
buddy_system_allocator = "0.9.0"
cfg-if = "1.0"
gimli = { version = "0.28", default-features = false, features = ["read-core"] }

View File

@ -1,291 +0,0 @@
//! The Linux Boot Protocol boot_params module.
//!
//! 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.
//!
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct ScreenInfo {
pub(super) orig_x: u8, /* 0x00 */
pub(super) orig_y: u8, /* 0x01 */
pub(super) ext_mem_k: u16, /* 0x02 */
pub(super) orig_video_page: u16, /* 0x04 */
pub(super) orig_video_mode: u8, /* 0x06 */
pub(super) orig_video_cols: u8, /* 0x07 */
pub(super) flags: u8, /* 0x08 */
pub(super) unused2: u8, /* 0x09 */
pub(super) orig_video_ega_bx: u16, /* 0x0a */
pub(super) unused3: u16, /* 0x0c */
pub(super) orig_video_lines: u8, /* 0x0e */
pub(super) orig_video_is_vga: u8, /* 0x0f */
pub(super) orig_video_points: u16, /* 0x10 */
/* VESA graphic mode -- linear frame buffer */
pub(super) lfb_width: u16, /* 0x12 */
pub(super) lfb_height: u16, /* 0x14 */
pub(super) lfb_depth: u16, /* 0x16 */
pub(super) lfb_base: u32, /* 0x18 */
pub(super) lfb_size: u32, /* 0x1c */
pub(super) cl_magic: u16,
pub(super) cl_offset: u16, /* 0x20 */
pub(super) lfb_linelength: u16, /* 0x24 */
pub(super) red_size: u8, /* 0x26 */
pub(super) red_pos: u8, /* 0x27 */
pub(super) green_size: u8, /* 0x28 */
pub(super) green_pos: u8, /* 0x29 */
pub(super) blue_size: u8, /* 0x2a */
pub(super) blue_pos: u8, /* 0x2b */
pub(super) rsvd_size: u8, /* 0x2c */
pub(super) rsvd_pos: u8, /* 0x2d */
pub(super) vesapm_seg: u16, /* 0x2e */
pub(super) vesapm_off: u16, /* 0x30 */
pub(super) pages: u16, /* 0x32 */
pub(super) vesa_attributes: u16, /* 0x34 */
pub(super) capabilities: u32, /* 0x36 */
pub(super) ext_lfb_base: u32, /* 0x3a */
pub(super) _reserved: [u8; 2], /* 0x3e */
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct ApmBiosInfo {
pub(super) version: u16,
pub(super) cseg: u16,
pub(super) offset: u32,
pub(super) cseg_16: u16,
pub(super) dseg: u16,
pub(super) flags: u16,
pub(super) cseg_len: u16,
pub(super) cseg_16_len: u16,
pub(super) dseg_len: u16,
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct IstInfo {
pub(super) signature: u32,
pub(super) command: u32,
pub(super) event: u32,
pub(super) perf_level: u32,
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct SysDescTable {
pub(super) length: u16,
pub(super) table: [u8; 14],
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct OlpcOfwHeader {
pub(super) ofw_magic: u32, /* OFW signature */
pub(super) ofw_version: u32,
pub(super) cif_handler: u32, /* callback into OFW */
pub(super) irq_desc_table: u32,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct EdidInfo {
pub(super) dummy: [u8; 128],
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct EfiInfo {
pub(super) efi_loader_signature: u32,
pub(super) efi_systab: u32,
pub(super) efi_memdesc_size: u32,
pub(super) efi_memdesc_version: u32,
pub(super) efi_memmap: u32,
pub(super) efi_memmap_size: u32,
pub(super) efi_systab_hi: u32,
pub(super) efi_memmap_hi: u32,
}
/// Magic stored in SetupHeader.header.
pub(super) 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(super) struct SetupHeader {
pub(super) setup_sects: u8,
pub(super) root_flags: u16,
pub(super) syssize: u32,
pub(super) ram_size: u16,
pub(super) vid_mode: u16,
pub(super) root_dev: u16,
pub(super) boot_flag: u16,
pub(super) jump: u16,
pub(super) header: u32,
pub(super) version: u16,
pub(super) realmode_swtch: u32,
pub(super) start_sys_seg: u16,
pub(super) kernel_version: u16,
pub(super) type_of_loader: u8,
pub(super) loadflags: u8,
pub(super) setup_move_size: u16,
pub(super) code32_start: u32,
pub(super) ramdisk_image: u32,
pub(super) ramdisk_size: u32,
pub(super) bootsect_kludge: u32,
pub(super) heap_end_ptr: u16,
pub(super) ext_loader_ver: u8,
pub(super) ext_loader_type: u8,
pub(super) cmd_line_ptr: u32,
pub(super) initrd_addr_max: u32,
pub(super) kernel_alignment: u32,
pub(super) relocatable_kernel: u8,
pub(super) min_alignment: u8,
pub(super) xloadflags: u16,
pub(super) cmdline_size: u32,
pub(super) hardware_subarch: u32,
pub(super) hardware_subarch_data: u64,
pub(super) payload_offset: u32,
pub(super) payload_length: u32,
pub(super) setup_data: u64,
pub(super) pref_address: u64,
pub(super) init_size: u32,
pub(super) handover_offset: u32,
pub(super) 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(super) 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(super) struct BootE820Entry {
pub(super) addr: u64,
pub(super) size: u64,
pub(super) typ: E820Type,
}
const E820_MAX_ENTRIES_ZEROPAGE: usize = 128;
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) 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(super) _dummy: [u8; (0xeec - 0xd00) / 6 - 8],
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub(super) struct EddInfo {
pub(super) device: u8,
pub(super) version: u8,
pub(super) interface_support: u16,
pub(super) legacy_max_cylinder: u16,
pub(super) legacy_max_head: u8,
pub(super) legacy_sectors_per_track: u8,
pub(super) 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(super) struct BootParams {
pub(super) screen_info: ScreenInfo, /* 0x000 */
pub(super) apm_bios_info: ApmBiosInfo, /* 0x040 */
pub(super) _pad2: [u8; 4], /* 0x054 */
pub(super) tboot_addr: u64, /* 0x058 */
pub(super) ist_info: IstInfo, /* 0x060 */
pub(super) acpi_rsdp_addr: u64, /* 0x070 */
pub(super) _pad3: [u8; 8], /* 0x078 */
pub(super) hd0_info: [u8; 16], /* obsolete! 0x080 */
pub(super) hd1_info: [u8; 16], /* obsolete! 0x090 */
pub(super) sys_desc_table: SysDescTable, /* obsolete! 0x0a0 */
pub(super) olpc_ofw_header: OlpcOfwHeader, /* 0x0b0 */
pub(super) ext_ramdisk_image: u32, /* 0x0c0 */
pub(super) ext_ramdisk_size: u32, /* 0x0c4 */
pub(super) ext_cmd_line_ptr: u32, /* 0x0c8 */
pub(super) _pad4: [u8; 112], /* 0x0cc */
pub(super) cc_blob_address: u32, /* 0x13c */
pub(super) edid_info: EdidInfo, /* 0x140 */
pub(super) efi_info: EfiInfo, /* 0x1c0 */
pub(super) alt_mem_k: u32, /* 0x1e0 */
pub(super) scratch: u32, /* Scratch field! 0x1e4 */
pub(super) e820_entries: u8, /* 0x1e8 */
pub(super) eddbuf_entries: u8, /* 0x1e9 */
pub(super) edd_mbr_sig_buf_entries: u8, /* 0x1ea */
pub(super) kbd_status: u8, /* 0x1eb */
pub(super) secure_boot: u8, /* 0x1ec */
pub(super) _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(super) sentinel: u8, /* 0x1ef */
pub(super) _pad6: [u8; 1], /* 0x1f0 */
pub(super) hdr: SetupHeader, /* setup header 0x1f1 */
pub(super) _pad7: [u8; 0x290 - 0x1f1 - core::mem::size_of::<SetupHeader>()],
pub(super) edd_mbr_sig_buffer: [u32; EDD_MBR_SIG_MAX], /* 0x290 */
pub(super) e820_table: [BootE820Entry; E820_MAX_ENTRIES_ZEROPAGE], /* 0x2d0 */
pub(super) _pad8: [u8; 48], /* 0xcd0 */
pub(super) eddbuf: [EddInfo; EDDMAXNR], /* 0xd00 */
pub(super) _pad9: [u8; 276], /* 0xeec */
}

View File

@ -1,8 +1,8 @@
//! The Linux 64-bit Boot Protocol supporting module.
//!
mod boot_params;
use boot_params::E820Type;
extern crate linux_boot_params;
use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC};
use crate::boot::{
kcmdline::KCmdlineArg,
@ -16,7 +16,7 @@ use core::ffi::CStr;
use spin::Once;
static BOOT_PARAMS: Once<boot_params::BootParams> = Once::new();
static BOOT_PARAMS: Once<BootParams> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
let hdr = &BOOT_PARAMS.get().unwrap().hdr;
@ -140,9 +140,9 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
/// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader.
#[no_mangle]
unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const boot_params::BootParams) -> ! {
unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const BootParams) -> ! {
let params = *params_ptr;
assert_eq!({ params.hdr.header }, boot_params::LINUX_BOOT_HEADER_MAGIC);
assert_eq!({ params.hdr.header }, LINUX_BOOT_HEADER_MAGIC);
BOOT_PARAMS.call_once(|| params);
crate::boot::register_boot_init_callbacks(
init_bootloader_name,

View File

@ -2,7 +2,6 @@ mod mapping;
mod pe_header;
use std::{
ffi::OsStr,
fs::File,
io::{Read, Write},
path::{Path, PathBuf},
@ -85,12 +84,15 @@ fn fill_legacy_header_fields(
pub fn make_bzimage(path: &Path, kernel_path: &Path, trojan_src: &Path, trojan_out: &Path) {
#[cfg(feature = "trojan64")]
let trojan = build_trojan_with_arch(trojan_src, trojan_out, "x86_64-unknown-none".as_ref());
let trojan = build_trojan_with_arch(trojan_src, trojan_out, &TrojanBuildArch::X86_64);
#[cfg(not(feature = "trojan64"))]
let trojan = {
let arch = trojan_src.join("x86_64-i386_pm-none.json");
build_trojan_with_arch(trojan_src, trojan_out, arch.as_os_str())
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();
@ -140,14 +142,32 @@ pub fn make_bzimage(path: &Path, kernel_path: &Path, trojan_src: &Path, trojan_o
}
}
fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &OsStr) -> PathBuf {
// 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),
}
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();
let cargo = std::env::var("CARGO").unwrap();
let mut cmd = std::process::Command::new(cargo);
cmd.current_dir(source_dir);
cmd.arg("build");
// Relocations are fewer in release mode, saving header real-estate.
cmd.arg("--release");
cmd.arg("--package").arg("aster-boot-trojan");
cmd.arg("--manifest-path")
.arg(source_dir.join("Cargo.toml").as_os_str());
cmd.arg("--target").arg(arch);
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
@ -155,6 +175,7 @@ fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &OsStr) -> Pa
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() {
@ -164,12 +185,15 @@ fn build_trojan_with_arch(source_dir: &Path, out_dir: &Path, arch: &OsStr) -> Pa
);
}
// If the arch is a builtin target rather than json, the path operation works as well.
let arch_name = Path::new(arch).file_stem().unwrap().to_str().unwrap();
// Return the path to the trojan 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("debug")
.join("release")
.join("aster-boot-trojan");
trojan_artifact.to_owned()

View File

@ -1,7 +1,11 @@
//! 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};
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.
@ -40,6 +44,16 @@ impl Sub for TrojanVA {
}
}
impl Add<usize> for TrojanVA {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self {
addr: self.addr + rhs,
}
}
}
impl From<usize> for TrojanFileOffset {
fn from(offset: usize) -> Self {
Self { offset }
@ -60,6 +74,16 @@ impl Sub for TrojanFileOffset {
}
}
impl Add<usize> for TrojanFileOffset {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self {
offset: self.offset + rhs,
}
}
}
impl From<TrojanVA> for TrojanFileOffset {
fn from(va: TrojanVA) -> Self {
Self {

View File

@ -104,6 +104,42 @@ struct Pe32PlusOptHdr {
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! {
@ -163,51 +199,50 @@ struct PeSectionHdr {
}
struct TrojanSectionAddrInfo {
pub setup: Range<TrojanVA>,
pub text: Range<TrojanVA>,
pub data: Range<TrojanVA>,
pub bss: Range<TrojanVA>,
/// All the readonly but loaded sections.
pub rodata: 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);
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 = TrojanVA::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);
}
}
}
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()),
data: TrojanVA::from(data_start.unwrap())..TrojanVA::from(data_end.unwrap()),
bss: TrojanVA::from(bss_start.unwrap())..TrojanVA::from(bss_end.unwrap()),
rodata: TrojanVA::from(rodata_start.unwrap())..TrojanVA::from(rodata_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
}
@ -215,6 +250,26 @@ impl TrojanSectionAddrInfo {
fn text_file_size(&self) -> usize {
TrojanFileOffset::from(self.text.end) - TrojanFileOffset::from(self.text.start)
}
fn data_virt_size(&self) -> usize {
self.data.end - self.data.start
}
fn data_file_size(&self) -> usize {
TrojanFileOffset::from(self.data.end) - TrojanFileOffset::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 {
TrojanFileOffset::from(self.rodata.end) - TrojanFileOffset::from(self.rodata.start)
}
}
pub struct TrojanPeCoffHeaderBuf {
@ -226,11 +281,16 @@ pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> Trojan
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 = TrojanFileOffset::from(0x500);
// PE header
let pe_hdr = PeHdr {
let mut 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
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
@ -271,33 +331,27 @@ pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> Trojan
heap_size_req: 0,
heap_size: 0,
loader_flags: 0,
data_dirs: 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 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 {
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(TrojanVA::from(reloc_offset)) as u32,
@ -312,8 +366,9 @@ pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> Trojan
| PeSectionHdrFlags::MEM_DISCARDABLE)
.bits
| PeSectionHdrFlagsAlign::_1Bytes as u32,
};
let pe_text_hdr = PeSectionHdr {
});
// .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,
@ -328,7 +383,55 @@ pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> Trojan
| 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(TrojanFileOffset::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(TrojanFileOffset::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());
@ -338,13 +441,15 @@ pub(crate) fn make_pe_coff_header(setup_elf: &[u8], image_size: usize) -> Trojan
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
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));
for sec_hdr in sec_hdrs {
bin.extend_from_slice(bytemuck::bytes_of(&sec_hdr));
}
TrojanPeCoffHeaderBuf {
header_at_zero: bin,

View 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]

View 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 */
}

View 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",
]

View File

@ -7,5 +7,11 @@ edition = "2021"
[dependencies]
cfg-if = "1.0.0"
linux_boot_params = { path = "../linux-boot-params" }
uart_16550 = "0.3.0"
xmas-elf = "0.8.0"
xmas-elf = "0.8.0"
[target.x86_64-unknown-none.dependencies]
log = "0.4.20"
uefi = "0.26.0"
uefi-services = "0.23.0"

View File

@ -4,15 +4,12 @@ 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/arch/x86_64.linker.ld")
source_dir.join("src/arch/x86_64/linker.ld")
} else if target_arch == "x86_64-i386_pm-none" {
source_dir.join("src/arch/i386.linker.ld")
source_dir.join("src/arch/i386/linker.ld")
} else {
panic!("Unsupported target_arch: {}", target_arch);
};
println!("cargo:rerun-if-changed={}", linker_script.display());
println!(
"cargo:rustc-link-arg-bins=--script={}",
linker_script.display()
);
println!("cargo:rustc-link-arg=-T{}", linker_script.display());
}

View File

@ -42,7 +42,7 @@ 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
xloadflags: .word 0
cmdline_size: .long 4096-1
hardware_subarch: .long 0
hardware_subarch_data: .quad 0
@ -51,6 +51,6 @@ payload_length: .long 0xabababab # at 0x24c/4, to be filled by the build
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 CODE32_START
handover_offset: .long 0
kernel_info_offset: .long 0
hdr_end:

View File

@ -1,3 +1,5 @@
use crate::println;
use core::arch::{asm, global_asm};
global_asm!(include_str!("header.S"));
@ -6,9 +8,11 @@ global_asm!(include_str!("setup.S"));
#[no_mangle]
extern "cdecl" fn _trojan_entry_32(boot_params_ptr: u32) -> ! {
crate::trojan_entry(boot_params_ptr);
crate::trojan_entry(0x100000, boot_params_ptr.try_into().unwrap());
}
pub const ASTER_ENTRY_POINT: u32 = 0x8001000;
pub 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);
@ -16,3 +20,9 @@ pub unsafe fn call_aster_entrypoint(entrypoint: u32, boot_params_ptr: u32) -> !
unreachable!();
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("panic: {:?}", info);
loop {}
}

View File

@ -1,16 +0,0 @@
.section ".setup", "ax"
.code64
.org 0x200
// start_of_setup64 should be at start_of_setup32 + 0x200
.global start_of_setup64
start_of_setup64:
.extern _trojan_entry_64
lea rax, [rip + _trojan_entry_64]
push rsi
call rax
// Unreachable here.
halt:
hlt
jmp halt

View File

@ -0,0 +1,66 @@
use core::fmt::Write;
use uefi::{
data_types::Handle,
proto::loaded_image::LoadedImage,
table::{Boot, SystemTable},
};
use linux_boot_params::BootParams;
#[no_mangle]
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_entry(
handle,
system_table,
todo!("Use EFI services to fill boot params"),
);
}
#[no_mangle]
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_entry(handle, system_table, boot_params)
}
fn efi_entry(
handle: Handle,
mut system_table: SystemTable<Boot>,
boot_params: *mut BootParams,
) -> ! {
system_table
.stdout()
.write_str("[EFI stub] Exiting EFI boot services.\n")
.unwrap();
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.exit_boot_services(memory_type);
let loaded_base = {
extern "C" {
fn start_of_setup32();
}
start_of_setup32 as usize
};
crate::trojan_entry(loaded_base, boot_params as usize);
}

View File

@ -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:

View File

@ -1,4 +1,4 @@
ENTRY(start_of_setup64)
ENTRY(efi_handover_setup_entry)
OUTPUT_ARCH(i386:x86-64)
OUTPUT_FORMAT(elf64-x86-64)
@ -17,7 +17,6 @@ SECTIONS
.setup : { KEEP(*(.setup)) }
.text : { *(.text .text.*) }
.rodata : { *(.rodata .rodata.*) }
.data : { *(.data .data.*) }
.bss : {
@ -26,6 +25,15 @@ SECTIONS
PROVIDE(__bss_end = .);
}
.got.plt : {
*(.got.plt .got.plt.*)
}
.dynamic : {
*(.dynamic .dynamic.*)
}
.rodata : { *(.rodata .rodata.*) }
.eh_frame : {
*(.eh_frame .eh_frame.*)
}
@ -36,4 +44,10 @@ SECTIONS
.rela.dyn : {
*(.rela.dyn .rela.dyn.*)
}
.rela.plt : {
*(.rela.plt .rela.plt.*)
}
.comment : { *(.comment) }
}

View File

@ -1,13 +1,12 @@
mod efi;
use core::arch::{asm, global_asm};
global_asm!(include_str!("header.S"));
global_asm!(include_str!("setup64.S"));
global_asm!(include_str!("setup.S"));
#[no_mangle]
extern "cdecl" fn _trojan_entry_64(boot_params_ptr: u64) -> ! {
crate::trojan_entry(boot_params_ptr as u32);
}
pub const ASTER_ENTRY_POINT: u32 = 0x8001200;
pub unsafe fn call_aster_entrypoint(entrypoint: u64, boot_params_ptr: u64) -> ! {
asm!("mov rsi, {}", in(reg) boot_params_ptr as u64);

View File

@ -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:

View File

@ -1,12 +0,0 @@
const FIELD_PAYLOAD_OFFSET: u32 = 0x248;
const FIELD_PAYLOAD_LENGTH: u32 = 0x24c;
/// Safty: user must ensure that the boot_params_ptr is valid
pub unsafe fn get_payload_offset(boot_params_ptr: u32) -> u32 {
*((boot_params_ptr + FIELD_PAYLOAD_OFFSET) as *const u32)
}
/// Safty: user must ensure that the boot_params_ptr is valid
pub unsafe fn get_payload_length(boot_params_ptr: u32) -> u32 {
*((boot_params_ptr + FIELD_PAYLOAD_LENGTH) as *const u32)
}

View File

@ -1,4 +1,4 @@
use core::fmt::{self, Write};
use core::fmt::Write;
use uart_16550::SerialPort;
@ -17,37 +17,45 @@ pub unsafe fn init() {
impl Stdout {
/// safety: this function must only be called once
pub unsafe fn init() -> Self {
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 {
impl Stdout {
fn write_str(&mut self, s: &str) {
self.serial_port.write_str(s).unwrap();
Ok(())
}
fn write_char(&mut self, c: char) {
self.serial_port.send(c as u8);
}
}
pub fn print(args: fmt::Arguments) {
// safety: init() must be called before print() and there is no race condition
unsafe {
STDOUT.write_fmt(args).unwrap();
/// Safety: init() must be called before print() and there should be no race condition
pub unsafe fn print(s: &str) {
STDOUT.write_str(s);
}
/// Safety: init() must be called before print_char() and there should be no race condition
pub unsafe fn print_char(c: char) {
STDOUT.write_char(c);
}
// Safety: init() must be called before print_hex() and there should be no race condition
pub unsafe fn print_hex(n: usize) {
print("0x");
let mut n = n;
for _ in 0..16 {
let digit = (n & 0xf) as u8;
n >>= 4;
let c = if digit < 10 {
(b'0' + digit) as char
} else {
(b'a' + digit - 10) as char
};
print_char(c);
}
}
#[macro_export]
macro_rules! print {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!($fmt $(, $($arg)+)?))
}
}
#[macro_export]
macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
}
}

View File

@ -24,6 +24,6 @@ pub fn load_elf(file: &[u8]) -> u32 {
}
}
// Return the Linux 32-bit Boot Protocol entry point defined by Asterinas.
0x8001000
// Return the Linux Boot Protocol entry point defined by Asterinas.
crate::arch::ASTER_ENTRY_POINT
}

View File

@ -1,32 +1,48 @@
#![no_std]
#![no_main]
use linux_boot_params::BootParams;
mod arch;
mod boot_params;
mod console;
mod loader;
fn trojan_entry(boot_params_ptr: u32) -> ! {
use console::{print, print_hex};
/// The entrypoint of the trojan. The architecture-specific entrypoint will call this function.
///
/// The loaded address of the CODE32_START should be passed in as `loaded_base`, since the trojan
/// may be loaded at any address, and offsets in the header are not position-independent.
fn trojan_entry(loaded_base: usize, boot_params_ptr: usize) -> ! {
// Safety: this init function is only called once.
unsafe { console::init() };
println!("[setup] boot_params_ptr: {:#x}", boot_params_ptr);
unsafe {
print("[setup] bzImage loaded at ");
print_hex(loaded_base);
print("\n");
}
let payload_offset = unsafe { boot_params::get_payload_offset(boot_params_ptr) };
let payload_length = unsafe { boot_params::get_payload_length(boot_params_ptr) };
// Safety: the boot_params_ptr is a valid pointer to be borrowed.
let boot_params = unsafe { &*(boot_params_ptr as *const BootParams) };
let hdr = &boot_params.hdr;
let payload_offset = loaded_base + hdr.payload_offset as usize;
let payload_length = hdr.payload_length as usize;
let payload = unsafe {
core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize)
};
println!("[setup] loading ELF payload...");
unsafe {
print("[setup] loading ELF payload at ");
print_hex(payload_offset);
print("...\n");
}
let entrypoint = loader::load_elf(payload);
println!("[setup] entrypoint: {:#x}", entrypoint);
unsafe {
print("[setup] jumping to payload entrypoint at ");
print_hex(entrypoint as usize);
print("...\n");
}
// Safety: the entrypoint and the ptr is valid.
unsafe { arch::call_aster_entrypoint(entrypoint.into(), boot_params_ptr.into()) };
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("panic: {:?}", info);
loop {}
unsafe { arch::call_aster_entrypoint(entrypoint.into(), boot_params_ptr.try_into().unwrap()) };
}

View File

@ -2,7 +2,7 @@
# AUTOMATICALLY GENERATED FILE, DO NOT EDIT IF YOU KNOW WHAT YOU ARE DOING
# set debug=linux,efi
# set debug=linux,efi,linuxefi
set timeout_style=#GRUB_TIMEOUT_STYLE#
set timeout=#GRUB_TIMEOUT#

View File

@ -10,7 +10,7 @@ use std::{fs::OpenOptions, io::Write, path::PathBuf, process::Command};
/// When debugging grub, the OVMF firmware will load the grub kernel at an
/// address unknown at the moment. You should use the debug message from our
/// custom built OVMF firmware and read the entrypoint address
/// (often `0x0007E685000`). Then use the following GDB command to load symbols:
/// (often `0x0007E684000`). Then use the following GDB command to load symbols:
/// `dynamic_load_symbols ${ENTRY_ADDRESS}`.
/// During each run, the address is unlikely to change. But the address will
/// depend on the versions of grub or OVMF.