Skip to content

Commit

Permalink
Refactors kernel reader (#936)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Aug 19, 2024
1 parent fab5bef commit 0a66e34
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 146 deletions.
8 changes: 5 additions & 3 deletions src/core/src/vmm/hw/ram/builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{Ram, RamError};
use crate::vmm::hw::DeviceTree;
use crate::vmm::kernel::ProgramHeader;
use crate::vmm::VmmError;
use obconf::BootEnv;
use std::num::NonZero;
Expand Down Expand Up @@ -133,7 +134,7 @@ impl RamBuilder {
pub fn build(
mut self,
devices: &DeviceTree,
dynamic: Option<(usize, usize)>,
dynamic: Option<ProgramHeader>,
) -> Result<(Ram, RamMap), RamBuilderError> {
// For x86-64 we require the kernel to be a Position-Independent Executable so we can map it
// at the same address as the PS4 kernel.
Expand Down Expand Up @@ -195,7 +196,8 @@ impl RamBuilder {
self.setup_4k_page_tables(pml4t, vaddr, ram.start, ram.end - ram.start)?;

// Check if PT_DYNAMIC valid.
let (p_vaddr, p_memsz) = dynamic;
let p_vaddr = dynamic.p_vaddr;
let p_memsz = dynamic.p_memsz;

if p_memsz % 16 != 0 {
return Err(RamBuilderError::InvalidDynamicLinking);
Expand Down Expand Up @@ -249,7 +251,7 @@ impl RamBuilder {
pub fn build(
self,
devices: &DeviceTree,
dynamic: Option<(usize, usize)>,
dynamic: Option<ProgramHeader>,
) -> Result<(Ram, RamMap), RamBuilderError> {
todo!()
}
Expand Down
117 changes: 117 additions & 0 deletions src/core/src/vmm/kernel/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Take};
use std::path::Path;
use thiserror::Error;

pub use self::segment::*;

mod segment;

/// Encapsulates a kernel ELF file.
pub struct Kernel {
file: File,
e_entry: usize,
e_phoff: u64,
e_phnum: u64,
}

impl Kernel {
pub fn open(path: impl AsRef<Path>) -> Result<Self, KernelError> {
// Read ELF header.
let mut file = File::open(path).map_err(KernelError::OpenImageFailed)?;
let mut hdr = [0; 64];

file.read_exact(&mut hdr)
.map_err(KernelError::ReadElfHeaderFailed)?;

// Check if ELF.
if &hdr[..4] != b"\x7fELF" {
return Err(KernelError::NotElf);
}

// Check ELF type.
if hdr[4] != 2 {
return Err(KernelError::Not64);
}

match hdr[6] {
1 => {}
v => return Err(KernelError::UnknownElfVersion(v)),
}

if u16::from_ne_bytes(hdr[18..20].try_into().unwrap()) != ELF_MACHINE {
return Err(KernelError::DifferentArch);
}

// Load ELF header.
let e_entry = usize::from_ne_bytes(hdr[24..32].try_into().unwrap());
let e_phoff = u64::from_ne_bytes(hdr[32..40].try_into().unwrap());
let e_phentsize: usize = u16::from_ne_bytes(hdr[54..56].try_into().unwrap()).into();
let e_phnum: u64 = u16::from_ne_bytes(hdr[56..58].try_into().unwrap()).into();

if e_phentsize != 56 {
return Err(KernelError::UnsupportedProgramHeader);
}

Ok(Self {
file,
e_entry,
e_phoff,
e_phnum,
})
}

pub fn entry(&self) -> usize {
self.e_entry
}

pub fn program_headers(&mut self) -> Result<ProgramHeaders, std::io::Error> {
let off = self.file.seek(SeekFrom::Start(self.e_phoff))?;

if off != self.e_phoff {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
} else {
Ok(ProgramHeaders::new(&mut self.file, off, self.e_phnum))
}
}

pub fn segment_data(&mut self, hdr: &ProgramHeader) -> Result<Take<&mut File>, std::io::Error> {
let off = self.file.seek(SeekFrom::Start(hdr.p_offset))?;

if off != hdr.p_offset {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
} else {
Ok(self.file.by_ref().take(hdr.p_filesz))
}
}
}

#[cfg(target_arch = "x86_64")]
const ELF_MACHINE: u16 = 62;
#[cfg(target_arch = "aarch64")]
const ELF_MACHINE: u16 = 183;

/// Represents an error when [`Kernel::open()`] fails.
#[derive(Debug, Error)]
pub enum KernelError {
#[error("couldn't open kernel file")]
OpenImageFailed(#[source] std::io::Error),

#[error("couldn't read ELF header")]
ReadElfHeaderFailed(#[source] std::io::Error),

#[error("the kernel is not an ELF file")]
NotElf,

#[error("the kernel has unknown ELF version {0}")]
UnknownElfVersion(u8),

#[error("the kernel is not 64-bit kernel")]
Not64,

#[error("the kernel is for a different CPU architecture")]
DifferentArch,

#[error("the kernel has unsupported e_phentsize")]
UnsupportedProgramHeader,
}
79 changes: 79 additions & 0 deletions src/core/src/vmm/kernel/segment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::fs::File;
use std::io::Read;
use std::iter::FusedIterator;
use thiserror::Error;

/// Iterator to enumerate ELF program headers.
pub struct ProgramHeaders<'a> {
file: &'a mut File,
start: u64,
count: u64,
parsed: u64,
}

impl<'a> ProgramHeaders<'a> {
pub(super) fn new(file: &'a mut File, start: u64, count: u64) -> Self {
Self {
file,
start,
count,
parsed: 0,
}
}
}

impl<'a> Iterator for ProgramHeaders<'a> {
type Item = Result<ProgramHeader, ProgramHeaderError>;

fn next(&mut self) -> Option<Self::Item> {
// Check remaining.
if self.parsed == self.count {
return None;
}

// Read data.
let mut data = [0u8; 56];

if let Err(e) = self.file.read_exact(&mut data) {
return Some(Err(ProgramHeaderError::ReadFailed(
self.start + self.parsed * 56,
e,
)));
}

// Parse data.
let p_type = u32::from_ne_bytes(data[..4].try_into().unwrap());
let p_offset = u64::from_ne_bytes(data[8..16].try_into().unwrap());
let p_vaddr = usize::from_ne_bytes(data[16..24].try_into().unwrap());
let p_filesz = u64::from_ne_bytes(data[32..40].try_into().unwrap());
let p_memsz = usize::from_ne_bytes(data[40..48].try_into().unwrap());

self.parsed += 1;

Some(Ok(ProgramHeader {
p_type,
p_offset,
p_vaddr,
p_filesz,
p_memsz,
}))
}
}

impl<'a> FusedIterator for ProgramHeaders<'a> {}

/// Parsed ELF program header.
pub struct ProgramHeader {
pub p_type: u32,
pub p_offset: u64,
pub p_vaddr: usize,
pub p_filesz: u64,
pub p_memsz: usize,
}

/// Represents an error when [`ProgramHeaders`] fails to enumerate an ELF header.
#[derive(Debug, Error)]
pub enum ProgramHeaderError {
#[error("couldn't read 56 bytes at offset {0}")]
ReadFailed(u64, #[source] std::io::Error),
}
Loading

0 comments on commit 0a66e34

Please sign in to comment.