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:
@@ -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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user