rust-osdev / uefi-rs

Rusty wrapper for the Unified Extensible Firmware Interface (UEFI). This crate makes it easy to develop Rust software that leverages safe, convenient, and performant abstractions for UEFI functionality.
https://rust-osdev.com/uefi-book
Mozilla Public License 2.0
1.33k stars 161 forks source link

Just a question about how I can exit boot services correctly. #1238

Closed OrdChaos closed 3 months ago

OrdChaos commented 4 months ago

I've tried my best to look for the docs about how I can exit boot services in last few days. However, I don't found anything about it. I'm a green hand on this, sorry. QAQ

I know that on EDKII, I can just use BootServices->ExitBootServices(ImageHandle, Mapkey), but what should I do when I'm using this crate? I tried this: let _ = system_table.exit_boot_services(MemoryType::BOOT_SERVICES_DATA); But whether the memory structure changes has nothing to do with whether I execute this

And this: let _ = system_table.exit_boot_services(MemoryType::CONVENTIONAL); qemu just reboot.

I have seen a signature similar to that implemented in EDKII in &BootServices, but it cannot be used for security reasons. So, ... What should I do?

Best wishes for everyone can help me (and cannot help me). QAQ

My code:

fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
    uefi::helpers::init(&mut system_table).unwrap();
    system_table.stdout().reset(false).unwrap();
    let boot_services = system_table.boot_services();

    //File Init
    let simple_file_system = boot_services.get_image_file_system(image_handle).unwrap();

    //Load Kernel
    let entry_point_addr = load_kernel(cstr16!("\\kernel.elf"), boot_services, simple_file_system);
    let entry_point: extern "sysv64" fn(&FrameBufferInfo, &MemoryMapInfo) -> ! = unsafe { mem::transmute(entry_point_addr) };

    //Graphic Init
    let frame_buffer_info = get_frame_buffer_info(boot_services);
    println!("FrameBuffer Info:\n - base_addr: {:#x}\n - screen_width: {}\n - screen_height: {}\n - stride: {}",
        frame_buffer_info.base_addr,
        frame_buffer_info.screen_width,
        frame_buffer_info.screen_height,
        frame_buffer_info.stride,
    );

    //Memory Map Init
    let memory_map = get_memory_map_info(boot_services);
    println!("MemoryMap Info:\n - map_size: {}\n - map_desc_size: {}",
        memory_map.map_size,
        memory_map.map_desc_size
    );

    //Exit Boot Services
    println!("Now Exit the Boot Services");
    let _ = system_table.exit_boot_services(MemoryType::BOOT_SERVICES_DATA);

    //To Kernel
    entry_point(&frame_buffer_info, &memory_map);
}

//...

fn get_memory_map_info(boot_services: &BootServices) -> MemoryMapInfo {
    let memory_map_size = boot_services.memory_map_size();
    let entry_size = memory_map_size.entry_size;
    let map_size = memory_map_size.map_size;

    let buffer_size = map_size + 8 * entry_size;
    let buffer = boot_services.allocate_pool(MemoryType::LOADER_DATA, buffer_size).unwrap();

    let memory_map_ptr = buffer as *mut u8;
    let mut memory_map_buffer = unsafe { slice::from_raw_parts_mut(memory_map_ptr, buffer_size) };

    let memory_map = boot_services.memory_map(&mut memory_map_buffer).unwrap();

    let memory_map_descriptors = memory_map.get(0).map_or(ptr::null_mut(), |desc| desc as *const _ as *mut MemoryDescriptor);

    MemoryMapInfo {
        memory_map: memory_map_descriptors,
        map_size: (memory_map.entries().count() * entry_size) as u64,
        map_desc_size: entry_size as u64
    }
}

The rest parts are ok.

phip1611 commented 4 months ago

Hey there!

First, I'd like to ask you to provide an example that compiles and runs the next time you are asking for help. This makes it much easier to reproduce.

I stripped down your example to a version that compiles:

#![no_main]
#![no_std]

use log::info;
use uefi::prelude::*;
use uefi::println;
use uefi::table::boot::MemoryType;

#[entry]
fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
    uefi::helpers::init(&mut system_table).unwrap()

    //Exit Boot Services
    log::info!("Now Exit the Boot Services");
    let _ = unsafe {
        let _x = system_table.exit_boot_services(MemoryType::BOOT_SERVICES_DATA);
    };
    log::info!("Exited boot services");

    loop {}
}

Here, I can't reproduce anything.

Tip: For debugging, you locally can put your minimal reproducer into <uefi-repo>/uefi-test-runner/examples/your_example.rs and run it using cargo xtask run --example your-example. The <uefi-repo>/integration-test-debugcon.log file will contain log messages even after boot services have been exited.

Perhaps you can debug further with that.

OrdChaos commented 4 months ago

OK

For now, I've found that the problem I met isn't how I can exit(Maybe I don't understand the docs correctly) It is just I can't get the correct entry_size and map_size for me, map_size / entry_size == 111

however, when i try to check every desc, the last few is like this: type = EfiReservedMemoryType size = (a big number)

type = EfiReservedMemoryType size = 0

type = EfiReservedMemoryType size = 0

...... (Size is in pagination units)

We all know that the type EfiReservedMemoryType means 0 so maybe i've tried to read some wrong data.(because size won't be 0) How i can do to get correct data?

    let memory_map_size = boot_services.memory_map_size();
    let entry_size = memory_map_size.entry_size;
    let map_size = memory_map_size.map_size;

Cannot I just use this? (Then I'll exit the boot services)

    let (_system_table_runtime , _iter) = system_table.exit_boot_services(MemoryType::LOADER_DATA);
    let memory_map_descriptors = _iter.get(0).map_or(ptr::null_mut(), |desc| desc as *const _ as *mut MemoryDescriptor);
    let memory_map = MemoryMapInfo {
        memory_map: memory_map_descriptors,
        map_size: map_size as u64,
        map_desc_size: entry_size as u64
    };
phip1611 commented 4 months ago

I'm not sure what you are doing there. Why are you creating your own MemoryMapInfo type? The MemoryMap (see docs.rs) that is returned by uefi already combines the meta data and the memory map itself?

_iter.get(0).map_or(ptr::null_mut(), |desc| desc as *const _ as *mut MemoryDescriptor);

Here are two wrong things. First, you are possibly having a null pointer here. Why not working with Option? Second, typing thw raw memory map as desc as *const _ as *mut MemoryDescriptor); is illegal. It must be *const u8 and each descriptor can be found via (*const u8).add(desz_size * n).cast::<MemoryDescriptor>(). This is unintuitive, but written in the documentation of the uefi crate and also in the UEFI spec.

I'd really want to know what you are doing there. Is something in our documentation missing that made you believe you can't use MemoryMap from our crate?

PS: In case you really want to get the pointer to the raw memory map, use MemoryMap::as_raw.

phip1611 commented 3 months ago

Any update, @OrdChaos ? Will re-open if you reply and there are things to do on our side