Skip to content

Commit

Permalink
Reads page size from kernel note (#935)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Aug 18, 2024
1 parent 0bd471b commit fab5bef
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 12 deletions.
12 changes: 7 additions & 5 deletions src/core/src/error/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::error::Error;
use std::ffi::{c_char, CString};
use std::fmt::Write;
use std::fmt::{Display, Write};

#[no_mangle]
pub unsafe extern "C" fn error_free(e: *mut RustError) {
Expand All @@ -16,12 +16,14 @@ pub unsafe extern "C" fn error_message(e: *const RustError) -> *const c_char {
pub struct RustError(CString);

impl RustError {
pub fn new(msg: impl AsRef<str>) -> *mut Self {
Box::into_raw(Self(CString::new(msg.as_ref()).unwrap()).into())
/// # Panics
/// If `msg` contains NUL character.
pub fn new(msg: impl Into<Vec<u8>>) -> *mut Self {
Box::into_raw(Self(CString::new(msg).unwrap()).into())
}

pub fn with_source(msg: impl AsRef<str>, src: impl Error) -> *mut Self {
let mut msg = format!("{} -> {}", msg.as_ref(), src);
pub fn with_source(msg: impl Display, src: impl Error) -> *mut Self {
let mut msg = format!("{} -> {}", msg, src);
let mut src = src.source();

while let Some(e) = src {
Expand Down
154 changes: 149 additions & 5 deletions src/core/src/vmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub unsafe extern "C" fn vmm_run(
// Parse program headers.
let mut segments = Vec::with_capacity(e_phnum);
let mut dynamic = None;
let mut note = None;

for (index, data) in data.chunks_exact(e_phentsize).enumerate() {
let p_type = u32::from_ne_bytes(data[..4].try_into().unwrap());
Expand All @@ -152,6 +153,14 @@ pub unsafe extern "C" fn vmm_run(

dynamic = Some((p_vaddr, p_memsz));
}
4 => {
if note.is_some() {
*err = RustError::new("multiple PT_NOTE is not supported");
return null_mut();
}

note = Some((p_offset, p_filesz));
}
6 | 1685382481 | 1685382482 => {}
v => {
*err = RustError::new(format!("unknown p_type {v} on program header {index}"));
Expand All @@ -176,6 +185,144 @@ pub unsafe extern "C" fn vmm_run(
}
}

// Check if PT_NOTE exists.
let (off, mut len) = match note {
Some(v) => v,
None => {
*err = RustError::new("no PT_NOTE segment on the kernel");
return null_mut();
}
};

// Seek to PT_NOTE.
match file.seek(SeekFrom::Start(off)) {
Ok(v) => {
if v != off {
*err = RustError::new("the kernel is incomplete");
return null_mut();
}
}
Err(e) => {
*err = RustError::with_source(format_args!("couldn't seek to PT_NOTE at {off:#}"), e);
return null_mut();
}
}

// Parse PT_NOTE.
let mut vm_page_size = None;

for i in 0.. {
const HDR_LEN: usize = 4 * 3;

// Check remaining data.
match len {
0 => break,
..HDR_LEN => {
*err = RustError::new("invalid PT_NOTE on the kernel");
return null_mut();
}
_ => {}
}

// Read note header.
let mut buf = [0u8; HDR_LEN];

if let Err(e) = file.read_exact(&mut buf) {
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} header"), e);
return null_mut();
}

// Parse note header.
let nlen: usize = u32::from_ne_bytes(buf[..4].try_into().unwrap())
.try_into()
.unwrap();
let dlen: usize = u32::from_ne_bytes(buf[4..8].try_into().unwrap())
.try_into()
.unwrap();
let ty = u32::from_ne_bytes(buf[8..].try_into().unwrap());

if nlen > 0xff {
*err = RustError::new(format!("name on kernel note #{i} is too large"));
return null_mut();
}

if dlen > 0xff {
*err = RustError::new(format!("description on kernel note #{i} is too large"));
return null_mut();
}

// Check if header valid.
let nalign = nlen.next_multiple_of(4);

len = match HDR_LEN
.checked_add(nalign)
.and_then(|v| v.checked_add(dlen))
.and_then(|v| len.checked_sub(v))
{
Some(v) => v,
None => {
*err = RustError::new(format!("kernel note #{i} is not valid"));
return null_mut();
}
};

// Read note name + description.
let mut buf = vec![0u8; nalign + dlen];

if let Err(e) = file.read_exact(&mut buf) {
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} data"), e);
return null_mut();
}

// Check name.
let name = match CStr::from_bytes_until_nul(&buf) {
Ok(v) if v.to_bytes_with_nul().len() == nlen => v,
_ => {
*err = RustError::new(format!("kernel note #{i} has invalid name"));
return null_mut();
}
};

if name.to_bytes() != b"obkrnl" {
continue;
}

// Parse description.
match ty {
0 => {
if vm_page_size.is_some() {
*err = RustError::new(format!("kernel note #{i} is duplicated"));
return null_mut();
}

vm_page_size = buf[nalign..]
.try_into()
.map(usize::from_ne_bytes)
.ok()
.and_then(NonZero::new)
.filter(|v| v.is_power_of_two());

if vm_page_size.is_none() {
*err = RustError::new(format!("invalid description on kernel note #{i}"));
return null_mut();
}
}
v => {
*err = RustError::new(format!("unknown type {v} on kernel note #{i}"));
return null_mut();
}
}
}

// Check if page size exists.
let vm_page_size = match vm_page_size {
Some(v) => v,
None => {
*err = RustError::new("no page size in kernel note");
return null_mut();
}
};

// Get kernel memory size.
let mut len = 0;

Expand Down Expand Up @@ -211,7 +358,7 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::new("the kernel has PT_LOAD with zero length");
return null_mut();
}
v => match v.checked_next_multiple_of(host_page_size.get()) {
v => match v.checked_next_multiple_of(vm_page_size.get()) {
Some(v) => NonZero::new_unchecked(v),
None => {
*err = RustError::new("total size of PT_LOAD is too large");
Expand Down Expand Up @@ -268,11 +415,8 @@ pub unsafe extern "C" fn vmm_run(
return null_mut();
}

// Setup virtual devices.
let vm_page_size = NonZero::new(0x4000).unwrap();
let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size));

// Allocate arguments.
let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size));
let env = BootEnv::Vm(Vm {
console: devices.console().addr(),
});
Expand Down
3 changes: 1 addition & 2 deletions src/macros/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ pub fn transform_note(opts: Options, mut item: ItemStatic) -> syn::Result<TokenS

// Compose.
Ok(quote! {
#[cfg(not(test))]
#[used]
#[link_section = #section]
#[cfg_attr(not(test), link_section = #section)]
#item
})
}
Expand Down

0 comments on commit fab5bef

Please sign in to comment.