Red Bear OS — microkernel OS in Rust, based on Redox

Derivative of Redox OS (https://www.redox-os.org) adding:
- AMD GPU driver (amdgpu) via LinuxKPI compat layer
- ext4 filesystem support (ext4d scheme daemon)
- ACPI fixes for AMD bare metal (x2APIC, DMAR, IVRS, MCFG)
- Custom branding (hostname, os-release, boot identity)

Build system is full upstream Redox with RBOS overlay in local/.
Patches for kernel, base, and relibc are symlinked from local/patches/
and protected from make clean/distclean. Custom recipes live in
local/recipes/ with symlinks into the recipes/ search path.

Build:  make all CONFIG_NAME=redbear-full
Sync:   ./local/scripts/sync-upstream.sh
This commit is contained in:
2026-04-12 19:05:00 +01:00
commit 50b731f1b7
3392 changed files with 98327 additions and 0 deletions
@@ -0,0 +1,16 @@
[package]
name = "ext4-blockdev"
description = "BlockDevice trait implementations for rsext4 on Redox OS"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
rsext4.workspace = true
redox_syscall = { workspace = true, optional = true }
libredox = { workspace = true, optional = true }
log.workspace = true
[features]
default = ["redox"]
redox = ["dep:redox_syscall", "dep:libredox"]
@@ -0,0 +1,100 @@
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::time::UNIX_EPOCH;
use rsext4::bmalloc::AbsoluteBN;
use rsext4::disknode::Ext4Timestamp;
use rsext4::{BlockDevice, Ext4Error, Ext4Result};
pub struct FileDisk {
file: File,
total_blocks: u64,
block_size: u32,
}
impl FileDisk {
pub fn open<P: AsRef<Path>>(path: P, block_size: u32) -> std::io::Result<Self> {
let file = OpenOptions::new().read(true).write(true).open(path)?;
let len = file.metadata()?.len();
Ok(Self {
file,
total_blocks: len / block_size as u64,
block_size,
})
}
pub fn create<P: AsRef<Path>>(path: P, size: u64, block_size: u32) -> std::io::Result<Self> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)?;
file.set_len(size)?;
Ok(Self {
file,
total_blocks: size / block_size as u64,
block_size,
})
}
}
impl BlockDevice for FileDisk {
fn read(&mut self, buffer: &mut [u8], block_id: AbsoluteBN, count: u32) -> Ext4Result<()> {
let offset = block_id.raw() * self.block_size as u64;
self.file
.seek(SeekFrom::Start(offset))
.map_err(|_| Ext4Error::io())?;
let total = count as usize * self.block_size as usize;
if buffer.len() < total {
return Err(Ext4Error::invalid_input());
}
self.file
.read_exact(&mut buffer[..total])
.map_err(|_| Ext4Error::io())?;
Ok(())
}
fn write(&mut self, buffer: &[u8], block_id: AbsoluteBN, count: u32) -> Ext4Result<()> {
let offset = block_id.raw() * self.block_size as u64;
self.file
.seek(SeekFrom::Start(offset))
.map_err(|_| Ext4Error::io())?;
let total = count as usize * self.block_size as usize;
if buffer.len() < total {
return Err(Ext4Error::invalid_input());
}
self.file
.write_all(&buffer[..total])
.map_err(|_| Ext4Error::io())?;
Ok(())
}
fn open(&mut self) -> Ext4Result<()> {
Ok(())
}
fn close(&mut self) -> Ext4Result<()> {
Ok(())
}
fn total_blocks(&self) -> u64 {
self.total_blocks
}
fn block_size(&self) -> u32 {
self.block_size
}
fn flush(&mut self) -> Ext4Result<()> {
self.file.sync_data().map_err(|_| Ext4Error::io())
}
fn current_time(&self) -> Ext4Result<Ext4Timestamp> {
let dur = std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|_| Ext4Error::io())?;
Ok(Ext4Timestamp::new(dur.as_secs() as i64, dur.subsec_nanos()))
}
}
@@ -0,0 +1,13 @@
pub mod file_disk;
#[cfg(feature = "redox")]
pub mod redox_disk;
pub use file_disk::FileDisk;
#[cfg(feature = "redox")]
pub use redox_disk::RedoxDisk;
pub use rsext4::bmalloc::AbsoluteBN;
pub use rsext4::disknode::Ext4Timestamp;
pub use rsext4::{BlockDevice, Ext4Error, Ext4Result};
@@ -0,0 +1,93 @@
use rsext4::bmalloc::AbsoluteBN;
use rsext4::disknode::Ext4Timestamp;
use rsext4::{BlockDevice, Ext4Error, Ext4Result};
pub struct RedoxDisk {
fd: usize,
total_blocks: u64,
block_size: u32,
}
impl RedoxDisk {
pub fn open(disk_path: &str, block_size: u32) -> syscall::error::Result<Self> {
let fd = libredox::call::open(disk_path, libredox::flag::O_RDWR, 0)?;
let mut stat = syscall::data::Stat::default();
syscall::call::fstat(fd, &mut stat)?;
let total_blocks = stat.st_size / block_size as u64;
Ok(Self {
fd,
total_blocks,
block_size,
})
}
}
impl BlockDevice for RedoxDisk {
fn read(&mut self, buffer: &mut [u8], block_id: AbsoluteBN, count: u32) -> Ext4Result<()> {
let offset = block_id.raw() * self.block_size as u64;
let total = count as usize * self.block_size as usize;
if buffer.len() < total {
return Err(Ext4Error::invalid_input());
}
syscall::call::lseek(self.fd, offset as isize, syscall::flag::SEEK_SET)
.map_err(|_| Ext4Error::io())?;
let mut read_total = 0;
while read_total < total {
let n = syscall::call::read(self.fd, &mut buffer[read_total..total])
.map_err(|_| Ext4Error::io())?;
if n == 0 {
return Err(Ext4Error::io());
}
read_total += n;
}
Ok(())
}
fn write(&mut self, buffer: &[u8], block_id: AbsoluteBN, count: u32) -> Ext4Result<()> {
let offset = block_id.raw() * self.block_size as u64;
let total = count as usize * self.block_size as usize;
if buffer.len() < total {
return Err(Ext4Error::invalid_input());
}
syscall::call::lseek(self.fd, offset as isize, syscall::flag::SEEK_SET)
.map_err(|_| Ext4Error::io())?;
let mut written_total = 0;
while written_total < total {
let n = syscall::call::write(self.fd, &buffer[written_total..total])
.map_err(|_| Ext4Error::io())?;
if n == 0 {
return Err(Ext4Error::io());
}
written_total += n;
}
Ok(())
}
fn open(&mut self) -> Ext4Result<()> {
Ok(())
}
fn close(&mut self) -> Ext4Result<()> {
Ok(())
}
fn total_blocks(&self) -> u64 {
self.total_blocks
}
fn block_size(&self) -> u32 {
self.block_size
}
fn flush(&mut self) -> Ext4Result<()> {
syscall::call::fsync(self.fd).map_err(|_| Ext4Error::io())?;
Ok(())
}
fn current_time(&self) -> Ext4Result<Ext4Timestamp> {
let mut ts = syscall::data::TimeSpec::default();
syscall::call::clock_gettime(syscall::flag::CLOCK_REALTIME, &mut ts)
.map_err(|_| Ext4Error::io())?;
Ok(Ext4Timestamp::new(ts.tv_sec, ts.tv_nsec as u32))
}
}