More detailed error reporting

This commit is contained in:
Mark 2020-10-16 23:15:10 +03:00
parent f362616fdc
commit 448e9c7abe
5 changed files with 303 additions and 168 deletions

View File

@ -1,6 +1,7 @@
use efi::{File, Status, CStr16, MemoryMap};
use core::mem::{MaybeUninit, size_of};
use yboot2_proto::{Magic, LoadProtocol};
use crate::error::{ImageLoadError, ProtocolError};
use core::mem::{size_of, MaybeUninit};
use efi::{CStr16, File, MemoryMap, Status};
use yboot2_proto::{LoadProtocol, Magic};
type Off = u64;
type Addr = u64;
@ -17,136 +18,148 @@ const SHF_ALLOC: XWord = 1 << 1;
#[repr(C)]
struct Ehdr {
ident: [u8; 16],
_type: Half,
machine: Half,
version: Word,
entry: Addr,
phoff: Off,
shoff: Off,
flags: Word,
ehsize: Half,
phentsize: Half,
phnum: Half,
shentsize: Half,
shnum: Half,
shstrndx: Half
ident: [u8; 16],
_type: Half,
machine: Half,
version: Word,
entry: Addr,
phoff: Off,
shoff: Off,
flags: Word,
ehsize: Half,
phentsize: Half,
phnum: Half,
shentsize: Half,
shnum: Half,
shstrndx: Half,
}
#[repr(C)]
struct Shdr {
name: Word,
_type: Word,
flags: XWord,
addr: Addr,
offset: Off,
size: XWord,
link: Word,
info: Word,
addralign: XWord,
entsize: XWord
name: Word,
_type: Word,
flags: XWord,
addr: Addr,
offset: Off,
size: XWord,
link: Word,
info: Word,
addralign: XWord,
entsize: XWord,
}
#[repr(C)]
struct Phdr {
_type: Word,
flags: Word,
offset: Off,
vaddr: Addr,
paddr: Addr,
filesz: XWord,
memsz: XWord,
align: XWord
_type: Word,
flags: Word,
offset: Off,
vaddr: Addr,
paddr: Addr,
filesz: XWord,
memsz: XWord,
align: XWord,
}
pub struct Object {
file: File,
ehdr: Ehdr,
file: File,
ehdr: Ehdr,
pub start: usize,
pub end: usize
pub start: usize,
pub end: usize,
}
unsafe fn any_as_u8_slice<T: Sized>(p: &mut T) -> &mut [u8] {
::core::slice::from_raw_parts_mut(
(p as *mut T) as *mut u8,
::core::mem::size_of::<T>(),
)
::core::slice::from_raw_parts_mut((p as *mut T) as *mut u8, ::core::mem::size_of::<T>())
}
impl Object {
// Reason: EFI autism
pub fn open(root: &mut File, path: &CStr16) -> Result<Object, Status> {
pub fn open(root: &mut File, path: &CStr16) -> Result<Object, ImageLoadError> {
let mut obj = Object {
file: root.open(path, efi::proto::fp::OPEN_MODE_READ, 0)?,
file: root
.open(path, efi::proto::fp::OPEN_MODE_READ, 0)
.map_err(ImageLoadError::IOError)?,
ehdr: unsafe { MaybeUninit::uninit().assume_init() },
start: 0xFFFFFFFFFFFFFFFF,
end: 0,
};
// Load header
obj.file.seek(0)?;
obj.file.read(unsafe {any_as_u8_slice(&mut obj.ehdr)})?;
obj.file.seek(0).map_err(ImageLoadError::IOError)?;
obj.file
.read(unsafe { any_as_u8_slice(&mut obj.ehdr) })
.map_err(ImageLoadError::IOError)?;
// Validate that this is ELF
if &obj.ehdr.ident[0 .. 4] != [0x7F, b'E', b'L', b'F'] {
return Err(Status::InvalidParameter);
if &obj.ehdr.ident[0..4] != [0x7F, b'E', b'L', b'F'] {
return Err(ImageLoadError::BadMagic);
}
// Validate that bitness matches requested
if obj.ehdr.ident[4] != 2 {
return Err(Status::InvalidParameter);
return Err(ImageLoadError::BadTarget);
}
Ok(obj)
}
fn read_phdr(&mut self, phdr: &mut Phdr, index: usize) -> Result<(), Status> {
fn read_phdr(&mut self, phdr: &mut Phdr, index: usize) -> Result<(), ImageLoadError> {
let off = self.ehdr.phoff + self.ehdr.phentsize as u64 * index as u64;
self.file.seek(off)?;
if self.file.read(unsafe { any_as_u8_slice(phdr) })? != size_of::<Phdr>() {
Err(Status::Err)
self.file.seek(off).map_err(ImageLoadError::IOError)?;
if self
.file
.read(unsafe { any_as_u8_slice(phdr) })
.map_err(ImageLoadError::IOError)?
!= size_of::<Phdr>()
{
Err(ImageLoadError::IOError(efi::Status::Err))
} else {
Ok(())
}
}
fn read_shdr(&mut self, shdr: &mut Shdr, index: usize) -> Result<(), Status> {
fn read_shdr(&mut self, shdr: &mut Shdr, index: usize) -> Result<(), ImageLoadError> {
let off = self.ehdr.shoff + self.ehdr.shentsize as u64 * index as u64;
self.file.seek(off)?;
if self.file.read(unsafe { any_as_u8_slice(shdr) })? != size_of::<Shdr>() {
Err(Status::Err)
self.file.seek(off).map_err(ImageLoadError::IOError)?;
if self
.file
.read(unsafe { any_as_u8_slice(shdr) })
.map_err(ImageLoadError::IOError)?
!= size_of::<Shdr>()
{
Err(ImageLoadError::IOError(efi::Status::Err))
} else {
Ok(())
}
}
// Called after load()
pub fn locate_protocol_data<T: Magic + LoadProtocol>(&mut self)
-> Result<&'static mut T, Status>
{
pub fn locate_protocol_data<T: Magic + LoadProtocol>(
&mut self,
) -> Result<&'static mut T, ImageLoadError> {
let mut shdr = unsafe { MaybeUninit::<Shdr>::uninit().assume_init() };
for i in 0 .. self.ehdr.shnum {
for i in 0..self.ehdr.shnum {
self.read_shdr(&mut shdr, i as usize)?;
if shdr._type == SHT_PROGBITS &&
(shdr.flags & (SHF_ALLOC | SHF_WRITE)) == SHF_ALLOC | SHF_WRITE {
if shdr._type == SHT_PROGBITS
&& (shdr.flags & (SHF_ALLOC | SHF_WRITE)) == SHF_ALLOC | SHF_WRITE
{
if shdr.size as usize >= size_of::<T>() {
// Make a physical address
let ptr = shdr.addr - 0xFFFFFF0000000000;
let magic: &[u8] = unsafe { core::slice::from_raw_parts(ptr as *const _, 8)};
let magic: &[u8] = unsafe { core::slice::from_raw_parts(ptr as *const _, 8) };
if magic == T::KERNEL_MAGIC {
return Ok(unsafe { &mut *(ptr as *mut _) })
return Ok(unsafe { &mut *(ptr as *mut _) });
}
}
}
}
Err(Status::InvalidParameter)
Err(ImageLoadError::NoProtocol)
}
pub fn load(&mut self, mmap: &MemoryMap) -> Result<usize, Status> {
extern {
pub fn load(&mut self, mmap: &MemoryMap) -> Result<usize, ImageLoadError> {
extern "C" {
fn memset(block: *mut u8, value: i32, count: usize) -> *mut u8;
}
@ -154,10 +167,18 @@ impl Object {
// 1. Check that all pages in load segments are usable
// Also find out kernel's lowest and highest physical addresses
for i in 0 .. self.ehdr.phnum {
for i in 0..self.ehdr.phnum {
self.read_phdr(&mut phdr, i as usize)?;
if phdr._type == PT_LOAD {
if phdr.paddr + phdr.memsz >= 0x100000000 {
return Err(ImageLoadError::BadAddress(
phdr.paddr + phdr.memsz,
0,
0x100000000,
));
}
let start = phdr.paddr & !0xFFF;
let end = (phdr.paddr + phdr.memsz as u64 + 0xFFF) & !0xFFF;
@ -168,36 +189,37 @@ impl Object {
self.end = end as usize;
}
for addr in (start .. end).step_by(0x1000) {
for addr in (start..end).step_by(0x1000) {
if !mmap.is_usable_now(addr as usize) {
return Err(Status::InvalidParameter);
return Err(ImageLoadError::BadSegment(start, end, addr));
}
}
}
}
// 2. Load segments
for i in 0 .. self.ehdr.phnum {
for i in 0..self.ehdr.phnum {
self.read_phdr(&mut phdr, i as usize)?;
if phdr._type == PT_LOAD {
// Load what's provided in ELF
if phdr.filesz > 0 {
let mut data = unsafe {core::slice::from_raw_parts_mut(
phdr.paddr as *mut u8,
phdr.filesz as usize,
)};
let mut data = unsafe {
core::slice::from_raw_parts_mut(phdr.paddr as *mut u8, phdr.filesz as usize)
};
self.file.seek(phdr.offset)?;
self.file.read(&mut data)?;
self.file.seek(phdr.offset).map_err(ImageLoadError::IOError)?;
self.file.read(&mut data).map_err(ImageLoadError::IOError)?;
}
// Zero the rest
if phdr.memsz > phdr.filesz {
unsafe {
memset((phdr.paddr as usize + phdr.filesz as usize) as *mut u8,
0,
(phdr.memsz - phdr.filesz) as usize);
memset(
(phdr.paddr as usize + phdr.filesz as usize) as *mut u8,
0,
(phdr.memsz - phdr.filesz) as usize,
);
}
}
}

104
src/error.rs Normal file
View File

@ -0,0 +1,104 @@
use core::fmt;
use efi;
#[derive(Debug)]
pub enum BootError {
ImageLoadError(ImageLoadError),
InitrdLoadError(InitrdLoadError),
ProtocolError(ProtocolError),
MemoryMapError(efi::Status),
FileError(efi::Status),
TerminateServicesError(efi::Status),
VideoModeUnsupported,
VideoModeFailed,
}
#[derive(Debug)]
pub enum ImageLoadError {
BadAddress(u64, u64, u64),
BadSegment(u64, u64, u64),
IOError(efi::Status),
NoProtocol,
BadMagic,
BadTarget,
}
#[derive(Debug)]
pub enum InitrdLoadError {
IOError(efi::Status),
NoSpace,
}
#[derive(Debug)]
pub enum ProtocolError {}
impl From<ProtocolError> for BootError {
fn from(p: ProtocolError) -> Self {
BootError::ProtocolError(p)
}
}
impl From<InitrdLoadError> for BootError {
fn from(p: InitrdLoadError) -> Self {
BootError::InitrdLoadError(p)
}
}
impl From<ImageLoadError> for BootError {
fn from(p: ImageLoadError) -> Self {
BootError::ImageLoadError(p)
}
}
impl From<&BootError> for efi::Status {
fn from(f: &BootError) -> Self {
todo!()
}
}
impl fmt::Display for BootError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use BootError::*;
match self {
ImageLoadError(e) => e.fmt(f),
InitrdLoadError(e) => e.fmt(f),
_ => {
write!(f, "Unknown error: {:?}", self)?;
Ok(())
}
}
}
}
impl fmt::Display for InitrdLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InitrdLoadError::*;
match self {
IOError(e) => write!(f, "I/O or file error (initrd): {:?}", e),
NoSpace => write!(f, "Failed to fit initrd in memory"),
}
}
}
impl fmt::Display for ImageLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ImageLoadError::*;
match self {
BadAddress(addr, start, end) => write!(
f,
"Invalid image address: 0x{:016x}. Expected in range 0x{:016x} .. 0x{:016x}",
addr, start, end
),
BadSegment(page, start, end) => write!(
f,
"Invalid segment range: 0x{:016x} .. 0x{:016x}. Page 0x{:016x} can't be used.",
start, end, page
),
IOError(e) => write!(f, "I/O or file error (image): {:?}", e),
NoProtocol => write!(f, "The image doesn't have a protocol structure"),
BadTarget => write!(f, "The image targets a different arch"),
BadMagic => write!(f, "Bad image magic"),
}
}
}

View File

@ -1,9 +1,10 @@
use efi::{File, CStr16, Status};
use core::mem::MaybeUninit;
use crate::elf;
use crate::error::InitrdLoadError;
use core::mem::MaybeUninit;
use efi::{CStr16, File, Status};
fn check_placement(mmap: &efi::MemoryMap, base: usize, size: usize) -> bool {
for page in (base & !0xFFF .. (base + size + 0xFFF) & !0xFFF).step_by(0x1000) {
for page in (base & !0xFFF..(base + size + 0xFFF) & !0xFFF).step_by(0x1000) {
if !mmap.is_usable_now(page) {
return false;
}
@ -11,20 +12,23 @@ fn check_placement(mmap: &efi::MemoryMap, base: usize, size: usize) -> bool {
true
}
fn do_load(file: &mut File, base: usize, size: usize) -> efi::Result<()> {
file.read(unsafe {core::slice::from_raw_parts_mut(base as *mut u8, size)})?;
fn do_load(file: &mut File, base: usize, size: usize) -> Result<(), InitrdLoadError> {
file.read(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, size) })
.map_err(InitrdLoadError::IOError)?;
Ok(())
}
pub fn load_somewhere(root: &mut File,
filename: &CStr16,
mmap: &efi::MemoryMap,
obj: &elf::Object) -> efi::Result<(usize, usize)> {
pub fn load_somewhere(
root: &mut File,
filename: &CStr16,
mmap: &efi::MemoryMap,
obj: &elf::Object,
) -> Result<(usize, usize), InitrdLoadError> {
let mut statbuf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() };
let mut file = root.open(filename,
efi::proto::fp::OPEN_MODE_READ,
0)?;
let stat = file.stat(&mut statbuf)?;
let mut file = root
.open(filename, efi::proto::fp::OPEN_MODE_READ, 0)
.map_err(InitrdLoadError::IOError)?;
let stat = file.stat(&mut statbuf).map_err(InitrdLoadError::IOError)?;
let size = stat.file_size as usize;
// 1. Try loading right below the kernel
@ -39,7 +43,7 @@ pub fn load_somewhere(root: &mut File,
}
// 2. Any location above the kernel
for start in ((obj.end + 0x3FFF) & !0xFFF .. 0x100000000).step_by(0x1000) {
for start in ((obj.end + 0x3FFF) & !0xFFF..0x100000000).step_by(0x1000) {
if check_placement(mmap, start, size) {
println!("Loading initrd at 0x{:016x}", start);
do_load(&mut file, start, size)?;
@ -47,6 +51,5 @@ pub fn load_somewhere(root: &mut File,
}
}
Err(Status::InvalidParameter)
Err(InitrdLoadError::NoSpace)
}

View File

@ -1,61 +1,61 @@
#![feature(asm,const_fn,llvm_asm)]
#![feature(asm, const_fn, llvm_asm)]
#![no_main]
#![no_std]
extern crate efi;
extern crate core_rt;
extern crate char16_literal;
extern crate core_rt;
extern crate efi;
extern crate yboot2_proto;
pub (crate) use char16_literal::cstr16;
pub(crate) use char16_literal::cstr16;
use efi::{
CStr16,
Status,
ConfigurationTableEntry,
ImageHandle,
SystemTable,
system_table,
image_handle,
};
use yboot2_proto::{LoadProtocol, ProtoV1, MemoryMapInfo};
use core::convert::TryInto;
use efi::{
image_handle, system_table, CStr16, ConfigurationTableEntry, ImageHandle, Status, SystemTable,
};
use yboot2_proto::{LoadProtocol, MemoryMapInfo, ProtoV1};
#[macro_use]
mod println;
mod initrd;
mod video;
mod mem;
mod elf;
mod error;
mod initrd;
mod mem;
mod video;
fn set_efi_mmap<T: LoadProtocol>(data: &mut T, mmap: &efi::MemoryMap) -> efi::Result<()> {
use error::BootError;
fn set_efi_mmap<T: LoadProtocol>(data: &mut T, mmap: &efi::MemoryMap) -> Result<(), BootError> {
match data.set_mmap(&MemoryMapInfo {
address: mmap.storage_ref.as_ptr() as u64,
entsize: mmap.descriptor_size.try_into().unwrap(),
size: mmap.size.try_into().unwrap()
address: mmap.storage_ref.as_ptr() as u64,
entsize: mmap.descriptor_size.try_into().unwrap(),
size: mmap.size.try_into().unwrap(),
}) {
Err(_) => Err(Status::Err),
Ok(()) => Ok(())
Err(_) => Err(BootError::MemoryMapError(efi::Status::Err)),
Ok(()) => Ok(()),
}
}
fn main() -> efi::Result<()> {
fn main() -> Result<(), BootError> {
let mut desc_array = [0u8; 16384];
let mut mmap = efi::MemoryMap::new(&mut desc_array);
let bs = &system_table().boot_services;
println!("Getting memory map");
bs.get_memory_map(&mut mmap)?;
bs.get_memory_map(&mut mmap)
.map_err(BootError::MemoryMapError)?;
println!("Getting RSDP");
let rsdp = system_table()
.config_iter()
.find(|x| matches!(x, ConfigurationTableEntry::Acpi10Table(_)))
.map(|x| match x {
ConfigurationTableEntry::Acpi10Table(ptr) => ptr,
_ => panic!()
ConfigurationTableEntry::Acpi10Table(ptr) => ptr,
_ => panic!(),
});
let mut root = image_handle().get_boot_path()?.open_partition()?;
let mut root = image_handle()
.get_boot_path()
.map_err(BootError::FileError)?
.open_partition()
.map_err(BootError::FileError)?;
// Load kernel
let mut obj = elf::Object::open(&mut root, CStr16::from_literal(cstr16!(r"\kernel.elf")))?;
@ -64,10 +64,12 @@ fn main() -> efi::Result<()> {
if (data.get_flags() & yboot2_proto::FLAG_INITRD) != 0 {
// Load initrd
let (initrd_base, initrd_size) = initrd::load_somewhere(&mut root,
let (initrd_base, initrd_size) = initrd::load_somewhere(
&mut root,
CStr16::from_literal(cstr16!(r"\initrd.img")),
&mmap,
&obj)?;
&obj,
)?;
// Set video mode
data.set_initrd(initrd_base, initrd_size);
@ -81,23 +83,17 @@ fn main() -> efi::Result<()> {
video::set_mode(bs, data)?;
// Get the new memory map and terminate boot services
bs.get_memory_map(&mut mmap)?;
bs.exit_boot_services(mmap.key)?;
bs.get_memory_map(&mut mmap).map_err(BootError::MemoryMapError)?;
bs.exit_boot_services(mmap.key).map_err(BootError::TerminateServicesError)?;
set_efi_mmap(data, &mmap)?;
// Setup upper virtual mapping if requested
if (data.get_flags() & yboot2_proto::FLAG_UPPER) != 0 {
mem::setup_upper();
unsafe {
llvm_asm!("xor %rbp, %rbp; jmp *$0"::"{di}"(entry));
}
} else {
unsafe {
let entry_fn: unsafe fn () -> ! = core::mem::transmute(entry);
entry_fn();
}
}
unsafe {
llvm_asm!("xor %rbp, %rbp; jmp *$0"::"{di}"(entry));
}
loop {}
}
@ -105,9 +101,15 @@ fn main() -> efi::Result<()> {
extern "C" fn efi_main(ih: *mut ImageHandle, st: *mut SystemTable) -> u64 {
efi::init(ih, st);
let res = &main();
println!("result -> {:?}", res);
// Don't return immediately on failure
if let Err(err) = res {
let bs = &system_table().boot_services;
println!("yboot2 error: {}", err);
// Delay for 5s so error message can be read
bs.stall(5000000);
}
efi::Termination::to_efi(res)
efi::Termination::to_efi(&res.as_ref().map_err(efi::Status::from))
}
use core::panic::PanicInfo;

View File

@ -1,5 +1,6 @@
use yboot2_proto::{LoadProtocol, VideoInfo, video::PixelFormat};
use efi::{BootServices, Status, GraphicsOutputProtocol, gop::ModeInformation};
use crate::error::BootError;
use efi::{gop::ModeInformation, BootServices, GraphicsOutputProtocol, Status};
use yboot2_proto::{video::PixelFormat, LoadProtocol, VideoInfo};
// TODO: "Any" format
// TODO: "Text" format
@ -7,54 +8,57 @@ use efi::{BootServices, Status, GraphicsOutputProtocol, gop::ModeInformation};
fn pixel_to_efi(from: PixelFormat) -> Option<efi::gop::PixelFormat> {
use efi::gop::PixelFormat::*;
match from {
PixelFormat::LfbRgb32 => Some(PixelRedGreenBlueReserved8BitPerColor),
PixelFormat::LfbBgr32 => Some(PixelBlueGreenRedReserved8BitPerColor),
_ => None
PixelFormat::LfbRgb32 => Some(PixelRedGreenBlueReserved8BitPerColor),
PixelFormat::LfbBgr32 => Some(PixelBlueGreenRedReserved8BitPerColor),
_ => None,
}
}
fn pixel_from_efi(from: efi::gop::PixelFormat) -> Option<PixelFormat> {
use efi::gop::PixelFormat::*;
match from {
PixelRedGreenBlueReserved8BitPerColor => Some(PixelFormat::LfbRgb32),
PixelBlueGreenRedReserved8BitPerColor => Some(PixelFormat::LfbBgr32),
PixelRedGreenBlueReserved8BitPerColor => Some(PixelFormat::LfbRgb32),
PixelBlueGreenRedReserved8BitPerColor => Some(PixelFormat::LfbBgr32),
}
}
fn find_mode(proto: &GraphicsOutputProtocol,
req: &VideoInfo) -> Result<(u32, &'static ModeInformation), Status> {
fn find_mode(
proto: &GraphicsOutputProtocol,
req: &VideoInfo,
) -> Result<(u32, &'static ModeInformation), BootError> {
let req_format = pixel_to_efi(req.format).unwrap();
for (num, info) in proto.mode_iter() {
if info.horizontal_resolution == req.width &&
info.vertical_resolution == req.height &&
info.pixel_format == req_format {
if info.horizontal_resolution == req.width
&& info.vertical_resolution == req.height
&& info.pixel_format == req_format
{
return Ok((num, info));
}
}
Err(Status::InvalidParameter)
Err(BootError::VideoModeUnsupported)
}
pub fn set_mode<T: LoadProtocol>(bs: &BootServices, data: &mut T) -> Result<(), Status> {
let gop = bs.locate_protocol::<GraphicsOutputProtocol>()?;
pub fn set_mode<T: LoadProtocol>(bs: &BootServices, data: &mut T) -> Result<(), BootError> {
let gop = bs
.locate_protocol::<GraphicsOutputProtocol>()
.map_err(|_| BootError::VideoModeFailed)?;
match find_mode(gop, data.get_video_info()) {
Ok((num, info)) => {
let mode = gop.set_mode(num)?;
Ok((num, info)) => {
let mode = gop.set_mode(num).map_err(|_| BootError::VideoModeFailed)?;
let info = VideoInfo {
width: info.horizontal_resolution,
height: info.vertical_resolution,
format: pixel_from_efi(info.pixel_format).unwrap(),
framebuffer: mode.framebuffer_addr() as u64,
pitch: 4 * info.horizontal_resolution as u64
width: info.horizontal_resolution,
height: info.vertical_resolution,
format: pixel_from_efi(info.pixel_format).unwrap(),
framebuffer: mode.framebuffer_addr() as u64,
pitch: 4 * info.horizontal_resolution as u64,
};
data.set_video_info(&info);
Ok(())
},
Err(err) => {
Err(err)
}
Err(err) => Err(err),
}
}