load linux kernel from UEFI

This commit is contained in:
Masato Imai
2025-08-10 13:23:50 +00:00
parent c1e555304c
commit 46e5da346d
8 changed files with 82 additions and 38 deletions

View File

@ -6,12 +6,14 @@ cd ../nel_os_kernel
cargo build --release -q
cd ../nel_os_bootloader
dd if=/dev/zero of=fat.img bs=1k count=1440
mformat -i fat.img -f 1440 ::
dd if=/dev/zero of=fat.img bs=1k count=32768
mformat -i fat.img -C -h 16 -t 128 -s 32 ::
mmd -i fat.img ::/EFI
mmd -i fat.img ::/EFI/BOOT
mcopy -i fat.img "$EFI_BINARY" ::/EFI/BOOT/BOOTX64.EFI
mcopy -i fat.img ../nel_os_kernel/target/x86_64-nel_os/release/nel_os_kernel.elf ::/nel_os_kernel.elf
mcopy -i fat.img bzImage ::/bzImage
mcopy -i fat.img rootfs-n.cpio.gz ::/rootfs-n.cpio.gz
mkdir iso
cp fat.img iso

View File

@ -42,25 +42,53 @@ fn get_fs() -> Directory {
fs.open_volume().unwrap()
}
fn read_kernel(name: &CStr16) -> Box<[u8]> {
fn read_file(name: &CStr16) -> Box<[u8]> {
let mut root = get_fs();
let kernel_file_info = root
let file_info = root
.open(name, FileMode::Read, FileAttribute::empty())
.unwrap();
let mut kernel_file = kernel_file_info.into_regular_file().unwrap();
let mut file = file_info.into_regular_file().unwrap();
let file_size = kernel_file
.get_boxed_info::<FileInfo>()
.unwrap()
.file_size();
let file_size = file.get_boxed_info::<FileInfo>().unwrap().file_size();
let mut buf = vec![0; file_size as usize];
let read_size = kernel_file.read(&mut buf).unwrap();
println!("kernel size: {}", read_size);
let read_size = file.read(&mut buf).unwrap();
println!("file {} size: {}", name, read_size);
buf.into_boxed_slice()
}
fn load_file_to_laoder_data(name: &CStr16) -> (u64, u64) {
let mut root = get_fs();
let file_info = root
.open(name, FileMode::Read, FileAttribute::empty())
.expect("Failed to open file");
let mut file = file_info
.into_regular_file()
.expect("Failed to convert to regular file");
let file_size = file
.get_boxed_info::<FileInfo>()
.expect("Failed to get file info")
.file_size();
let page_ptr = unsafe {
uefi::boot::allocate_pages(
AllocateType::AnyPages,
MemoryType::LOADER_DATA,
file_size.div_ceil(4096) as usize,
)
.expect("Failed to allocate pages")
.as_mut()
};
let buf = unsafe { slice::from_raw_parts_mut(page_ptr, file_size as usize) };
let read_size = file.read(buf).expect("Failed to read file");
println!("file {} size: {}", name, read_size);
(page_ptr as *mut u8 as u64, file_size)
}
fn load_elf(bin: Box<[u8]>) -> u64 {
let elf = elf::Elf::parse(&bin).expect("Failed to parse elf");
let mut dest_start = u64::MAX;
@ -143,7 +171,10 @@ fn main() -> Status {
println!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
let kernel = read_kernel(cstr16!("nel_os_kernel.elf"));
let kernel = read_file(cstr16!("nel_os_kernel.elf"));
let (bzimage_addr, bzimage_size) = load_file_to_laoder_data(cstr16!("bzImage"));
let (rootfs_addr, rootfs_size) = load_file_to_laoder_data(cstr16!("rootfs-n.cpio.gz"));
let entry_point = load_elf(kernel);
@ -195,6 +226,10 @@ fn main() -> Status {
usable_memory,
frame_buffer,
rsdp,
bzimage_addr,
bzimage_size,
rootfs_addr,
rootfs_size,
});
hlt_loop();

View File

@ -9,4 +9,8 @@ pub struct BootInfo {
pub usable_memory: UsableMemory,
pub frame_buffer: FrameBuffer,
pub rsdp: u64,
pub bzimage_addr: u64,
pub bzimage_size: u64,
pub rootfs_addr: u64,
pub rootfs_size: u64,
}

View File

@ -22,6 +22,7 @@ use core::panic::PanicInfo;
use core::ptr::addr_of;
use ::acpi::AcpiTables;
use spin::Once;
use x86_64::{registers::control::Cr3, structures::paging::OffsetPageTable, VirtAddr};
use crate::{
@ -32,6 +33,11 @@ use crate::{
memory::{allocator, memory::BitmapMemoryTable, paging},
};
pub static BZIMAGE_ADDR: Once<u64> = Once::new();
pub static BZIMAGE_SIZE: Once<u64> = Once::new();
pub static ROOTFS_ADDR: Once<u64> = Once::new();
pub static ROOTFS_SIZE: Once<u64> = Once::new();
#[repr(C, align(16))]
struct AlignedStack {
stack: [u8; KERNEL_STACK_SIZE],
@ -159,7 +165,14 @@ pub extern "sysv64" fn main(boot_info: &nel_os_common::BootInfo) {
serial::disable_screen_output();
BZIMAGE_ADDR.call_once(|| boot_info.bzimage_addr);
BZIMAGE_SIZE.call_once(|| boot_info.bzimage_size);
ROOTFS_ADDR.call_once(|| boot_info.rootfs_addr);
ROOTFS_SIZE.call_once(|| boot_info.rootfs_size);
let mut vcpu = vmm::get_vcpu(&mut bitmap_table).unwrap();
info!("Running guest VM...");
loop {
let result = vcpu.run(&mut bitmap_table);
if let Err(e) = result {

View File

@ -1,12 +1,16 @@
use core::ptr::read_unaligned;
use crate::vmm::VCpu;
use crate::{vmm::VCpu, BZIMAGE_ADDR, BZIMAGE_SIZE};
pub fn load_kernel(vcpu: &mut dyn VCpu) -> Result<(), &'static str> {
let kernel = BZIMAGE;
let kernel_addr = BZIMAGE_ADDR.get().unwrap();
let kernel_size = BZIMAGE_SIZE.get().unwrap();
let kernel =
unsafe { core::slice::from_raw_parts(*kernel_addr as *const u8, *kernel_size as usize) };
let guest_mem_size = vcpu.get_guest_memory_size();
let mut bp = BootParams::from_bytes(kernel).unwrap();
let mut bp = BootParams::from_bytes(kernel)?;
bp.e820_entries = 0;
bp.hdr.type_of_loader = 0xFF;
@ -68,9 +72,6 @@ fn load_image(vcpu: &mut dyn VCpu, image: &[u8], addr: usize) -> Result<(), &'st
Ok(())
}
pub const BZIMAGE: &[u8] = include_bytes!("../../../../bzImage");
pub const INITRD: &[u8] = include_bytes!("../../../../rootfs-n.cpio.gz");
pub const LAYOUT_BOOTPARAM: u64 = 0x0001_0000;
pub const LAYOUT_CMDLINE: u64 = 0x0002_0000;
pub const LAYOUT_KERNEL_BASE: u64 = 0x0010_0000;
@ -209,7 +210,7 @@ impl SetupHeader {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
if bytes.len() < Self::HEADER_OFFSET + size_of::<Self>() {
return Err("バイト配列が小さすぎます");
return Err("Binary data is too short to contain a valid SetupHeader");
}
let mut hdr = unsafe {
@ -335,7 +336,7 @@ impl E820Entry {
3 => Ok(E820Type::Acpi),
4 => Ok(E820Type::Nvs),
5 => Ok(E820Type::Unusable),
_ => Err("不明なE820タイプ"),
_ => Err("Invalid E820 type"),
}
}

View File

@ -124,10 +124,12 @@ impl IntelVCpu {
controls::setup_entry_controls()?;
controls::setup_exit_controls()?;
Self::setup_host_state()?;
Self::setup_guest_state()?;
self.setup_guest_state()?;
self.init_guest_memory(frame_allocator)?;
common::linux::load_kernel(self)?;
Ok(())
}
@ -135,7 +137,7 @@ impl IntelVCpu {
&mut self,
frame_allocator: &mut dyn FrameAllocator<Size4KiB>,
) -> Result<(), &'static str> {
let mut pages = 1000;
let mut pages = self.guest_memory_size / 0x1000;
let mut gpa = 0;
while pages > 0 {
@ -147,22 +149,9 @@ impl IntelVCpu {
pages -= 1;
}
let guest_ptr = Self::test_guest_code as u64;
let guest_addr = self.ept.get_phys_addr(0).unwrap();
unsafe {
core::ptr::copy_nonoverlapping(guest_ptr as *const u8, guest_addr as *mut u8, 200);
}
let eptp = ept::EPTP::init(&self.ept.root_table);
vmwrite(x86::vmx::vmcs::control::EPTP_FULL, u64::from(eptp))?;
info!(
"GPA 0x0 -> HPA {:#x}",
self.ept
.get_phys_addr(0)
.ok_or("Failed to get physical address")?
);
Ok(())
}
@ -213,7 +202,7 @@ impl IntelVCpu {
Ok(())
}
fn setup_guest_state() -> Result<(), &'static str> {
fn setup_guest_state(&mut self) -> Result<(), &'static str> {
use x86::{controlregs::*, vmx::vmcs};
let cr0 = Cr0::empty()
| Cr0::CR0_PROTECTED_MODE
@ -316,8 +305,8 @@ impl IntelVCpu {
vmwrite(vmcs::guest::RFLAGS, 0x2)?;
vmwrite(vmcs::guest::LINK_PTR_FULL, u64::MAX)?;
vmwrite(vmcs::guest::RIP, 0)?; // TODO: Set linux kernel base
// TODO: RSI
vmwrite(vmcs::guest::RIP, common::linux::LAYOUT_KERNEL_BASE as u64)?;
self.guest_registers.rsi = common::linux::LAYOUT_BOOTPARAM as u64;
//vmwrite(vmcs::control::CR0_READ_SHADOW, vmread(vmcs::guest::CR0)?)?;
//vmwrite(vmcs::control::CR4_READ_SHADOW, vmread(vmcs::guest::CR4)?)?;