b9874d0941
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.
170 lines
5.5 KiB
Rust
170 lines
5.5 KiB
Rust
use common::{io::Io, timeout::Timeout};
|
|
use embedded_hal::blocking::i2c::{self, Operation, SevenBitAddress, Transactional};
|
|
|
|
use super::ddi::*;
|
|
|
|
pub struct Aux<'a> {
|
|
ddi: &'a mut Ddi,
|
|
}
|
|
|
|
impl<'a> Aux<'a> {
|
|
pub fn new(ddi: &'a mut Ddi) -> Self {
|
|
Self { ddi }
|
|
}
|
|
}
|
|
|
|
impl<'a> Transactional for Aux<'a> {
|
|
type Error = ();
|
|
fn exec(&mut self, addr7: SevenBitAddress, full_ops: &mut [Operation<'_>]) -> Result<(), ()> {
|
|
// Break ops into 16-byte chunks that will fit into aux data
|
|
let mut ops = Vec::new();
|
|
for op in full_ops.iter_mut() {
|
|
match op {
|
|
Operation::Read(buf) => {
|
|
for chunk in buf.chunks_mut(16) {
|
|
ops.push(Operation::Read(chunk));
|
|
}
|
|
}
|
|
Operation::Write(buf) => {
|
|
for chunk in buf.chunks(16) {
|
|
ops.push(Operation::Write(chunk));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let ops_len = ops.len();
|
|
for (i, op) in ops.iter_mut().enumerate() {
|
|
// Write header and data
|
|
let mut header = 0;
|
|
match op {
|
|
Operation::Read(_) => {
|
|
header |= 1 << 4;
|
|
}
|
|
Operation::Write(_) => (),
|
|
}
|
|
if (i + 1) < ops_len {
|
|
// Middle of transaction
|
|
header |= 1 << 6;
|
|
}
|
|
let mut aux_datas = [0u8; 20];
|
|
let mut aux_data_i = 0;
|
|
aux_datas[aux_data_i] = header;
|
|
aux_data_i += 1;
|
|
//TODO: what is this byte?
|
|
aux_datas[aux_data_i] = 0;
|
|
aux_data_i += 1;
|
|
aux_datas[aux_data_i] = addr7;
|
|
aux_data_i += 1;
|
|
match op {
|
|
Operation::Read(buf) => {
|
|
if !buf.is_empty() {
|
|
aux_datas[aux_data_i] = (buf.len() - 1) as u8;
|
|
aux_data_i += 1;
|
|
}
|
|
}
|
|
Operation::Write(buf) => {
|
|
if !buf.is_empty() {
|
|
aux_datas[aux_data_i] = (buf.len() - 1) as u8;
|
|
aux_data_i += 1;
|
|
for b in buf.iter() {
|
|
aux_datas[aux_data_i] = *b;
|
|
aux_data_i += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write data to registers (big endian, dword access only)
|
|
for (i, chunk) in aux_datas.chunks(4).enumerate() {
|
|
let mut bytes = [0; 4];
|
|
bytes[..chunk.len()].copy_from_slice(&chunk);
|
|
self.ddi.aux_datas[i].write(u32::from_be_bytes(bytes));
|
|
}
|
|
|
|
let mut v = self.ddi.aux_ctl.read();
|
|
// Set length
|
|
v &= !DDI_AUX_CTL_SIZE_MASK;
|
|
v |= (aux_data_i as u32) << DDI_AUX_CTL_SIZE_SHIFT;
|
|
// Set timeout
|
|
v &= !DDI_AUX_CTL_TIMEOUT_MASK;
|
|
v |= DDI_AUX_CTL_TIMEOUT_4000US;
|
|
// Set I/O select to legacy (cleared)
|
|
//TODO: TBT support?
|
|
v &= !DDI_AUX_CTL_IO_SELECT;
|
|
// Start transaction
|
|
v |= DDI_AUX_CTL_BUSY;
|
|
self.ddi.aux_ctl.write(v);
|
|
|
|
// Wait while busy
|
|
let timeout = Timeout::from_secs(1);
|
|
while self.ddi.aux_ctl.readf(DDI_AUX_CTL_BUSY) {
|
|
timeout.run().map_err(|()| {
|
|
log::debug!(
|
|
"AUX I2C transaction wait timeout 0x{:08X}",
|
|
self.ddi.aux_ctl.read()
|
|
);
|
|
()
|
|
})?;
|
|
}
|
|
|
|
// Read result
|
|
v = self.ddi.aux_ctl.read();
|
|
if (v & DDI_AUX_CTL_TIMEOUT_ERROR) != 0 {
|
|
log::debug!("AUX I2C transaction timeout error");
|
|
return Err(());
|
|
}
|
|
if (v & DDI_AUX_CTL_RECEIVE_ERROR) != 0 {
|
|
log::debug!("AUX I2C transaction receive error");
|
|
return Err(());
|
|
}
|
|
if (v & DDI_AUX_CTL_DONE) == 0 {
|
|
log::debug!("AUX I2C transaction done not set");
|
|
return Err(());
|
|
}
|
|
|
|
// Read data from registers (big endian, dword access only)
|
|
for (i, chunk) in aux_datas.chunks_mut(4).enumerate() {
|
|
let bytes = self.ddi.aux_datas[i].read().to_be_bytes();
|
|
chunk.copy_from_slice(&bytes[..chunk.len()]);
|
|
}
|
|
|
|
aux_data_i = 0;
|
|
let response = aux_datas[aux_data_i];
|
|
if response != 0 {
|
|
log::debug!("AUX I2C unexpected response {:02X}", response);
|
|
return Err(());
|
|
}
|
|
aux_data_i += 1;
|
|
match op {
|
|
Operation::Read(buf) => {
|
|
if !buf.is_empty() {
|
|
for b in buf.iter_mut() {
|
|
*b = aux_datas[aux_data_i];
|
|
aux_data_i += 1;
|
|
}
|
|
}
|
|
}
|
|
Operation::Write(_) => (),
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> i2c::WriteRead for Aux<'a> {
|
|
type Error = ();
|
|
fn write_read(
|
|
&mut self,
|
|
addr7: SevenBitAddress,
|
|
bytes: &[u8],
|
|
buffer: &mut [u8],
|
|
) -> Result<(), ()> {
|
|
self.exec(
|
|
addr7,
|
|
&mut [Operation::Write(bytes), Operation::Read(buffer)],
|
|
)
|
|
}
|
|
}
|