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,113 @@
#[derive(Clone, Debug)]
pub enum Subsystem {
Gpu,
Network,
Storage,
Audio,
Usb,
Input,
Unknown,
}
#[derive(Clone, Debug)]
pub struct DeviceInfo {
pub bus: u8,
pub dev: u8,
pub func: u8,
pub vendor_id: u16,
pub device_id: u16,
pub class_code: u8,
pub subclass: u8,
pub subsystem: Subsystem,
pub name: String,
pub path: String,
}
pub fn classify_pci_device(bus: u8, dev: u8, func: u8) -> DeviceInfo {
let path = format!("/devices/pci/{:04x}:{:02x}:{:02x}.{}", bus, 0, dev, func);
let config_path = format!("/scheme/pci/{}.{}.{}", bus, dev, func);
let (vendor_id, device_id, class_code, subclass) = read_pci_config(&config_path);
let subsystem = match class_code {
0x03 => Subsystem::Gpu,
0x02 => Subsystem::Network,
0x01 => Subsystem::Storage,
0x04 => Subsystem::Audio,
0x0C => Subsystem::Usb,
0x09 => Subsystem::Input,
_ => Subsystem::Unknown,
};
let name = format_device_name(vendor_id, device_id, class_code);
DeviceInfo {
bus,
dev,
func,
vendor_id,
device_id,
class_code,
subclass,
subsystem,
name,
path,
}
}
fn read_pci_config(path: &str) -> (u16, u16, u8, u8) {
match std::fs::read(path) {
Ok(data) if data.len() >= 16 => {
let vendor_id = u16::from_le_bytes([data[0], data[1]]);
let device_id = u16::from_le_bytes([data[2], data[3]]);
let class_code = data[11];
let subclass = data[10];
(vendor_id, device_id, class_code, subclass)
}
_ => (0xFFFF, 0xFFFF, 0xFF, 0xFF),
}
}
fn format_device_name(vendor_id: u16, device_id: u16, class_code: u8) -> String {
let vendor_name = match vendor_id {
0x8086 => "Intel",
0x1002 => "AMD",
0x10DE => "NVIDIA",
0x10EC => "Realtek",
0x8087 => "Intel",
0x14E4 => "Broadcom",
_ => "Unknown",
};
let class_name = match class_code {
0x03 => "GPU",
0x02 => "Network Controller",
0x01 => "Storage Controller",
0x04 => "Multimedia Device",
0x0C => "USB Controller",
0x09 => "Input Device",
_ => "PCI Device",
};
format!(
"{} {} [{:04x}:{:04x}]",
vendor_name, class_name, vendor_id, device_id
)
}
pub fn format_device_info(dev: &DeviceInfo) -> String {
let subsystem = match dev.subsystem {
Subsystem::Gpu => "gpu",
Subsystem::Network => "net",
Subsystem::Storage => "block",
Subsystem::Audio => "sound",
Subsystem::Usb => "usb",
Subsystem::Input => "input",
Subsystem::Unknown => "unknown",
};
format!(
"P={}\nE=SUBSYSTEM={}\nE=PCI_VENDOR_ID={:#06x}\nE=PCI_DEVICE_ID={:#06x}\nE=PCI_CLASS={:#04x}{:02x}\nE=DEVNAME={}\n",
dev.path, subsystem, dev.vendor_id, dev.device_id, dev.class_code, dev.subclass, dev.name
)
}
@@ -0,0 +1,82 @@
mod device_db;
mod scheme;
use std::env;
use std::process;
use log::{error, info, LevelFilter, Metadata, Record};
use redox_scheme::{SignalBehavior, Socket};
use scheme::UdevScheme;
struct StderrLogger {
level: LevelFilter,
}
impl log::Log for StderrLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("[{}] {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
fn run() -> Result<(), String> {
let mut scheme = UdevScheme::new();
match scheme.scan_pci_devices() {
Ok(n) => info!("udev-shim: enumerated {} PCI device(s)", n),
Err(e) => error!("udev-shim: PCI scan failed: {}", e),
}
let socket =
Socket::create("udev").map_err(|e| format!("failed to register udev scheme: {}", e))?;
info!("udev-shim: registered scheme:udev");
loop {
let request = match socket.next_request(SignalBehavior::Restart) {
Ok(Some(r)) => r,
Ok(None) => {
info!("udev-shim: scheme unmounted, exiting");
break;
}
Err(e) => {
error!("udev-shim: failed to read scheme request: {}", e);
continue;
}
};
let response = match request.handle_scheme_block_mut(&mut scheme) {
Ok(r) => r,
Err(_req) => {
error!("udev-shim: failed to handle request");
continue;
}
};
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
error!("udev-shim: failed to write response: {}", e);
}
}
Ok(())
}
fn main() {
let log_level = match env::var("UDEV_SHIM_LOG").as_deref() {
Ok("debug") => LevelFilter::Debug,
Ok("trace") => LevelFilter::Trace,
_ => LevelFilter::Info,
};
let _ = log::set_boxed_logger(Box::new(StderrLogger { level: log_level }));
log::set_max_level(log_level);
if let Err(e) = run() {
error!("udev-shim: fatal error: {}", e);
process::exit(1);
}
}
@@ -0,0 +1,170 @@
use std::collections::BTreeMap;
use syscall::data::Stat;
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EROFS};
use syscall::flag::{EventFlags, MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
use crate::device_db::{classify_pci_device, format_device_info, DeviceInfo, Subsystem};
struct Handle {
kind: HandleKind,
offset: usize,
}
enum HandleKind {
Root,
Device(usize),
}
pub struct UdevScheme {
next_id: usize,
handles: BTreeMap<usize, Handle>,
devices: Vec<DeviceInfo>,
}
impl UdevScheme {
pub fn new() -> Self {
UdevScheme {
next_id: 0,
handles: BTreeMap::new(),
devices: Vec::new(),
}
}
pub fn scan_pci_devices(&mut self) -> Result<usize> {
let dir = match std::fs::read_dir("/scheme/pci") {
Ok(d) => d,
Err(e) => {
log::warn!("udev-shim: failed to read /scheme/pci: {e}");
return Ok(0);
}
};
let mut count = 0;
for entry in dir {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let name = match entry.file_name().to_str() {
Some(n) => n.to_string(),
None => continue,
};
let parts: Vec<&str> = name.split('.').collect();
if parts.len() < 3 {
continue;
}
let bus: u8 = parts[0].parse().unwrap_or(0);
let dev: u8 = parts[1].parse().unwrap_or(0);
let func: u8 = parts[2].parse().unwrap_or(0);
let info = classify_pci_device(bus, dev, func);
self.devices.push(info);
count += 1;
}
Ok(count)
}
}
impl redox_scheme::SchemeBlockMut for UdevScheme {
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
let cleaned = path.trim_matches('/');
let kind = if cleaned.is_empty() {
HandleKind::Root
} else if cleaned == "devices" || cleaned == "devices/" {
HandleKind::Root
} else if let Some(rest) = cleaned.strip_prefix("devices/") {
let idx: usize = rest
.trim_end_matches('/')
.parse()
.map_err(|_| Error::new(ENOENT))?;
if idx >= self.devices.len() {
return Err(Error::new(ENOENT));
}
HandleKind::Device(idx)
} else {
return Err(Error::new(ENOENT));
};
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle { kind, offset: 0 });
Ok(Some(id))
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let content = match &handle.kind {
HandleKind::Root => {
let mut listing = String::new();
for (i, dev) in self.devices.iter().enumerate() {
listing.push_str(&format!("devices/{}\n", i));
}
listing
}
HandleKind::Device(idx) => {
let dev = &self.devices[*idx];
format_device_info(dev)
}
};
let bytes = content.as_bytes();
let remaining = &bytes[handle.offset..];
let to_copy = remaining.len().min(buf.len());
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
handle.offset += to_copy;
Ok(Some(to_copy))
}
fn write(&mut self, id: usize, _buf: &[u8]) -> Result<Option<usize>> {
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
Err(Error::new(EROFS))
}
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let len = match &handle.kind {
HandleKind::Root => self.devices.len() * 20,
HandleKind::Device(idx) => format_device_info(&self.devices[*idx]).len(),
};
let new_offset = match whence {
SEEK_SET => pos as isize,
SEEK_CUR => handle.offset as isize + pos,
SEEK_END => len as isize + pos,
_ => return Err(Error::new(EINVAL)),
};
if new_offset < 0 {
return Err(Error::new(EINVAL));
}
handle.offset = new_offset as usize;
Ok(Some(new_offset))
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
match &handle.kind {
HandleKind::Root => {
stat.st_mode = MODE_DIR | 0o555;
}
HandleKind::Device(_) => {
stat.st_mode = MODE_FILE | 0o444;
}
}
Ok(Some(0))
}
fn fevent(&mut self, id: usize, _flags: EventFlags) -> Result<Option<EventFlags>> {
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
Ok(Some(EventFlags::empty()))
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles.remove(&id);
Ok(Some(0))
}
}