我正在制作一个基本的 uefi 应用程序,该应用程序应该加载 elf 内核。我已经加载了填充文件和包含文件信息的缓冲区。但要实际读取文件并对其执行任何操作,我需要知道文件大小,以便为其创建缓冲区。我知道 uefi-rs 有一个
FileInfo
结构,但我不知道如何将我拥有的缓冲区转换为这个结构。
我尝试寻找类似问题的解决方案,遇到了这个 Transmuting u8 buffer to struct in Rust。这些解决方案都不起作用,我一直收到该页面上答案的错误,因为我无法将瘦 u8 指针转换为胖
FileInfo
指针。
这是我到目前为止的源代码:
#![no_main]
#![no_std]
#![feature(abi_efiapi)]
#![allow(stable_features)]
#[macro_use]
extern crate alloc;
use elf_rs::{Elf, ElfFile};
use log::info;
use uefi::{prelude::{entry, BootServices, cstr16}, Handle, table::{SystemTable, Boot}, Status, Char16, proto::{loaded_image::LoadedImage, media::{file::{File, FileHandle, FileMode, FileAttribute, FileInfo}, fs::SimpleFileSystem}}, CStr16, data_types::Align};
fn load_file(path: &CStr16, boot_services: &BootServices) -> FileHandle {
let loaded_image = boot_services.open_protocol_exclusive::<LoadedImage>(boot_services.image_handle()).unwrap();
let mut file_system = boot_services.open_protocol_exclusive::<SimpleFileSystem>(loaded_image.device()).unwrap();
let mut directory = file_system.open_volume().unwrap();
directory.open(path, FileMode::Read, FileAttribute::READ_ONLY).unwrap()
}
#[entry]
fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
uefi_services::init(&mut system_table).unwrap();
info!("Loading kernel...");
let mut kernel = load_file(cstr16!("kernel.elf"), system_table.boot_services()).into_regular_file().unwrap();
let mut small_buffer = vec![0u8; 0];
let size = kernel.get_info::<FileInfo>(&mut small_buffer).err().unwrap().data().unwrap();
let mut file_info = vec![0u8; size];
kernel.get_info::<FileInfo>(&mut file_info);
let info: FileInfo; //this is what I need
let elf_buffer = vec![0u8; info.file_size().try_into().unwrap()];
let elf = Elf::from_bytes(&mut elf_buffer).expect("Kernel loading failed!");
info!("{:?} header: {:?}", elf, elf.elf_header());
for p in elf.program_header_iter() {
info!("{:x?}", p);
}
for s in elf.section_header_iter() {
info!("{:x?}", s);
}
let s = elf.lookup_section(b".text");
info!("s {:?}", s);
system_table.boot_services().stall(100_000_000);
Status::SUCCESS
}
有几种方法可以做到这一点。哪一种适合您取决于您需要对文件读取和读入内存进行多少控制。
uefi::fs::FileSystem
。它提供了一个 metadata
方法来获取文件信息,其中包括文件大小。更好的是,如果您只想要大小以便将文件读入 vec,您可以使用 FileSystem::read
来获取 Vec<u8>
。请注意,uefi::fs
需要启用 alloc
箱的 uefi
功能。
File::get_info
、File::get_info_boxed
或文件查找来获取大小。以下是如何操作的示例:
#![no_main]
#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use uefi::prelude::*;
use uefi::proto::media::file::{File, FileAttribute, FileInfo, FileMode, RegularFile};
use uefi::proto::media::fs::SimpleFileSystem;
use uefi::table::boot::ScopedProtocol;
use uefi::{CStr16, Result};
use uefi_services::println;
/// Read via the `uefi::fs` module. Note that this requires the `alloc`
/// feature of the `uefi` crate to be enabled.
fn read_via_fs_module(path: &CStr16, simple_file_system: ScopedProtocol<SimpleFileSystem>) {
let mut fs = uefi::fs::FileSystem::new(simple_file_system);
let size = fs
.metadata(path)
.expect("failed to get metadata")
.file_size();
let vec = fs.read(path).expect("read failed");
assert_eq!(vec.len(), usize::try_from(size).unwrap());
println!(
"successfully read {} into a Vec of length {}",
path,
vec.len()
);
}
/// Read via the `SimpleFileSystem` protocol directly.
fn read_via_sfs(
path: &CStr16,
simple_file_system: &mut ScopedProtocol<SimpleFileSystem>,
) -> Result {
let mut root = simple_file_system.open_volume()?;
let mut file = root.open(
path,
FileMode::Read,
// File attributes only matter when creating a file, so
// use `empty`.
FileAttribute::empty(),
)?;
// There are a few ways to get the length:
// Option 1: `get_boxed_info`. This requires the `alloc` feature of
// the `uefi` crate to be enabled.
let size1 = file.get_boxed_info::<FileInfo>()?.file_size();
// Option 2: `get_info` with a stack buffer that is sufficiently
// large to hold the output. This call would fail if the buffer was
// too small.
let mut buf = [0; 128];
let size2 = file.get_info::<FileInfo>(&mut buf).unwrap().file_size();
// Option 3: `get_info` with a buffer allocated with the correct
// size. We'll pass in an empty buffer initially to get the
// size. Since the buffer is too small, we'll get an error -- and
// that error will contain a `usize` with the required buffer size.
let mut buf = Vec::new();
let info_size = file
.get_info::<FileInfo>(&mut buf)
.unwrap_err()
.data()
.unwrap();
buf.resize(info_size, 0);
let size3 = file.get_info::<FileInfo>(&mut buf).unwrap().file_size();
// Option 4: rather than using `FileInfo`, seek to the end of the
// buffer. First we need to convert the file into a `RegularFile`
// (the other type of file is a directory). We'd do that to read the
// file anyway.
let mut file = file.into_regular_file().unwrap();
file.set_position(RegularFile::END_OF_FILE)?;
let size4 = file.get_position()?;
// Verify that all four methods of getting the size gave the same
// value.
assert_eq!(size1, size2);
assert_eq!(size1, size3);
assert_eq!(size1, size4);
// Now we can create a buffer and read into it.
let mut buf = vec![0; usize::try_from(size4).unwrap()];
// Earlier we seeked to the end of the file. Seek back to the
// beginning so we read the whole thing.
file.set_position(0)?;
file.read(&mut buf)?;
println!("successfully read {} bytes from {}", size1, path);
Ok(())
}
#[entry]
fn main(image: Handle, mut st: SystemTable<Boot>) -> Status {
uefi_services::init(&mut st).unwrap();
// Get the `SimpleFileSystem` protocol for the file system that the
// current executable was loaded from, probably the ESP (EFI System
// Partition).
let mut simple_file_system = st.boot_services().get_image_file_system(image).unwrap();
let path = cstr16!("kernel.elf");
read_via_sfs(path, &mut simple_file_system).unwrap();
read_via_fs_module(path, simple_file_system);
Status::SUCCESS
}