mod debug;
mod mem_map;
use core::alloc::Layout;
use core::ffi::{CStr, c_char};
use core::{ptr, slice};
pub use self::mem_map::{IdentityMap, MemMap};
use crate::start_info::{MemmapTableEntry, ModlistEntry, StartInfo};
pub struct StartInfoReader<'a, M> {
start_info: &'a StartInfo,
mem_map: M,
}
impl StartInfoReader<'_, IdentityMap> {
#[inline]
#[must_use]
pub unsafe fn from_paddr_identity(paddr: u32) -> Option<Self> {
unsafe { Self::from_paddr(paddr, IdentityMap) }
}
}
impl<'a, M: MemMap> StartInfoReader<'a, M> {
#[must_use]
pub unsafe fn from_paddr(paddr: u32, mem_map: M) -> Option<Self> {
if paddr == 0 {
return None;
}
let paddr = usize::try_from(paddr).ok()?;
let layout = Layout::new::<StartInfo>();
let start_info = mem_map.ptr(paddr, layout).cast::<StartInfo>();
let start_info = unsafe { StartInfo::from_ptr(start_info)? };
Some(Self {
start_info,
mem_map,
})
}
#[inline]
#[must_use]
pub fn raw(&self) -> &'a StartInfo {
self.start_info
}
#[must_use]
fn modlist_vaddr(&self) -> *const ModlistEntry {
if self.start_info.modlist_paddr == 0 {
return ptr::null();
}
let Ok(paddr) = usize::try_from(self.start_info.modlist_paddr) else {
return ptr::null();
};
let Ok(size) = usize::try_from(self.start_info.nr_modules) else {
return ptr::null();
};
let Ok(layout) = Layout::array::<ModlistEntry>(size) else {
return ptr::null();
};
self.mem_map.ptr(paddr, layout).cast::<ModlistEntry>()
}
#[must_use]
fn raw_modlist(&self) -> &'a [ModlistEntry] {
let ptr = self.modlist_vaddr();
if ptr.is_null() {
return &[];
}
let Ok(len) = usize::try_from(self.start_info.nr_modules) else {
return &[];
};
unsafe { slice::from_raw_parts(ptr, len) }
}
pub fn modlist(&self) -> impl Iterator<Item = ModlistEntryReader<'a, &M>> {
let mem_map = &self.mem_map;
self.raw_modlist()
.iter()
.map(move |modlist_entry| ModlistEntryReader {
modlist_entry,
mem_map,
})
}
#[must_use]
fn cmdline_vaddr(&self) -> *const c_char {
if self.start_info.cmdline_paddr == 0 {
return ptr::null();
}
let Ok(paddr) = usize::try_from(self.start_info.cmdline_paddr) else {
return ptr::null();
};
let layout = Layout::new::<c_char>();
self.mem_map.ptr(paddr, layout).cast::<c_char>()
}
#[must_use]
pub fn cmdline(&self) -> Option<&'a CStr> {
let ptr = self.cmdline_vaddr();
if ptr.is_null() {
return None;
}
let cmdline = unsafe { CStr::from_ptr(ptr) };
Some(cmdline)
}
#[must_use]
fn memmap_vaddr(&self) -> *const MemmapTableEntry {
if self.start_info.version < 1 {
return ptr::null();
}
if self.start_info.memmap_paddr == 0 {
return ptr::null();
}
let Ok(paddr) = usize::try_from(self.start_info.memmap_paddr) else {
return ptr::null();
};
let Ok(size) = usize::try_from(self.start_info.memmap_entries) else {
return ptr::null();
};
let Ok(layout) = Layout::array::<MemmapTableEntry>(size) else {
return ptr::null();
};
self.mem_map.ptr(paddr, layout).cast::<MemmapTableEntry>()
}
#[must_use]
pub fn memmap(&self) -> &'a [MemmapTableEntry] {
let ptr = self.memmap_vaddr();
if ptr.is_null() {
return &[];
}
let Ok(len) = usize::try_from(self.start_info.memmap_entries) else {
return &[];
};
unsafe { slice::from_raw_parts(ptr, len) }
}
}
pub struct ModlistEntryReader<'a, M> {
modlist_entry: &'a ModlistEntry,
mem_map: M,
}
impl<'a, M: MemMap> ModlistEntryReader<'a, M> {
#[inline]
#[must_use]
pub fn raw(&self) -> &'a ModlistEntry {
self.modlist_entry
}
#[must_use]
fn vaddr(&self) -> *const u8 {
let Ok(paddr) = usize::try_from(self.modlist_entry.paddr) else {
return ptr::null();
};
let Ok(size) = usize::try_from(self.modlist_entry.size) else {
return ptr::null();
};
let Ok(layout) = Layout::array::<u8>(size) else {
return ptr::null();
};
self.mem_map.ptr(paddr, layout).cast::<u8>()
}
#[must_use]
pub fn as_slice(&self) -> &'a [u8] {
let ptr = self.vaddr();
let Ok(len) = usize::try_from(self.modlist_entry.size) else {
return &[];
};
unsafe { slice::from_raw_parts(ptr, len) }
}
#[must_use]
fn cmdline_vaddr(&self) -> *const c_char {
let Ok(paddr) = usize::try_from(self.modlist_entry.cmdline_paddr) else {
return ptr::null();
};
let layout = Layout::new::<c_char>();
self.mem_map.ptr(paddr, layout).cast::<c_char>()
}
#[must_use]
pub fn cmdline(&self) -> Option<&'a CStr> {
let ptr = self.cmdline_vaddr();
if ptr.is_null() {
return None;
}
let cmdline = unsafe { CStr::from_ptr(ptr) };
Some(cmdline)
}
}
#[expect(unused)]
#[cfg(test)]
mod start_info_reader_lifetimes {
use super::*;
type Reader<'a> = StartInfoReader<'a, IdentityMap>;
fn raw(start_info: Reader<'_>) -> &StartInfo {
start_info.raw()
}
fn raw_modlist(start_info: Reader<'_>) -> &[ModlistEntry] {
start_info.raw_modlist()
}
fn first_modlist_as_slice(start_info: Reader<'_>) -> &[u8] {
start_info.modlist().next().unwrap().as_slice()
}
fn cmdline(start_info: Reader<'_>) -> &CStr {
start_info.cmdline().unwrap()
}
fn memmap(start_info: Reader<'_>) -> &[MemmapTableEntry] {
start_info.memmap()
}
}
#[expect(unused)]
#[cfg(test)]
mod modlist_entry_reader_lifetimes {
use super::*;
type Reader<'a> = ModlistEntryReader<'a, IdentityMap>;
fn raw(modlist_entry: Reader<'_>) -> &ModlistEntry {
modlist_entry.raw()
}
fn as_slice(modlist_entry: Reader<'_>) -> &[u8] {
modlist_entry.as_slice()
}
fn cmdline(modlist_entry: Reader<'_>) -> &CStr {
modlist_entry.cmdline().unwrap()
}
}