Files
RedBear-OS/recipes/drivers/storage/usbscsid/src/main.rs
T
vasilito b9874d0941 feat: USB storage read/write proof + full Red Bear OS tree sync
Add redbear-usb-storage-check in-guest binary that validates USB mass
storage read and write I/O: discovers /scheme/disk/ devices, writes a
test pattern to sector 2048, reads it back, verifies match, restores
original content. Updates test-usb-storage-qemu.sh with write-proof
verification step.

Includes all accumulated Red Bear OS work: kernel patches, relibc
patches, driver infrastructure, DRM/GPU, KDE recipes, firmware,
validation tooling, build system hardening, and documentation.
2026-05-03 23:03:24 +01:00

169 lines
4.9 KiB
Rust

use std::collections::BTreeMap;
use std::env;
use driver_block::{Disk, DiskScheme, ExecutorTrait};
use syscall::{Error, EIO};
use xhcid_interface::{ConfigureEndpointsReq, PortId, XhciClientHandle};
pub mod protocol;
pub mod scsi;
use crate::protocol::Protocol;
use crate::scsi::Scsi;
fn main() {
daemon::Daemon::new(daemon);
}
fn daemon(daemon: daemon::Daemon) -> ! {
let mut args = env::args().skip(1);
const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";
let scheme = args.next().expect(USAGE);
let port = args
.next()
.expect(USAGE)
.parse::<PortId>()
.expect("Expected port ID");
let protocol = args
.next()
.expect(USAGE)
.parse::<u8>()
.expect("protocol has to be a number 0-255");
println!(
"USB SCSI driver spawned with scheme `{}`, port {}, protocol {}",
scheme, port, protocol
);
let disk_scheme_name = format!("disk.usb-{scheme}+{port}-scsi");
// TODO: Use eventfds.
let handle =
XhciClientHandle::new(scheme.to_owned(), port).expect("Failed to open XhciClientHandle");
let desc = handle
.get_standard_descs()
.expect("Failed to get standard descriptors");
// TODO: Perhaps the drivers should just be given the config, interface, and alternate setting
// from xhcid.
let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc
.config_descs
.iter()
.find_map(|config_desc| {
let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| {
if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 {
Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting))
} else {
None
}
})?;
Some((
config_desc.clone(),
config_desc.configuration_value,
interface_desc,
))
})
.expect("Failed to find suitable configuration");
handle
.configure_endpoints(&ConfigureEndpointsReq {
config_desc: configuration_value,
interface_desc: Some(interface_num),
alternate_setting: Some(alternate_setting),
hub_ports: None,
})
.expect("Failed to configure endpoints");
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
.expect("Failed to setup protocol");
// TODO: Let all of the USB drivers fork or be managed externally, and xhcid won't have to keep
// track of all the drivers.
let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
println!("SCSI initialized");
let mut buffer = [0u8; 512];
scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
let event_queue = event::EventQueue::new().unwrap();
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
None,
disk_scheme_name,
BTreeMap::from([(
0,
UsbDisk {
scsi: &mut scsi,
protocol: &mut *protocol,
},
)]),
&driver_block::FuturesExecutor,
);
// FIXME should this wait notifying readiness until the disk scheme is created?
daemon.ready();
//libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
event_queue
.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
)
.unwrap();
for event in event_queue {
match event.unwrap().user_data {
Event::Scheme => driver_block::FuturesExecutor
.block_on(scheme.tick())
.unwrap(),
}
}
std::process::exit(0);
}
struct UsbDisk<'a> {
scsi: &'a mut Scsi,
protocol: &'a mut dyn Protocol,
}
impl Disk for UsbDisk<'_> {
fn block_size(&self) -> u32 {
self.scsi.block_size
}
fn size(&self) -> u64 {
self.scsi.get_disk_size()
}
async fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
match self.scsi.read(self.protocol, block, buffer) {
Ok(bytes_read) => Ok(bytes_read as usize),
Err(err) => {
eprintln!("usbscsid: READ IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
async fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<usize> {
match self.scsi.write(self.protocol, block, buffer) {
Ok(bytes_written) => Ok(bytes_written as usize),
Err(err) => {
eprintln!("usbscsid: WRITE IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
}