milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
+285
View File
@@ -0,0 +1,285 @@
#![no_std]
//! A super simple initfs, only meant to be loaded into RAM by the bootloader, and then directly be
//! read.
use core::convert::{TryFrom, TryInto};
pub mod types;
use self::types::*;
#[derive(Clone, Copy)]
pub struct InitFs<'initfs> {
base: &'initfs [u8],
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Inode(u16);
#[derive(Clone, Copy, Debug)]
pub struct Error;
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "invalid or corrupt initfs")
}
}
impl core::error::Error for Error {}
type Result<T> = core::result::Result<T, Error>;
#[derive(Clone, Copy)]
pub struct InodeStruct<'initfs> {
initfs: InitFs<'initfs>,
inode_id: Inode,
inode: &'initfs InodeHeader,
}
#[derive(Clone, Copy)]
pub struct InodeFile<'initfs> {
inner: InodeStruct<'initfs>,
}
impl<'initfs> InodeFile<'initfs> {
pub fn inode(self) -> InodeStruct<'initfs> {
self.inner
}
pub fn data(&self) -> Result<&'initfs [u8]> {
self.inner.data()
}
}
#[derive(Clone, Copy)]
pub struct InodeDir<'initfs> {
inner: InodeStruct<'initfs>,
}
impl<'initfs> InodeDir<'initfs> {
pub fn inode(self) -> InodeStruct<'initfs> {
self.inner
}
pub fn entry_count(&self) -> Result<u32> {
let len = self.entries()?.len();
// NOTE: Len is originally stored as a u32 in the struct, so it can never exceed u32
// despite first being converted to usize.
let len = len as u32;
Ok(len)
}
pub fn get_entry(&self, idx: u32) -> Result<Option<Entry<'initfs>>> {
let idx = usize::try_from(idx).map_err(|_| Error)?;
self.entries().map(|entries| {
let entry = entries.get(idx)?;
Some(Entry {
entry,
initfs: self.inner.initfs,
})
})
}
fn entries(&self) -> Result<&'initfs [DirEntry]> {
let bytes = self.inner.data()?;
let entries = plain::slice_from_bytes::<DirEntry>(bytes)
.expect("expected dir entry to have alignment 1");
Ok(entries)
}
}
#[derive(Clone, Copy)]
pub struct InodeLink<'initfs> {
inner: InodeStruct<'initfs>,
}
impl<'initfs> InodeLink<'initfs> {
pub fn inode(self) -> InodeStruct<'initfs> {
self.inner
}
pub fn data(&self) -> Result<&'initfs [u8]> {
self.inner.data()
}
}
#[derive(Clone, Copy)]
pub struct Entry<'initfs> {
initfs: InitFs<'initfs>,
entry: &'initfs DirEntry,
}
impl<'initfs> Entry<'initfs> {
pub fn inode(&self) -> Inode {
Inode(self.entry.inode.get())
}
pub fn name(&self) -> Result<&'initfs [u8]> {
let name_offset: usize = self
.entry
.name_offset
.0
.get()
.try_into()
.map_err(|_| Error)?;
let name_length: usize = self.entry.name_len.get().into();
let name_end = name_offset.checked_add(name_length).ok_or(Error)?;
self.initfs.base.get(name_offset..name_end).ok_or(Error)
}
}
#[derive(Clone, Copy)]
pub enum InodeKind<'initfs> {
File(InodeFile<'initfs>),
Dir(InodeDir<'initfs>),
Link(InodeLink<'initfs>),
Unknown,
}
impl<'initfs> InodeStruct<'initfs> {
pub fn id(&self) -> u64 {
self.inode_id.0.into()
}
fn data(&self) -> Result<&'initfs [u8]> {
let start: usize = self.inode.offset.0.get().try_into().map_err(|_| Error)?;
let length: usize = self.inode.length.0.get().try_into().map_err(|_| Error)?;
let end = start.checked_add(length).ok_or(Error)?;
self.initfs.base.get(start..end).ok_or(Error)
}
pub fn mode(&self) -> u16 {
match self.ty() {
Some(InodeType::RegularFile) => 0o444,
Some(InodeType::ExecutableFile) => 0o555,
Some(InodeType::Dir) => 0o555,
Some(InodeType::Link) => 0o777,
None => 0o000,
}
}
fn ty(&self) -> Option<InodeType> {
InodeType::try_from(self.inode.type_.get()).ok()
}
pub fn kind(&self) -> InodeKind<'initfs> {
let inner = *self;
match self.ty() {
Some(InodeType::Dir) => InodeKind::Dir(InodeDir { inner }),
Some(InodeType::RegularFile | InodeType::ExecutableFile) => {
InodeKind::File(InodeFile { inner })
}
Some(InodeType::Link) => InodeKind::Link(InodeLink { inner }),
None => InodeKind::Unknown,
}
}
}
impl<'initfs> InitFs<'initfs> {
pub fn new(base: &'initfs [u8], required_page_size: Option<u16>) -> Result<Self> {
let this = Self { base };
if base.len() < core::mem::size_of::<Header>() {
return Err(Error);
}
if u32::try_from(base.len()).is_err() {
return Err(Error);
}
let header = this.get_header_assume_valid();
if header.magic != Magic(MAGIC) {
return Err(Error);
}
let inode_table_offset = header.inode_table_offset.0.get();
if inode_table_offset < u32::from(Self::header_len_8()) {
return Err(Error);
}
let inode_table_size = this.inode_table_size();
let inode_table_end = inode_table_offset
.checked_add(inode_table_size)
.ok_or(Error)?;
if inode_table_end > this.base_len_32() {
return Err(Error);
}
if let Some(required_page_size) = required_page_size
&& header.page_size.get() != required_page_size
{
return Err(Error);
}
// From now on, we can be completely sure that the header and inode tables offsets are
// valid, and thus continue based on that assumption.
Ok(this)
}
fn get_header_assume_valid(&self) -> &Header {
plain::from_bytes::<Header>(&self.base[..core::mem::size_of::<Header>()])
.expect("expected header type to require no alignment, and size to be sufficient")
}
pub fn header(&self) -> &Header {
self.get_header_assume_valid()
}
fn header_len_8() -> u8 {
core::mem::size_of::<Header>()
.try_into()
.expect("expected header size to fit within u8")
}
fn inode_struct_len_8() -> u8 {
core::mem::size_of::<InodeHeader>()
.try_into()
.expect("expected inode struct size to fit within u8")
}
fn inode_table_offset_usize(&self) -> usize {
// NOTE: We have already validated that the inode table fits within the initfs slice, and
// that this length, which must fit within usize, also fits within u32.
self.get_header_assume_valid().inode_table_offset.0.get() as usize
}
fn inode_table_end_usize(&self) -> usize {
// NOTE: This follows the same reasoning as in inode_table_offset_usize(). The end offset
// has been checked against u32 and the initfs slice length.
self.inode_table_offset_usize()
.wrapping_add(self.inode_table_size() as usize)
}
fn inode_table_range(&self) -> core::ops::Range<usize> {
self.inode_table_offset_usize()..self.inode_table_end_usize()
}
fn base_len_32(&self) -> u32 {
// NOTE: We have already validated that the length is sufficient.
self.base.len() as u32
}
fn inode_table_size(&self) -> u32 {
let count = self.get_header_assume_valid().inode_count.get();
let struct_size = Self::inode_struct_len_8();
// NOTE: We know for a fact, that even the largest u8 (255) and the largest u16 (65536),
// can only be approximately 2^24 at max.
u32::wrapping_mul(u32::from(count), u32::from(struct_size))
}
fn inode_table(&self) -> &'initfs [InodeHeader] {
let inode_table_bytes = &self.base[self.inode_table_range()];
plain::slice_from_bytes::<InodeHeader>(inode_table_bytes)
.expect("expected inode struct alignment to be 1")
}
pub fn root_inode(&self) -> Inode {
Inode(self.get_header_assume_valid().root_inode.get())
}
pub fn all_inodes(&self) -> impl Iterator<Item = Inode> {
(0..self.inode_count()).map(Inode)
}
pub fn inode_count(&self) -> u16 {
self.get_header_assume_valid().inode_count.get()
}
pub fn get_inode(&self, inode_id: Inode) -> Option<InodeStruct<'initfs>> {
Some(InodeStruct {
initfs: *self,
inode_id,
inode: self.inode_table().get(usize::from(inode_id.0))?,
})
}
}
+119
View File
@@ -0,0 +1,119 @@
use core::mem::offset_of;
pub const MAGIC_LEN: usize = 8;
pub const MAGIC: [u8; 8] = *b"RedoxFtw";
macro_rules! primitive(
($wrapper:ident, $bits:expr, $primitive:ident) => {
#[repr(transparent)]
#[derive(Clone, Copy, Default)]
pub struct $wrapper([u8; $bits / 8]);
impl $wrapper {
#[inline]
pub const fn get(self) -> $primitive {
<$primitive>::from_le_bytes(self.0)
}
#[inline]
pub const fn new(primitive: $primitive) -> Self {
Self(<$primitive>::to_le_bytes(primitive))
}
}
impl From<$primitive> for $wrapper {
fn from(primitive: $primitive) -> Self {
Self::new(primitive)
}
}
impl From<$wrapper> for $primitive {
fn from(wrapper: $wrapper) -> Self {
wrapper.get()
}
}
impl core::fmt::Debug for $wrapper {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#0width$x}", self.get(), width = 2 * core::mem::size_of::<$primitive>())
}
}
}
);
primitive!(U16, 16, u16);
primitive!(U32, 32, u32);
primitive!(U64, 64, u64);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Magic(pub [u8; MAGIC_LEN]);
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct Offset(pub U32);
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct Length(pub U32);
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct Header {
pub magic: Magic,
pub inode_table_offset: Offset,
pub initfs_size: U64,
pub page_size: U16,
pub root_inode: U16,
pub inode_count: U16,
pub bootstrap_entry: U64,
}
const _: () = {
// Ensure the offsets of field used by the bootloader stay stable.
assert!(offset_of!(Header, bootstrap_entry) == 0x1a);
};
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct InodeHeader {
pub type_: U32,
pub length: Length,
pub offset: Offset,
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum InodeType {
RegularFile = 0x0,
ExecutableFile = 0x1,
Dir = 0x2,
Link = 0x3,
// All other bit patterns are reserved... for now.
}
impl TryFrom<u32> for InodeType {
type Error = ();
fn try_from(value: u32) -> Result<Self, ()> {
Ok(if value == InodeType::RegularFile as u32 {
InodeType::RegularFile
} else if value == InodeType::ExecutableFile as u32 {
InodeType::ExecutableFile
} else if value == InodeType::Dir as u32 {
InodeType::Dir
} else if value == InodeType::Link as u32 {
InodeType::Link
} else {
return Err(());
})
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct DirEntry {
pub inode: U16,
pub name_len: U16,
pub name_offset: Offset,
}
unsafe impl plain::Plain for Header {}
unsafe impl plain::Plain for InodeHeader {}
unsafe impl plain::Plain for DirEntry {}