Finishing up#

To finally finish writing our bootloader, let’s finish main.rs:

#![no_std]
#![no_main]

mod initial_paging;
mod loader;

const FILE_NAME: &str = "hv.bin";

use crate::{
    initial_paging::copy_uefi_cr3,
    loader::{load_boot_info_and_jump, map_elf_load_segments},
};
use log::info;
use uefi::{
    CStr16, Status,
    boot::{self, ScopedProtocol, open_protocol_exclusive},
    entry,
    fs::{FileSystem, Path},
    proto::{loaded_image::LoadedImage, media::fs::SimpleFileSystem},
};

fn map_hv_binary(sfs: ScopedProtocol<SimpleFileSystem>) {
    let mut fs = FileSystem::new(sfs);

    let mut name_buffer = [0; 7];

    let cstr = CStr16::from_str_with_buf(FILE_NAME, &mut name_buffer).unwrap();

    let path = Path::new(&cstr);

    let buffer = fs.read(path).unwrap();

    let cr3 = copy_uefi_cr3();

    let copied_pml4 = unsafe { *cr3 };

    info!("Copied PML4 {:?}", copied_pml4[0]);

    info!("Loading HV binary into memory...");

    let entry = map_elf_load_segments(cr3, buffer.as_slice());

    info!("entry point: va {:x}", entry);

    load_boot_info_and_jump(cr3, entry, buffer.as_slice());
}

#[entry]
fn main() -> Status {
    uefi::helpers::init().unwrap();

    let loaded_image = open_protocol_exclusive::<LoadedImage>(boot::image_handle()).unwrap();

    let sfs =
        boot::open_protocol_exclusive::<SimpleFileSystem>(loaded_image.device().unwrap()).unwrap();

    map_hv_binary(sfs);

    panic!("we should have never gotten here");
}

This is all we need. Now, to actually boot our kernel we are going to add a couple stuff to our kernel directly, let’s go to main.rs in our hv kernel:

First, let’s add some packages we want, type:

cargo add com_logger
cargo add log

and in cargo.toml don’t forget to add our library as a dependency, so it should look like this:

[dependencies]
com_logger = "0.1.2"
log = "0.4.27"
lib = {path = "../lib"}

Now, in main.rs let’s put this:

#![no_main]
#![no_std]

use lib::{BootInfo, BOOT_MAGIC};
use log::{info, error};

#[panic_handler]
#[cfg(not(test))]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {

    if let Some(location) = info.location() {
        error!("Panic in {} at {}", location.file(), location.line());
    }

    error!("Panic: {}", info);

    loop {}
}

#[unsafe(no_mangle)]
pub extern "C" fn hventry(boot: *mut BootInfo) {
    com_logger::init();

    let info: &BootInfo = unsafe { &*boot };

    if info.is_magic_same(BOOT_MAGIC) {
        info!("Correct magic!");
    };

    loop {};
}

Now let’s compile it and see how it goes!

bootloader

Hmm.. no panics or anything, which is good.. Let’s see when we click “view” and go to the serial output (serial0):

magic

Yay! it works!

On the next blog post, we are going to be actually working more on our kernel and finally virtualizing our existing operating system.