在上一篇文章中我们介绍了rust在uefi中的基本使用,在本章中我们开始编写UEFI基础设施,主要包括File
结构和uefi编译脚本用来简化内核文件读取和qemu启动过程
建立基础异常
在标准库中为我们提供了Result
,但是在UEFI开发中将Result分成了2种,UEFI服务执行的Result和用户自定义的Result,在本节中我们仿照标准库的io::Result
建立我们自己的Result
设计思路
我们设计的Result
除了满足我们程序基本的使用以外还要兼容UEFI服务的Result
因此我们可以通过type
来重新命令2种Result
,其次我们要兼容UEFI服务的错误,但是还是要区分UEFI异常和UEFI应用异常,因此我们需要设计一个枚举Repr
来区分两种异常
根据Result
我们可以轻松定义出如下结构
// 表示UEFI服务使用的Result
pub type UefiResult<T> = uefi::Result<T>;
// 表示UEFI应用程序使用的Result
pub type Result<T> = core::result::Result<T, Error>;
第二个Result
的Error
为我们自定义的Error
,Error
分为2种UEFI执行中的错误以及应用程序自定义的错误,因此我们可以使用一个枚举来完成
// 用于表示异常的种类,分为UEFI执行错误和UEFI应用程序错误
#[derive(Debug)]
enum Repr {
/// UEFI服务错误
Uefi(Efi),
/// UEFI应用错误
Custom(Custom),
}
在Repr
中包含2个结构体Efi
和Custom
结构定义如下
#[derive(Debug, Clone)]
struct Custom {
/// UEFI应用执行错误类型
kind: ErrorKind,
/// UEFI应用执行错误说明
err_msg: String,
}
#[derive(Debug, Clone)]
struct Efi {
/// UEFI服务执行结果状态码
status: Status,
/// UEFI状态对应说明,如果为None使用默认的提示信息,
err_msg: Option<String>,
}
// 用于表示UEFI应用异常类型
#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
pub enum ErrorKind {
/// 文件未找到
NotFound,
/// 在读取文件中读取到了EOF
UnexpectedEof,
/// 无效的文件
InvalidFile,
/// 无效的文件模式
InvalidFileMode,
/// UEFI错误,包含错误码
UefiErrorCode,
/// 终止
Interrupted,
}
Custom
和Efi
主要的不同在于kind
为我们自定义的错误类型,Status
表示UEFI服务执行错误的状态码(Status其实是Rust与C的枚举的映射,但是uefi-rs中不是简单地与C枚举映射因为会产生UB
(undefined behaviour),而是采取了newtype_enum
宏进行处理)
紧接着我们为2种结构提供as_str()
方法,ErrorKind
的实现比较简单,以下的部分代码
impl ErrorKind {
pub fn as_str(&self) -> &'static str {
match *self {
ErrorKind::NotFound => "entity not found",
ErrorKind::UnexpectedEof => "unexpected end of file",
....
}
}
}
但是实现Efi
结构的as_str
是有些不同了,我们不能写出这样的代码
impl Efi {
pub fn as_str(&self) -> &'static str {
match self.status {
Status:: WARN_UNKNOWN_GLYPH => "The string contained characters that could not be rendered and were skipped."
.....
}
}
}
在上面我们也提到了,如果简单的看源码Status确实像枚举,但是其实是结构体,因此我们要判断为Status中具体的值
以
Status:: WARN_UNKNOWN_GLYPH
为例,经过宏扩展后结果为pub struct WARN_UNKNOWN_GLYPH(pub usize)
,因此我们要以结构体的思路来处理
考虑到完整性列出了所有Status的结果
const ERROR_BIT: usize = 1 << (core::mem::size_of::<usize>() * 8 - 1);
impl Efi {
pub fn as_str(&self) -> &'static str {
match self.status.0 {
0 => "The operation completed successfully.",
1 => "The string contained characters that could not be rendered and were skipped.",
2 => "The handle was closed, but the file was not deleted.",
3 => "The handle was closed, but the data to the file was not flushed properly.",
4 => "The resulting buffer was too small, and the data was truncated.",
5 => "The data has not been updated within the timeframe set by local policy.",
6 => "The resulting buffer contains UEFI-compliant file system.",
7 => "The operation will be processed across a system reset.",
ERROR_BIT | 1 => "The image failed to load.",
ERROR_BIT | 2 => "A parameter was incorrect.",
ERROR_BIT | 3 => "The operation is not supported.",
ERROR_BIT | 4 => "The buffer was not the proper size for the request.The buffer is not large enough to hold the requested data.",
ERROR_BIT | 5 => "The required buffer size is returned in the appropriate parameter.",
ERROR_BIT | 6 => "There is no data pending upon return.",
ERROR_BIT | 7 => "The physical device reported an error while attempting the operation.",
ERROR_BIT | 8 => "The device cannot be written to.",
ERROR_BIT | 9 => "A resource has run out.",
ERROR_BIT | 10 => "An inconstency was detected on the file system.",
ERROR_BIT | 11 => "There is no more space on the file system.",
ERROR_BIT | 12 => "The device does not contain any medium to perform the operation.",
ERROR_BIT | 13 => "The medium in the device has changed since the last access.",
ERROR_BIT | 14 => "The item was not found.",
ERROR_BIT | 15 => "Access was denied.",
ERROR_BIT | 16 => "The server was not found or did not respond to the request.",
ERROR_BIT | 17 => "A mapping to a device does not exist.",
ERROR_BIT | 18 => "The timeout time expired.",
ERROR_BIT | 19 => "The protocol has not been started.",
ERROR_BIT | 20 => "The protocol has already been started.",
ERROR_BIT | 21 => "The operation was aborted.",
ERROR_BIT | 22 => "An ICMP error occurred during the network operation.",
ERROR_BIT | 23 => "A TFTP error occurred during the network operation.",
ERROR_BIT | 24 => "A protocol error occurred during the network opera