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,300 @@
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use redox_syscall::flag::{
MAP_SHARED, O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE,
};
use redox_syscall::PAGE_SIZE;
use syscall as redox_syscall;
use crate::{DriverError, Result};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CacheType {
WriteBack,
Uncacheable,
WriteCombining,
DeviceMemory,
}
impl CacheType {
pub fn suffix(&self) -> &'static str {
match self {
Self::WriteBack => "wb",
Self::Uncacheable => "uc",
Self::WriteCombining => "wc",
Self::DeviceMemory => "dev",
}
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct MmioProt: u8 {
const READ = 0b01;
const WRITE = 0b10;
const READ_WRITE = 0b11;
}
}
// SAFETY: The memory scheme root FD is cached for the process lifetime.
// This is valid because:
// 1. scheme:memory is a kernel-built-in scheme that never terminates.
// 2. The FD is opened with O_CLOEXEC — children after exec(2) do not inherit it.
// 3. This code MUST NOT be used in processes that fork() without exec() —
// the child would share the same FD table slot, risking double-close.
static MEMORY_ROOT_FD: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
fn ensure_memory_root() -> Result<libredox::Fd> {
let current = MEMORY_ROOT_FD.load(Ordering::Acquire);
if !current.is_null() {
let raw_fd = current as usize;
let dup_fd = libredox::call::dup(raw_fd, b"")
.map_err(|e| std::io::Error::from_raw_os_error(e.errno()))?;
return Ok(libredox::Fd::new(dup_fd));
}
let fd = libredox::Fd::open("/scheme/memory/scheme-root", O_CLOEXEC as i32, 0)?;
let raw = fd.raw();
match MEMORY_ROOT_FD.compare_exchange(
ptr::null_mut(),
raw as *mut (),
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
std::mem::forget(fd);
let dup_fd = libredox::call::dup(raw, b"")
.map_err(|e| std::io::Error::from_raw_os_error(e.errno()))?;
return Ok(libredox::Fd::new(dup_fd));
}
Err(existing) => {
let dup_fd = libredox::call::dup(existing as usize, b"")
.map_err(|e| std::io::Error::from_raw_os_error(e.errno()))?;
return Ok(libredox::Fd::new(dup_fd));
}
}
}
pub struct MmioRegion {
ptr: *mut u8,
size: usize,
}
impl MmioRegion {
pub fn map(phys_addr: u64, size: usize, cache: CacheType, prot: MmioProt) -> Result<Self> {
if phys_addr == 0 {
return Err(DriverError::InvalidAddress(phys_addr));
}
let aligned_size = size.next_multiple_of(PAGE_SIZE);
let path = format!("physical@{}", cache.suffix());
let mode = if prot.contains(MmioProt::READ | MmioProt::WRITE) {
O_RDWR
} else if prot.contains(MmioProt::WRITE) {
O_WRONLY
} else {
O_RDONLY
};
let mut mmap_prot = redox_syscall::MapFlags::empty();
if prot.contains(MmioProt::READ) {
mmap_prot |= PROT_READ;
}
if prot.contains(MmioProt::WRITE) {
mmap_prot |= PROT_WRITE;
}
let root_fd = ensure_memory_root()?;
let mem_fd = root_fd.openat(&path, (O_CLOEXEC | mode) as i32, 0)?;
let ptr = unsafe {
libredox::call::mmap(libredox::call::MmapArgs {
fd: mem_fd.raw(),
offset: phys_addr,
length: aligned_size,
flags: MAP_SHARED.bits() as u32,
prot: mmap_prot.bits() as u32,
addr: ptr::null_mut(),
})
}
.map_err(|e| DriverError::MappingFailed {
phys: phys_addr,
size,
reason: format!("{e:?}"),
})?;
Ok(Self {
ptr: ptr as *mut u8,
size: aligned_size,
})
}
#[inline]
pub fn read8(&self, offset: usize) -> u8 {
if offset.checked_add(1).map_or(true, |end| end > self.size) {
log::error!(
"MMIO read8 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return 0;
}
unsafe { core::ptr::read_volatile(self.ptr.add(offset)) }
}
#[inline]
pub fn write8(&self, offset: usize, val: u8) {
if offset.checked_add(1).map_or(true, |end| end > self.size) {
log::error!(
"MMIO write8 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return;
}
unsafe { core::ptr::write_volatile(self.ptr.add(offset), val) }
}
#[inline]
pub fn read16(&self, offset: usize) -> u16 {
if offset.checked_add(2).map_or(true, |end| end > self.size) {
log::error!(
"MMIO read16 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return 0;
}
unsafe { core::ptr::read_volatile(self.ptr.add(offset) as *const u16) }
}
#[inline]
pub fn write16(&self, offset: usize, val: u16) {
if offset.checked_add(2).map_or(true, |end| end > self.size) {
log::error!(
"MMIO write16 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return;
}
unsafe { core::ptr::write_volatile(self.ptr.add(offset) as *mut u16, val) }
}
#[inline]
pub fn read32(&self, offset: usize) -> u32 {
if offset.checked_add(4).map_or(true, |end| end > self.size) {
log::error!(
"MMIO read32 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return 0;
}
unsafe { core::ptr::read_volatile(self.ptr.add(offset) as *const u32) }
}
#[inline]
pub fn write32(&self, offset: usize, val: u32) {
if offset.checked_add(4).map_or(true, |end| end > self.size) {
log::error!(
"MMIO write32 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return;
}
unsafe { core::ptr::write_volatile(self.ptr.add(offset) as *mut u32, val) }
}
#[inline]
pub fn read64(&self, offset: usize) -> u64 {
if offset.checked_add(8).map_or(true, |end| end > self.size) {
log::error!(
"MMIO read64 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return 0;
}
unsafe { core::ptr::read_volatile(self.ptr.add(offset) as *const u64) }
}
#[inline]
pub fn write64(&self, offset: usize, val: u64) {
if offset.checked_add(8).map_or(true, |end| end > self.size) {
log::error!(
"MMIO write64 out of bounds: offset={:#x}, size={:#x}",
offset,
self.size
);
return;
}
unsafe { core::ptr::write_volatile(self.ptr.add(offset) as *mut u64, val) }
}
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) {
if offset
.checked_add(buf.len())
.map_or(true, |end| end > self.size)
{
log::error!(
"MMIO read_bytes out of bounds: offset={:#x}, len={:#x}, size={:#x}",
offset,
buf.len(),
self.size
);
return;
}
// Volatile byte-by-byte read for MMIO correctness (compiler may
// optimise away or reorder copy_nonoverlapping).
for (i, byte) in buf.iter_mut().enumerate() {
*byte = unsafe { core::ptr::read_volatile(self.ptr.add(offset + i)) };
}
}
pub fn write_bytes(&self, offset: usize, buf: &[u8]) {
if offset
.checked_add(buf.len())
.map_or(true, |end| end > self.size)
{
log::error!(
"MMIO write_bytes out of bounds: offset={:#x}, len={:#x}, size={:#x}",
offset,
buf.len(),
self.size
);
return;
}
// Volatile byte-by-byte write for MMIO correctness.
for (i, byte) in buf.iter().enumerate() {
unsafe { core::ptr::write_volatile(self.ptr.add(offset + i), *byte) };
}
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr
}
pub fn size(&self) -> usize {
self.size
}
}
impl Drop for MmioRegion {
fn drop(&mut self) {
if !self.ptr.is_null() {
let _ = unsafe { libredox::call::munmap(self.ptr as *mut (), self.size) };
}
}
}
unsafe impl Send for MmioRegion {}
unsafe impl Sync for MmioRegion {}