使用Rust开发操作系统(UEFI内存管理和文件系统使用)

本文介绍了使用Rust开发UEFI操作系统的内存管理和文件系统使用。主要内容包括:改造Result结构以适应UEFI服务,讲解了AllocatePool和AllocatePages的内存分配方法,以及如何使用SimpleFileSystem Protocol读取文件。通过示例展示了遍历文件夹和读取文件内容的方法,为后续的OSLoader开发打下基础。
摘要由CSDN通过智能技术生成

在上一篇文章中我们简单介绍了UEFI的基本概念在本章中我们介绍uefi-rs库的内存管理和文件系统使用

基本结构

uefi-rs中基本的结构已经画成脑图的形式
在这里插入图片描述
uefi-rs中主要分为以下内容

  • 信息类: 固件的信息,UEFI信息,uefi配置表
  • 服务类: 在uefi-rs中主要包含运行时服务,启动服务,退出启动服务等
  • Protocol(协议): 在uefi-rs中支持以下协议,所有的Protocol需要使用BootServer.local_protocol::<ProtocolName>();来获取(ProtocolName表示Protocol名称,例如BootServer.local_protocol::<Input>())(以上都是伪代码)
    • 标准输入Input
    • 标准输出Output
    • 图形输出协议GOP(Grahpics Output Protocol)
    • 串行IO设备访问Serial
    • 调试服务DebugSupport
    • 镜像加载LoadImage
    • 文件系统SimpleFileSystem
    • 多处理器服务MPService
  • 其他服务: 内存分配等

UEFI的HelloWorld!

我们创建好项目后再Cargo.toml中添加如下内容

[dependencies.uefi]
version="0.4.0"
features = ['exts']

[dependencies.uefi-services]
version = "0.2.0"
features = ["qemu"]

[dependencies.log]
version = "0.4.8"

然后我们在main.rs中添加几个feature

// main.rs
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(abi_efiapi)] // uefi-rs使用的是efi调用约定
#![feature(never_type)] 
extern crate uefi;

然后定义UEFI的入口函数

#[entry]
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status{}

其中#[entry]uefi-marcos中定义主要功能是将我们定义的函数转化为pub extern "efiapi" fn eif_main这样的形式
程序进入main函数后的第一件事情就是uefi服务进入DXE(Driver Execution Environment)阶段

#[entry]
fn efi_main(image: Handle, st: SystemTable<Boot>) -> Status{
	if let Err(e) = uefi_services::init(&st).log_warning(){
        info!("{:?}",e);
        // 如果服务初始化失败后则不能继续执行
        return e.status();
    }
    info!("Hello World!");
    Status::SUCCESS
}

我们编写完毕代码后再项目的根目录(与src目录同级)创建.cargo文件夹,随后创建config文件并填写一下内容(rust已经支持了x86_64-unknown-uefi编译目标)

# build settings
[build]
target = "x86_64-unknown-uefi"

这样我们在运行时不需要指定--target参数,否则每次运行时需要添加,例如

cargo xbuild --target x86_64-unknown-uefi

Cargo代理
如果连接到cargo.io比较慢可以添加国内代理

//in .cargo/config
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'

[source.ustc]
registry = "http://mirrors.ustc.edu.cn/crates.io-index"

这样当我们运行cargo xbuild后会在target/debug目录中找到xxx.efi文件(xxx表示起的项目名称)

使用QEMU启动

当编译完毕后我们需要创建一个目录esp/EFI/Boot然后将我们编译的xxx.efi命名成BootX64.efi
目录结构如下

project
	├── .cargo
	├── src
	├── target
	├── Cargo.toml
	├── Cargo.lock
	└──esp
		└── EFI
			 └── Boot
			      └──BootX64.efi

esp目录是我们需要挂载的文件目录

因为我们使用的是虚拟机,我们需要使用OVMF(开放虚拟机固件Open Virtual Machine Firmware)OVMF的制作请参考使用Rust开发操作系统(UEFI基本介绍)的OVMF制作章节,然后为QEMU指定OVMF_CODE.fd路径,OVMF_VARS.fd的路径,以及我们创建的esp文件夹路径
例如(为了方便阅读对命令进行了换行)

qemu-system-x86_64
-drive if=pflash,format=raw,file=<OVMF_CODE.fd的路径>,readonly=on
-drive if=pflash,format=raw,file=<OVMF_VARS.fd的路径>,readonly=on
-drive format=raw,file=fat:rw:<esp文件路径>

以下是参考命令(windows)

qemu-system-x86_64 
-drive if=pflash,format=raw,file=C:\Users\VenmoSnake\Desktop\ReboxOS\OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,file=C:\Users\VenmoSnake\Desktop\OS\OVMF_VARS.fd,readonly=on
-drive format=raw,file=fat:rw:C:\Users\VenmoSnake\Desktop\OS\esp

这样我们可以在QEMU上运行我们编写的efi

基本的数据结构

Result

pub type Result<Output = (), ErrData = ()> = core::result::Result<Completion<Output>, Error<ErrData>>;

uefi-rs中Result的定义有些许不同而且使用方式跟通常用法也不同,因此我们要具体说明一下

我们可以看到一个名叫Completion的数据结构其定义如下

#[must_use]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Completion<T> {
    status: Status,
    result: T,
}

Completion用于表示UEFI服务操作完成后的结果,但在此过程中可能会遇到一些非致命问题,其中的Status表示执行的结果状态,定义如下

newtype_enum! {
	#[must_use]
	pub enum Status: usize => {
	    /// 操作执行成果
	    SUCCESS                 =  0,
	
	    /// 该字符串包含无法呈显示的字符,并被跳过。 UEFI使用的是UCS-2编码
	    WARN_UNKNOWN_GLYPH      =  1,
	    ///  handle已关闭,但未删除文件。
	    WARN_DELETE_FAILURE     =  2,
	    ///  handle已关闭,但文件内容没有正确刷新。
	    WARN_WRITE_FAILURE      =  3,
	    /// 指定的缓冲区太小,数据被截断。
	    WARN_BUFFER_TOO_SMALL   =  4,
	    /// The data has not been updated within the timeframe set by local policy.
	    /// 数据尚没有在指定的时间范围内更新。(时间范围在本地策略中设置)
	    WARN_STALE_DATA         =  5,
	    /// 结果缓冲区包含符合UEFI的文件系统。
	    WARN_FILE_SYSTEM        =  6,
	    /// 系统重置后将处理该操作。
	    WARN_RESET_REQUIRED     =  7,
	
	    /// 镜像读取失败
	    LOAD_ERROR              = ERROR_BIT |  1,
	    /// 参数不正确
	    INVALID_PARAMETER       = ERROR_BIT |  2,
	    /// 不支持的操作
	    UNSUPPORTED             = ERROR_BIT |  3,
	    /// 缓冲区的大小不符合要求。
	    BAD_BUFFER_SIZE         = ERROR_BIT |  4,
	    /// 缓冲区的大小不足以容纳请求的数据。或者表示在参数中应该返回所需的缓冲区大小。
	    BUFFER_TOO_SMALL        = ERROR_BIT |  5,
	    /// 没有数据返回。
	    NOT_READY               = ERROR_BIT |  6,
	    /// 物理设备在尝试操作时
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值