mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
Implement linux x86 32bit legacy boot protocol
This commit is contained in:
parent
9d0e0bbc70
commit
a532340c65
12
.github/workflows/syscall_test.yml
vendored
12
.github/workflows/syscall_test.yml
vendored
@ -17,12 +17,16 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Syscall Test (Multiboot)
|
||||||
|
id: syscall_test_mb
|
||||||
|
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=multiboot
|
||||||
|
|
||||||
- name: Syscall Test (Multiboot2)
|
- name: Syscall Test (Multiboot2)
|
||||||
id: syscall_test_mb2
|
id: syscall_test_mb2
|
||||||
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=multiboot2
|
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=multiboot2
|
||||||
|
|
||||||
# TODO: include the integration tests for Multiboot/MicroVM/Linux boot methods, which are not ready yet.
|
- name: Syscall Test (Linux Boot Protocol)
|
||||||
|
id: syscall_test_lbp
|
||||||
|
run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=linux
|
||||||
|
|
||||||
# - name: Syscall Test (Linux Boot Protocol)
|
# TODO: include the integration tests for MicroVM, which is not ready yet.
|
||||||
# id: syscall_test_lbp
|
|
||||||
# run: RUSTFLAGS="-C opt-level=1" make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0 SKIP_GRUB_MENU=1 BOOT_METHOD=qemu-grub BOOT_PROTOCOL=linux
|
|
||||||
|
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -647,7 +647,6 @@ dependencies = [
|
|||||||
"volatile",
|
"volatile",
|
||||||
"x86",
|
"x86",
|
||||||
"x86_64",
|
"x86_64",
|
||||||
"xmas-elf",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -655,6 +654,7 @@ name = "jinux-frame-x86-boot-setup"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"uart_16550",
|
"uart_16550",
|
||||||
|
"xmas-elf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -729,6 +729,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"glob",
|
"glob",
|
||||||
"rand",
|
"rand",
|
||||||
|
"xmas-elf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -30,8 +30,5 @@ aml = "0.16.3"
|
|||||||
multiboot2 = "0.16.0"
|
multiboot2 = "0.16.0"
|
||||||
rsdp = "2.0.0"
|
rsdp = "2.0.0"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
xmas-elf = "0.8.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
intel_tdx = ["dep:tdx-guest"]
|
intel_tdx = ["dep:tdx-guest"]
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
io::{Seek, Write},
|
io::Write,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use xmas_elf::program::{ProgramHeader, SegmentData};
|
|
||||||
|
|
||||||
// We chose the legacy setup sections to be 7 so that the setup header
|
|
||||||
// is page-aligned and the legacy setup section size would be 0x1000.
|
|
||||||
const LEGACY_SETUP_SECS: usize = 7;
|
|
||||||
const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1);
|
|
||||||
const SETUP32_LMA: usize = 0x100000;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||||
build_linux_setup_header(&source_dir, &out_dir)?;
|
build_linux_setup_header(&source_dir, &out_dir)?;
|
||||||
copy_to_raw_binary(&out_dir)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,44 +59,3 @@ fn build_linux_setup_header(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We need a binary which satisfies `LMA == File_Offset`, and objcopy
|
|
||||||
/// does not satisfy us well, so we should parse the ELF and do our own
|
|
||||||
/// objcopy job.
|
|
||||||
///
|
|
||||||
/// Interstingly, the resulting binary should be the same as the memory
|
|
||||||
/// dump of the kernel setup header when it's loaded by the bootloader.
|
|
||||||
fn copy_to_raw_binary(out_dir: &Path) -> Result<(), Box<dyn Error + Send + Sync>> {
|
|
||||||
// Strip the elf header to get the raw header.
|
|
||||||
let elf_path = out_dir.join("bin").join("jinux-frame-x86-boot-setup");
|
|
||||||
let bin_path = out_dir.join("bin").join("jinux-frame-x86-boot-setup.bin");
|
|
||||||
|
|
||||||
let elf_file = std::fs::read(elf_path)?;
|
|
||||||
let elf = xmas_elf::ElfFile::new(&elf_file)?;
|
|
||||||
|
|
||||||
let bin_file = std::fs::File::create(bin_path)?;
|
|
||||||
let mut bin_writer = std::io::BufWriter::new(bin_file);
|
|
||||||
|
|
||||||
for ph in elf.program_iter() {
|
|
||||||
let ProgramHeader::Ph32(program) = ph else {
|
|
||||||
return Err("Unexpected program header type".into());
|
|
||||||
};
|
|
||||||
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
|
||||||
let dest_file_offset = program.virtual_addr as usize + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA;
|
|
||||||
bin_writer.seek(std::io::SeekFrom::End(0))?;
|
|
||||||
let cur_file_offset = bin_writer.stream_position().unwrap() as usize;
|
|
||||||
if cur_file_offset < dest_file_offset {
|
|
||||||
let padding = vec![0; dest_file_offset - cur_file_offset];
|
|
||||||
bin_writer.write_all(&padding)?;
|
|
||||||
} else {
|
|
||||||
bin_writer.seek(std::io::SeekFrom::Start(dest_file_offset as u64))?;
|
|
||||||
}
|
|
||||||
let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
|
|
||||||
return Err("Unexpected segment data type".into());
|
|
||||||
};
|
|
||||||
bin_writer.write_all(header_data)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -15,7 +15,7 @@ MULTIBOOT_ENTRY_MAGIC = 0x2BADB002
|
|||||||
MULTIBOOT2_ENTRY_MAGIC = 0x36D76289
|
MULTIBOOT2_ENTRY_MAGIC = 0x36D76289
|
||||||
|
|
||||||
// The Linux 32-bit Boot Protocol entry point.
|
// The Linux 32-bit Boot Protocol entry point.
|
||||||
// Must be located at 0x100000, ABI immutable!
|
// Must be located at 0x8001000, ABI immutable!
|
||||||
.code32
|
.code32
|
||||||
.org 0x000
|
.org 0x000
|
||||||
.global __linux32_boot
|
.global __linux32_boot
|
||||||
@ -34,7 +34,7 @@ __linux32_boot:
|
|||||||
jmp initial_boot_setup
|
jmp initial_boot_setup
|
||||||
|
|
||||||
// The Linux 64-bit Boot Protocol entry point.
|
// The Linux 64-bit Boot Protocol entry point.
|
||||||
// Must be located at 0x100200, ABI immutable!
|
// Must be located at 0x8001200, ABI immutable!
|
||||||
.code64
|
.code64
|
||||||
.org 0x200
|
.org 0x200
|
||||||
.global __linux64_boot_tag
|
.global __linux64_boot_tag
|
||||||
|
@ -2,7 +2,8 @@ ENTRY(__multiboot_boot)
|
|||||||
OUTPUT_ARCH(i386:x86-64)
|
OUTPUT_ARCH(i386:x86-64)
|
||||||
OUTPUT_FORMAT(elf64-x86-64)
|
OUTPUT_FORMAT(elf64-x86-64)
|
||||||
|
|
||||||
KERNEL_LMA = 0x100000;
|
KERNEL_LMA = 0x8000000;
|
||||||
|
LINUX_32_ENTRY = 0x8001000;
|
||||||
KERNEL_VMA = 0xffffffff80000000;
|
KERNEL_VMA = 0xffffffff80000000;
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@ -13,6 +14,9 @@ SECTIONS
|
|||||||
|
|
||||||
.multiboot_header : { KEEP(*(.multiboot_header)) }
|
.multiboot_header : { KEEP(*(.multiboot_header)) }
|
||||||
.multiboot2_header : { KEEP(*(.multiboot2_header)) }
|
.multiboot2_header : { KEEP(*(.multiboot2_header)) }
|
||||||
|
|
||||||
|
. = LINUX_32_ENTRY;
|
||||||
|
|
||||||
.boot : { KEEP(*(.boot)) }
|
.boot : { KEEP(*(.boot)) }
|
||||||
|
|
||||||
. += KERNEL_VMA;
|
. += KERNEL_VMA;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//! currently not needed by Jinux.
|
//! currently not needed by Jinux.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct ScreenInfo {
|
pub(super) struct ScreenInfo {
|
||||||
pub(super) orig_x: u8, /* 0x00 */
|
pub(super) orig_x: u8, /* 0x00 */
|
||||||
@ -48,6 +49,7 @@ pub(super) struct ScreenInfo {
|
|||||||
pub(super) _reserved: [u8; 2], /* 0x3e */
|
pub(super) _reserved: [u8; 2], /* 0x3e */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct ApmBiosInfo {
|
pub(super) struct ApmBiosInfo {
|
||||||
pub(super) version: u16,
|
pub(super) version: u16,
|
||||||
@ -61,6 +63,7 @@ pub(super) struct ApmBiosInfo {
|
|||||||
pub(super) dseg_len: u16,
|
pub(super) dseg_len: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct IstInfo {
|
pub(super) struct IstInfo {
|
||||||
pub(super) signature: u32,
|
pub(super) signature: u32,
|
||||||
@ -69,12 +72,14 @@ pub(super) struct IstInfo {
|
|||||||
pub(super) perf_level: u32,
|
pub(super) perf_level: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct SysDescTable {
|
pub(super) struct SysDescTable {
|
||||||
pub(super) length: u16,
|
pub(super) length: u16,
|
||||||
pub(super) table: [u8; 14],
|
pub(super) table: [u8; 14],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct OlpcOfwHeader {
|
pub(super) struct OlpcOfwHeader {
|
||||||
pub(super) ofw_magic: u32, /* OFW signature */
|
pub(super) ofw_magic: u32, /* OFW signature */
|
||||||
@ -83,11 +88,13 @@ pub(super) struct OlpcOfwHeader {
|
|||||||
pub(super) irq_desc_table: u32,
|
pub(super) irq_desc_table: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub(super) struct EdidInfo {
|
pub(super) struct EdidInfo {
|
||||||
pub(super) dummy: [u8; 128],
|
pub(super) dummy: [u8; 128],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub(super) struct EfiInfo {
|
pub(super) struct EfiInfo {
|
||||||
pub(super) efi_loader_signature: u32,
|
pub(super) efi_loader_signature: u32,
|
||||||
@ -100,8 +107,6 @@ pub(super) struct EfiInfo {
|
|||||||
pub(super) efi_memmap_hi: u32,
|
pub(super) efi_memmap_hi: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Magic stored in SetupHeader.boot_flag.
|
|
||||||
pub(super) const LINUX_BOOT_FLAG_MAGIC: u16 = 0xAA55;
|
|
||||||
/// Magic stored in SetupHeader.header.
|
/// Magic stored in SetupHeader.header.
|
||||||
pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448;
|
pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448;
|
||||||
|
|
||||||
@ -109,6 +114,7 @@ pub(super) const LINUX_BOOT_HEADER_MAGIC: u32 = 0x53726448;
|
|||||||
///
|
///
|
||||||
/// Originally defined in the linux source tree:
|
/// Originally defined in the linux source tree:
|
||||||
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct SetupHeader {
|
pub(super) struct SetupHeader {
|
||||||
pub(super) setup_sects: u8,
|
pub(super) setup_sects: u8,
|
||||||
@ -156,7 +162,7 @@ pub(super) struct SetupHeader {
|
|||||||
///
|
///
|
||||||
/// Originally defined in the linux source tree:
|
/// Originally defined in the linux source tree:
|
||||||
/// `linux/arch/x86/include/asm/e820/types.h`
|
/// `linux/arch/x86/include/asm/e820/types.h`
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub(super) enum E820Type {
|
pub(super) enum E820Type {
|
||||||
Ram = 1,
|
Ram = 1,
|
||||||
@ -194,6 +200,7 @@ pub(super) enum E820Type {
|
|||||||
ReservedKern = 128,
|
ReservedKern = 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct BootE820Entry {
|
pub(super) struct BootE820Entry {
|
||||||
pub(super) addr: u64,
|
pub(super) addr: u64,
|
||||||
@ -203,6 +210,7 @@ pub(super) struct BootE820Entry {
|
|||||||
|
|
||||||
const E820_MAX_ENTRIES_ZEROPAGE: usize = 128;
|
const E820_MAX_ENTRIES_ZEROPAGE: usize = 128;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct EddDeviceParams {
|
pub(super) struct EddDeviceParams {
|
||||||
// TODO: We currently have no plans to support the edd device,
|
// TODO: We currently have no plans to support the edd device,
|
||||||
@ -212,6 +220,7 @@ pub(super) struct EddDeviceParams {
|
|||||||
pub(super) _dummy: [u8; (0xeec - 0xd00) / 6 - 8],
|
pub(super) _dummy: [u8; (0xeec - 0xd00) / 6 - 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct EddInfo {
|
pub(super) struct EddInfo {
|
||||||
pub(super) device: u8,
|
pub(super) device: u8,
|
||||||
@ -230,6 +239,7 @@ const EDDMAXNR: usize = 6;
|
|||||||
///
|
///
|
||||||
/// Originally defined in the linux source tree:
|
/// Originally defined in the linux source tree:
|
||||||
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
/// `linux/arch/x86/include/uapi/asm/bootparam.h`
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(super) struct BootParams {
|
pub(super) struct BootParams {
|
||||||
pub(super) screen_info: ScreenInfo, /* 0x000 */
|
pub(super) screen_info: ScreenInfo, /* 0x000 */
|
||||||
|
@ -78,16 +78,14 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
|
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
|
||||||
acpi.call_once(|| {
|
let rsdp = BOOT_PARAMS.get().unwrap().acpi_rsdp_addr;
|
||||||
BootloaderAcpiArg::Rsdp(
|
if rsdp == 0 {
|
||||||
BOOT_PARAMS
|
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
|
||||||
.get()
|
} else {
|
||||||
.unwrap()
|
acpi.call_once(|| {
|
||||||
.acpi_rsdp_addr
|
BootloaderAcpiArg::Rsdp(rsdp.try_into().expect("RSDP address overflowed!"))
|
||||||
.try_into()
|
});
|
||||||
.unwrap(),
|
}
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
|
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
|
||||||
@ -133,10 +131,9 @@ extern "Rust" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader.
|
/// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader.
|
||||||
/// It is the ELF entrypoint.
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "sysv64" fn __linux64_boot(params: boot_params::BootParams) -> ! {
|
unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const boot_params::BootParams) -> ! {
|
||||||
assert_eq!({ params.hdr.boot_flag }, boot_params::LINUX_BOOT_FLAG_MAGIC);
|
let params = *params_ptr;
|
||||||
assert_eq!({ params.hdr.header }, boot_params::LINUX_BOOT_HEADER_MAGIC);
|
assert_eq!({ params.hdr.header }, boot_params::LINUX_BOOT_HEADER_MAGIC);
|
||||||
BOOT_PARAMS.call_once(|| params);
|
BOOT_PARAMS.call_once(|| params);
|
||||||
crate::boot::register_boot_init_callbacks(
|
crate::boot::register_boot_init_callbacks(
|
||||||
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uart_16550 = "0.3.0"
|
uart_16550 = "0.3.0"
|
||||||
|
xmas-elf = "0.8.0"
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
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)
|
||||||
|
}
|
@ -6,11 +6,13 @@ struct Stdout {
|
|||||||
serial_port: SerialPort,
|
serial_port: SerialPort,
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut STDOUT: Stdout = Stdout { serial_port: unsafe { SerialPort::new(0x0) } };
|
static mut STDOUT: Stdout = Stdout {
|
||||||
|
serial_port: unsafe { SerialPort::new(0x0) },
|
||||||
|
};
|
||||||
|
|
||||||
/// safety: this function must only be called once
|
/// safety: this function must only be called once
|
||||||
pub unsafe fn init() {
|
pub unsafe fn init() {
|
||||||
STDOUT = Stdout::init();
|
STDOUT = Stdout::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stdout {
|
impl Stdout {
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
use xmas_elf::program::{ProgramHeader, SegmentData};
|
||||||
|
|
||||||
|
pub fn load_elf(file: &[u8]) -> u32 {
|
||||||
|
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!");
|
||||||
|
};
|
||||||
|
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||||
|
let SegmentData::Undefined(header_data) = program.get_data(&elf).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,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
dst_slice[..program.file_size as usize].copy_from_slice(header_data);
|
||||||
|
let zero_slice = &mut dst_slice[program.file_size as usize..];
|
||||||
|
zero_slice.fill(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the Linux 32-bit Boot Protocol entry point defined by Jinux.
|
||||||
|
0x8001000
|
||||||
|
}
|
@ -1,22 +1,44 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
mod boot_params;
|
||||||
mod console;
|
mod console;
|
||||||
|
mod loader;
|
||||||
|
|
||||||
use core::arch::global_asm;
|
use core::arch::{asm, global_asm};
|
||||||
|
|
||||||
global_asm!(include_str!("header.S"));
|
global_asm!(include_str!("header.S"));
|
||||||
|
|
||||||
|
unsafe fn call_jinux_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!();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "cdecl" fn _rust_setup_entry(boot_params_ptr: u32) -> ! {
|
pub extern "cdecl" fn _rust_setup_entry(boot_params_ptr: u32) -> ! {
|
||||||
// safety: this init function is only called once
|
// Safety: this init function is only called once.
|
||||||
unsafe { console::init() };
|
unsafe { console::init() };
|
||||||
println!("[setup] boot_params_ptr: {:#x}", boot_params_ptr);
|
println!("[setup] boot_params_ptr: {:#x}", boot_params_ptr);
|
||||||
#[allow(clippy::empty_loop)]
|
|
||||||
loop {}
|
let payload_offset = unsafe { boot_params::get_payload_offset(boot_params_ptr) };
|
||||||
|
let payload_length = unsafe { boot_params::get_payload_length(boot_params_ptr) };
|
||||||
|
let payload = unsafe {
|
||||||
|
core::slice::from_raw_parts_mut(payload_offset as *mut u8, payload_length as usize)
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("[setup] loading ELF payload...");
|
||||||
|
let entrypoint = loader::load_elf(payload);
|
||||||
|
println!("[setup] entrypoint: {:#x}", entrypoint);
|
||||||
|
|
||||||
|
// Safety: the entrypoint and the ptr is valid.
|
||||||
|
unsafe { call_jinux_entrypoint(entrypoint, boot_params_ptr) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
println!("panic: {:?}", info);
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ use multiboot2::MemoryAreaType;
|
|||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::x86::kernel::acpi::AcpiMemoryHandler,
|
|
||||||
boot::{
|
boot::{
|
||||||
kcmdline::KCmdlineArg,
|
kcmdline::KCmdlineArg,
|
||||||
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
|
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
|
||||||
@ -88,20 +87,7 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
|
|||||||
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
|
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
|
||||||
// The multiboot protocol does not contain RSDP address.
|
// The multiboot protocol does not contain RSDP address.
|
||||||
// TODO: What about UEFI?
|
// TODO: What about UEFI?
|
||||||
let rsdp = unsafe { rsdp::Rsdp::search_for_on_bios(AcpiMemoryHandler {}) };
|
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
|
||||||
match rsdp {
|
|
||||||
Ok(map) => match map.validate() {
|
|
||||||
Ok(_) => acpi.call_once(|| {
|
|
||||||
if map.revision() > 0 {
|
|
||||||
BootloaderAcpiArg::Xsdt(map.xsdt_address() as usize)
|
|
||||||
} else {
|
|
||||||
BootloaderAcpiArg::Rsdt(map.rsdt_address() as usize)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Err(_) => acpi.call_once(|| BootloaderAcpiArg::NotExists),
|
|
||||||
},
|
|
||||||
Err(_) => acpi.call_once(|| BootloaderAcpiArg::NotExists),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
|
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
|
||||||
|
@ -104,9 +104,42 @@ pub fn init() {
|
|||||||
BootloaderAcpiArg::Xsdt(addr) => unsafe {
|
BootloaderAcpiArg::Xsdt(addr) => unsafe {
|
||||||
AcpiTables::from_rsdt(AcpiMemoryHandler {}, 1, addr).unwrap()
|
AcpiTables::from_rsdt(AcpiMemoryHandler {}, 1, addr).unwrap()
|
||||||
},
|
},
|
||||||
BootloaderAcpiArg::NotExists => {
|
BootloaderAcpiArg::NotProvided => {
|
||||||
warn!("Not found ACPI table");
|
// We search by ourselves if the bootloader decides not to provide a rsdp location.
|
||||||
return;
|
let rsdp = unsafe { rsdp::Rsdp::search_for_on_bios(AcpiMemoryHandler {}) };
|
||||||
|
match rsdp {
|
||||||
|
Ok(map) => match map.validate() {
|
||||||
|
Ok(_) => {
|
||||||
|
if map.revision() > 0 {
|
||||||
|
unsafe {
|
||||||
|
AcpiTables::from_rsdt(
|
||||||
|
AcpiMemoryHandler {},
|
||||||
|
1,
|
||||||
|
map.xsdt_address() as usize,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
AcpiTables::from_rsdt(
|
||||||
|
AcpiMemoryHandler {},
|
||||||
|
0,
|
||||||
|
map.rsdt_address() as usize,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("ACPI info not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
warn!("ACPI info not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ use spin::Once;
|
|||||||
/// This is because bootloaders differ in such behaviors.
|
/// This is because bootloaders differ in such behaviors.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum BootloaderAcpiArg {
|
pub enum BootloaderAcpiArg {
|
||||||
NotExists,
|
/// The bootloader does not provide one, a manual search is needed.
|
||||||
|
NotProvided,
|
||||||
/// Physical address of the RSDP.
|
/// Physical address of the RSDP.
|
||||||
Rsdp(usize),
|
Rsdp(usize),
|
||||||
/// Address of RSDT provided in RSDP v1.
|
/// Address of RSDT provided in RSDP v1.
|
||||||
|
@ -9,3 +9,4 @@ anyhow = "1.0.32"
|
|||||||
clap = { version = "4.3.19", features = ["derive"] }
|
clap = { version = "4.3.19", features = ["derive"] }
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
xmas-elf = "0.8.0"
|
||||||
|
98
runner/src/machine/qemu_grub_efi/linux_boot.rs
Normal file
98
runner/src/machine/qemu_grub_efi/linux_boot.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use xmas_elf::program::{ProgramHeader, SegmentData};
|
||||||
|
|
||||||
|
// We chose the legacy setup sections to be 7 so that the setup header
|
||||||
|
// is page-aligned and the legacy setup section size would be 0x1000.
|
||||||
|
const LEGACY_SETUP_SECS: usize = 7;
|
||||||
|
const LEGACY_SETUP_SEC_SIZE: usize = 0x200 * (LEGACY_SETUP_SECS + 1);
|
||||||
|
const SETUP32_LMA: usize = 0x100000;
|
||||||
|
|
||||||
|
/// We need a binary which satisfies `LMA == File_Offset`, and objcopy
|
||||||
|
/// does not satisfy us well, so we should parse the ELF and do our own
|
||||||
|
/// objcopy job.
|
||||||
|
///
|
||||||
|
/// Interstingly, the resulting binary should be the same as the memory
|
||||||
|
/// dump of the kernel setup header when it's loaded by the bootloader.
|
||||||
|
fn header_to_raw_binary(elf_file: &[u8]) -> Vec<u8> {
|
||||||
|
let elf = xmas_elf::ElfFile::new(&elf_file).unwrap();
|
||||||
|
let mut bin = Vec::<u8>::new();
|
||||||
|
|
||||||
|
for ph in elf.program_iter() {
|
||||||
|
let ProgramHeader::Ph32(program) = ph else {
|
||||||
|
panic!("Unexpected program header type");
|
||||||
|
};
|
||||||
|
if program.get_type().unwrap() == xmas_elf::program::Type::Load {
|
||||||
|
let SegmentData::Undefined(header_data) = program.get_data(&elf).unwrap() else {
|
||||||
|
panic!("Unexpected segment data type");
|
||||||
|
};
|
||||||
|
let dst_file_offset =
|
||||||
|
program.virtual_addr as usize + LEGACY_SETUP_SEC_SIZE - SETUP32_LMA;
|
||||||
|
let dst_file_length = program.file_size as usize;
|
||||||
|
if bin.len() < dst_file_offset + dst_file_length {
|
||||||
|
bin.resize(dst_file_offset + dst_file_length, 0);
|
||||||
|
}
|
||||||
|
let dest_slice = bin[dst_file_offset..dst_file_offset + dst_file_length].as_mut();
|
||||||
|
dest_slice.copy_from_slice(header_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bin
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function sould be used when generating the Linux x86 Boot setup header.
|
||||||
|
/// Some fields in the Linux x86 Boot setup header should be filled after assembled.
|
||||||
|
/// And the filled fields must have the bytes with values of 0xAB. See
|
||||||
|
/// `framework/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S` for more
|
||||||
|
/// info on this mechanism.
|
||||||
|
fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
|
||||||
|
let size = value.len();
|
||||||
|
assert_eq!(
|
||||||
|
&header[offset..offset + size],
|
||||||
|
vec![0xABu8; size].as_slice()
|
||||||
|
);
|
||||||
|
header[offset..offset + size].copy_from_slice(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_bzimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> {
|
||||||
|
let mut header = Vec::new();
|
||||||
|
File::open(header_path)?.read_to_end(&mut header)?;
|
||||||
|
let mut header = header_to_raw_binary(&header);
|
||||||
|
// Pad the header to let the payload starts with 8-byte alignment.
|
||||||
|
header.resize((header.len() + 7) & !7, 0x00);
|
||||||
|
|
||||||
|
let mut kernel = Vec::new();
|
||||||
|
File::open(kernel_path)?.read_to_end(&mut kernel)?;
|
||||||
|
|
||||||
|
let header_len = header.len();
|
||||||
|
let kernel_len = kernel.len();
|
||||||
|
|
||||||
|
let payload_offset = header_len - LEGACY_SETUP_SEC_SIZE + SETUP32_LMA;
|
||||||
|
fill_header_field(
|
||||||
|
&mut header,
|
||||||
|
0x248, /* payload_offset */
|
||||||
|
&(payload_offset as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_header_field(
|
||||||
|
&mut header,
|
||||||
|
0x24C, /* payload_length */
|
||||||
|
&(kernel_len as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_header_field(
|
||||||
|
&mut header,
|
||||||
|
0x260, /* init_size */
|
||||||
|
&((header_len + kernel_len) as u32).to_le_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut kernel_image = File::create(path)?;
|
||||||
|
kernel_image.write_all(&header)?;
|
||||||
|
kernel_image.write_all(&kernel)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
mod linux_boot;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs,
|
||||||
io::{Read, Write},
|
io::Read,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,13 +87,14 @@ pub fn create_bootdev_image(
|
|||||||
BootProtocol::Linux => {
|
BootProtocol::Linux => {
|
||||||
// Find the setup header in the build script output directory.
|
// Find the setup header in the build script output directory.
|
||||||
let bs_out_dir = glob("target/x86_64-custom/debug/build/jinux-frame-*").unwrap();
|
let bs_out_dir = glob("target/x86_64-custom/debug/build/jinux-frame-*").unwrap();
|
||||||
let header_bin = Path::new(bs_out_dir.into_iter().next().unwrap().unwrap().as_path())
|
let header_path = Path::new(bs_out_dir.into_iter().next().unwrap().unwrap().as_path())
|
||||||
.join("out")
|
.join("out")
|
||||||
.join("bin")
|
.join("bin")
|
||||||
.join("jinux-frame-x86-boot-setup.bin");
|
.join("jinux-frame-x86-boot-setup");
|
||||||
// Make the `zimage`-compatible kernel image and place it in the boot directory.
|
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
|
||||||
let target_path = iso_root.join("boot").join("jinuz");
|
let target_path = iso_root.join("boot").join("jinuz");
|
||||||
make_zimage(&target_path, &jinux_path.as_path(), &header_bin.as_path()).unwrap();
|
linux_boot::make_bzimage(&target_path, &jinux_path.as_path(), &header_path.as_path())
|
||||||
|
.unwrap();
|
||||||
target_path
|
target_path
|
||||||
}
|
}
|
||||||
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
|
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
|
||||||
@ -162,52 +165,3 @@ pub fn generate_grub_cfg(
|
|||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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/jinux-frame/src/arch/x86/boot/linux_boot/setup/src/header.S` for more
|
|
||||||
/// info on this mechanism.
|
|
||||||
fn fill_header_field(header: &mut [u8], offset: usize, value: &[u8]) {
|
|
||||||
let size = value.len();
|
|
||||||
assert_eq!(
|
|
||||||
&header[offset..offset + size],
|
|
||||||
vec![0xABu8; size].as_slice()
|
|
||||||
);
|
|
||||||
header[offset..offset + size].copy_from_slice(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_zimage(path: &Path, kernel_path: &Path, header_path: &Path) -> std::io::Result<()> {
|
|
||||||
let mut header = Vec::new();
|
|
||||||
File::open(header_path)?.read_to_end(&mut header)?;
|
|
||||||
// Pad the header to let the payload starts with 8-byte alignment.
|
|
||||||
header.resize((header.len() + 7) & !7, 0x00);
|
|
||||||
|
|
||||||
let mut kernel = Vec::new();
|
|
||||||
File::open(kernel_path)?.read_to_end(&mut kernel)?;
|
|
||||||
|
|
||||||
let header_len = header.len();
|
|
||||||
let kernel_len = kernel.len();
|
|
||||||
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x248, /* payload_offset */
|
|
||||||
&(header_len as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x24C, /* payload_length */
|
|
||||||
&(kernel_len as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
fill_header_field(
|
|
||||||
&mut header,
|
|
||||||
0x260, /* init_size */
|
|
||||||
&((kernel_len + header_len) as u32).to_le_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut kernel_image = File::create(path)?;
|
|
||||||
kernel_image.write_all(&header)?;
|
|
||||||
kernel_image.write_all(&kernel)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user