Add runtime tools and Red Bear service wiring
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -4,8 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
redox_syscall = { version = "0.7", features = ["std"] }
|
||||
syscall04 = { package = "redox_syscall", version = "0.4" }
|
||||
redox_scheme = { package = "redox-scheme", version = "0.1" }
|
||||
libc = "0.2"
|
||||
syscall = { package = "redox_syscall", version = "0.7", features = ["std"] }
|
||||
redox_scheme = { package = "redox-scheme", version = "0.11" }
|
||||
libredox = "0.1"
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
thiserror = "2"
|
||||
|
||||
@@ -103,6 +103,14 @@ impl FirmwareRegistry {
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.blobs.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.blobs.is_empty()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn list_keys(&self) -> Vec<&str> {
|
||||
self.blobs.keys().map(|s| s.as_str()).collect()
|
||||
|
||||
@@ -2,11 +2,12 @@ mod blob;
|
||||
mod scheme;
|
||||
|
||||
use std::env;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use log::{error, info, LevelFilter, Metadata, Record};
|
||||
use redox_scheme::{SignalBehavior, Socket};
|
||||
use redox_scheme::{scheme::SchemeSync, SignalBehavior, Socket};
|
||||
|
||||
use blob::FirmwareRegistry;
|
||||
use scheme::FirmwareScheme;
|
||||
@@ -40,52 +41,59 @@ fn default_firmware_dir() -> PathBuf {
|
||||
PathBuf::from("/usr/firmware/")
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let firmware_dir = env::var("FIRMWARE_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| default_firmware_dir());
|
||||
unsafe fn get_init_notify_fd() -> RawFd {
|
||||
let fd: RawFd = env::var("INIT_NOTIFY")
|
||||
.expect("firmware-loader: INIT_NOTIFY not set")
|
||||
.parse()
|
||||
.expect("firmware-loader: INIT_NOTIFY is not a valid fd");
|
||||
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
|
||||
fd
|
||||
}
|
||||
|
||||
info!(
|
||||
"firmware-loader: starting with directory {}",
|
||||
firmware_dir.display()
|
||||
);
|
||||
fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut FirmwareScheme) {
|
||||
let cap_id = scheme
|
||||
.scheme_root()
|
||||
.expect("firmware-loader: scheme_root failed");
|
||||
let cap_fd = socket
|
||||
.create_this_scheme_fd(0, cap_id, 0, 0)
|
||||
.expect("firmware-loader: create_this_scheme_fd failed");
|
||||
|
||||
let registry = FirmwareRegistry::new(&firmware_dir)
|
||||
.map_err(|e| format!("failed to initialize firmware registry: {e}"))?;
|
||||
syscall::call_wo(
|
||||
notify_fd as usize,
|
||||
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
|
||||
syscall::CallFlags::FD,
|
||||
&[],
|
||||
)
|
||||
.expect("firmware-loader: failed to notify init that scheme is ready");
|
||||
}
|
||||
|
||||
fn run_daemon(notify_fd: RawFd, registry: FirmwareRegistry) -> ! {
|
||||
let socket = Socket::create().expect("firmware-loader: failed to create scheme socket");
|
||||
let mut scheme = FirmwareScheme::new(registry);
|
||||
|
||||
notify_scheme_ready(notify_fd, &socket, &mut scheme);
|
||||
|
||||
let socket = Socket::create("firmware")
|
||||
.map_err(|e| format!("failed to register firmware scheme: {e}"))?;
|
||||
info!("firmware-loader: registered scheme:firmware");
|
||||
|
||||
let mut firmware_scheme = FirmwareScheme::new(registry);
|
||||
libredox::call::setrens(0, 0).expect("firmware-loader: failed to enter null namespace");
|
||||
|
||||
loop {
|
||||
let request = match socket.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(request)) => request,
|
||||
Ok(None) => {
|
||||
info!("firmware-loader: scheme unmounted, exiting");
|
||||
break;
|
||||
while let Some(request) = socket
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("firmware-loader: failed to read scheme request")
|
||||
{
|
||||
match request.kind() {
|
||||
redox_scheme::RequestKind::Call(request) => {
|
||||
let mut state = redox_scheme::scheme::SchemeState::new();
|
||||
let response = request.handle_sync(&mut scheme, &mut state);
|
||||
socket
|
||||
.write_response(response, SignalBehavior::Restart)
|
||||
.expect("firmware-loader: failed to write response");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("firmware-loader: failed to read scheme request: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let response = match request.handle_scheme_block_mut(&mut firmware_scheme) {
|
||||
Ok(response) => response,
|
||||
Err(_request) => {
|
||||
error!("firmware-loader: failed to handle request");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
|
||||
error!("firmware-loader: failed to write response: {}", e);
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -99,8 +107,26 @@ fn main() {
|
||||
|
||||
init_logging(log_level);
|
||||
|
||||
if let Err(e) = run() {
|
||||
error!("firmware-loader: fatal error: {}", e);
|
||||
let firmware_dir = env::var("FIRMWARE_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| default_firmware_dir());
|
||||
|
||||
info!(
|
||||
"firmware-loader: starting with directory {}",
|
||||
firmware_dir.display()
|
||||
);
|
||||
|
||||
let registry = FirmwareRegistry::new(&firmware_dir).unwrap_or_else(|e| {
|
||||
error!("firmware-loader: fatal error: failed to initialize firmware registry: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
info!(
|
||||
"firmware-loader: indexed {} firmware blob(s) from {}",
|
||||
registry.len(),
|
||||
firmware_dir.display()
|
||||
);
|
||||
|
||||
let notify_fd = unsafe { get_init_notify_fd() };
|
||||
run_daemon(notify_fd, registry);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,19 @@ use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::warn;
|
||||
use redox_scheme::SchemeBlockMut;
|
||||
use syscall04::data::Stat;
|
||||
use syscall04::error::{Error, Result, EBADF, EINVAL, EISDIR, ENOENT, EROFS};
|
||||
use syscall04::flag::{EventFlags, MapFlags, MunmapFlags, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use syscall::error::*;
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{EventFlags, Stat, MODE_FILE};
|
||||
|
||||
use crate::blob::FirmwareRegistry;
|
||||
|
||||
const SCHEME_ROOT_ID: usize = 1;
|
||||
|
||||
struct Handle {
|
||||
blob_key: String,
|
||||
data: Arc<Vec<u8>>,
|
||||
offset: u64,
|
||||
map_count: usize,
|
||||
closed: bool,
|
||||
}
|
||||
@@ -27,10 +29,18 @@ impl FirmwareScheme {
|
||||
pub fn new(registry: FirmwareRegistry) -> Self {
|
||||
FirmwareScheme {
|
||||
registry,
|
||||
next_id: 0,
|
||||
next_id: SCHEME_ROOT_ID + 1,
|
||||
handles: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(&self, id: usize) -> Result<&Handle> {
|
||||
self.handles.get(&id).ok_or(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn handle_mut(&mut self, id: usize) -> Result<&mut Handle> {
|
||||
self.handles.get_mut(&id).ok_or(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_key(path: &str) -> Option<String> {
|
||||
@@ -65,8 +75,23 @@ fn resolve_key(path: &str) -> Option<String> {
|
||||
Some(key)
|
||||
}
|
||||
|
||||
impl SchemeBlockMut for FirmwareScheme {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
impl SchemeSync for FirmwareScheme {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
Ok(SCHEME_ROOT_ID)
|
||||
}
|
||||
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
if dirfd != SCHEME_ROOT_ID {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let key = resolve_key(path).ok_or(Error::new(EISDIR))?;
|
||||
|
||||
if !self.registry.contains(&key) {
|
||||
@@ -87,94 +112,95 @@ impl SchemeBlockMut for FirmwareScheme {
|
||||
Handle {
|
||||
blob_key: key,
|
||||
data,
|
||||
offset: 0,
|
||||
map_count: 0,
|
||||
closed: false,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(Some(id))
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
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 = handle.data.len() as i64;
|
||||
let new_offset = match whence {
|
||||
SEEK_SET => pos as i64,
|
||||
SEEK_CUR => handle.offset as i64 + pos as i64,
|
||||
SEEK_END => len + pos as i64,
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
if new_offset < 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
handle.offset = new_offset as u64;
|
||||
let new_offset = isize::try_from(new_offset).map_err(|_| Error::new(EINVAL))?;
|
||||
Ok(Some(new_offset))
|
||||
}
|
||||
|
||||
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 offset = handle.offset as usize;
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.handle(id)?;
|
||||
let offset = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
|
||||
let data = &handle.data;
|
||||
|
||||
if offset >= data.len() {
|
||||
return Ok(Some(0));
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let available = data.len() - offset;
|
||||
let to_copy = available.min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&data[offset..offset + to_copy]);
|
||||
handle.offset += to_copy as u64;
|
||||
|
||||
Ok(Some(to_copy))
|
||||
Ok(to_copy)
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, _buf: &[u8]) -> Result<Option<usize>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
_buf: &[u8],
|
||||
_offset: u64,
|
||||
_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let _ = self.handle(id)?;
|
||||
Err(Error::new(EROFS))
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
let handle = self.handle(id)?;
|
||||
let path = format!("firmware:/{}.bin", handle.blob_key);
|
||||
let bytes = path.as_bytes();
|
||||
let len = bytes.len().min(buf.len());
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(Some(len))
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
let handle = self.handle(id)?;
|
||||
stat.st_mode = MODE_FILE | 0o444;
|
||||
stat.st_size = handle.data.len() as u64;
|
||||
stat.st_blksize = 4096;
|
||||
stat.st_blocks = (handle.data.len() as u64 + 511) / 512;
|
||||
Ok(Some(0))
|
||||
stat.st_nlink = 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
if !self.handles.contains_key(&id) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
Ok(Some(0))
|
||||
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
let _ = self.handle(id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags) -> Result<Option<EventFlags>> {
|
||||
if !self.handles.contains_key(&id) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
Ok(Some(EventFlags::empty()))
|
||||
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
let _ = self.handle(id)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.closed = true;
|
||||
let should_remove = handle.map_count == 0;
|
||||
if should_remove {
|
||||
self.handles.remove(&id);
|
||||
}
|
||||
Ok(Some(0))
|
||||
fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result<u64> {
|
||||
let handle = self.handle(id)?;
|
||||
Ok(handle.data.len() as u64)
|
||||
}
|
||||
|
||||
fn ftruncate(&mut self, id: usize, _len: u64, _ctx: &CallerCtx) -> Result<()> {
|
||||
let _ = self.handle(id)?;
|
||||
Err(Error::new(EROFS))
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
|
||||
let _ = self.handle(id)?;
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn mmap_prep(
|
||||
@@ -182,9 +208,10 @@ impl SchemeBlockMut for FirmwareScheme {
|
||||
id: usize,
|
||||
offset: u64,
|
||||
size: usize,
|
||||
_flags: MapFlags,
|
||||
) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
_flags: syscall::MapFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let handle = self.handle_mut(id)?;
|
||||
let data_len = handle.data.len() as u64;
|
||||
|
||||
if offset > data_len {
|
||||
@@ -196,7 +223,7 @@ impl SchemeBlockMut for FirmwareScheme {
|
||||
|
||||
let ptr = &handle.data[offset as usize] as *const u8;
|
||||
handle.map_count += 1;
|
||||
Ok(Some(ptr as usize))
|
||||
Ok(ptr as usize)
|
||||
}
|
||||
|
||||
fn munmap(
|
||||
@@ -204,9 +231,10 @@ impl SchemeBlockMut for FirmwareScheme {
|
||||
id: usize,
|
||||
_offset: u64,
|
||||
_size: usize,
|
||||
_flags: MunmapFlags,
|
||||
) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
_flags: syscall::MunmapFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<()> {
|
||||
let handle = self.handle_mut(id)?;
|
||||
if handle.map_count > 0 {
|
||||
handle.map_count -= 1;
|
||||
}
|
||||
@@ -214,6 +242,20 @@ impl SchemeBlockMut for FirmwareScheme {
|
||||
if should_cleanup {
|
||||
self.handles.remove(&id);
|
||||
}
|
||||
Ok(Some(0))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
if id == SCHEME_ROOT_ID {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(handle) = self.handles.get_mut(&id) {
|
||||
handle.closed = true;
|
||||
let should_remove = handle.map_count == 0;
|
||||
if should_remove {
|
||||
self.handles.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user