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.
159 lines
5.1 KiB
Rust
159 lines
5.1 KiB
Rust
use std::fs::File;
|
|
use std::sync::Arc;
|
|
|
|
use pcid_interface::*;
|
|
|
|
use crate::spec::*;
|
|
use crate::transport::{Error, StandardTransport, Transport};
|
|
use crate::utils::align_down;
|
|
|
|
pub struct Device {
|
|
pub transport: Arc<dyn Transport>,
|
|
pub device_space: *const u8,
|
|
pub irq_handle: File,
|
|
}
|
|
|
|
// FIXME(andypython): `device_space` should not be `Send` nor `Sync`. Take
|
|
// it out of `Device`.
|
|
unsafe impl Send for Device {}
|
|
unsafe impl Sync for Device {}
|
|
|
|
pub const MSIX_PRIMARY_VECTOR: u16 = 0;
|
|
|
|
/// VirtIO Device Probe
|
|
///
|
|
/// ## Device State
|
|
/// After this function, the device will have been successfully reseted and is ready for use.
|
|
///
|
|
/// The caller is required to do the following:
|
|
/// * Negotiate the device and driver supported features (finialize via [`StandardTransport::finalize_features`])
|
|
/// * Create the device specific virtio queues (via [`StandardTransport::setup_queue`]). This is *required* to be done
|
|
/// before starting the device.
|
|
/// * Finally start the device (via [`StandardTransport::run_device`]). At this point, the device
|
|
/// is alive.
|
|
///
|
|
/// ## Panics
|
|
/// This function panics if the device is not a virtio device.
|
|
pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result<Device, Error> {
|
|
let pci_config = pcid_handle.config();
|
|
|
|
assert_eq!(
|
|
pci_config.func.full_device_id.vendor_id, 6900,
|
|
"virtio_core::probe_device: not a virtio device"
|
|
);
|
|
|
|
let mut common_addr = None;
|
|
let mut notify_addr = None;
|
|
let mut device_addr = None;
|
|
|
|
for raw_capability in pcid_handle.get_vendor_capabilities() {
|
|
// SAFETY: We have verified that the length of the data is correct.
|
|
let capability = unsafe { &*(raw_capability.data.as_ptr() as *const PciCapability) };
|
|
|
|
match capability.cfg_type {
|
|
CfgType::Common | CfgType::Notify | CfgType::Device => {}
|
|
_ => continue,
|
|
}
|
|
|
|
let (addr, _) = pci_config.func.bars[capability.bar as usize].expect_mem();
|
|
|
|
let address = unsafe {
|
|
let addr = addr + capability.offset as usize;
|
|
|
|
// XXX: physmap() requires the address to be page aligned.
|
|
let aligned_addr = align_down(addr);
|
|
let offset = addr - aligned_addr;
|
|
|
|
let size = offset + capability.length as usize;
|
|
|
|
let addr = common::physmap(
|
|
aligned_addr,
|
|
size,
|
|
common::Prot::RW,
|
|
common::MemoryType::Uncacheable,
|
|
)? as usize;
|
|
|
|
addr + offset
|
|
};
|
|
|
|
match capability.cfg_type {
|
|
CfgType::Common => {
|
|
debug_assert!(common_addr.is_none());
|
|
common_addr = Some(address);
|
|
}
|
|
|
|
CfgType::Notify => {
|
|
debug_assert!(notify_addr.is_none());
|
|
|
|
// SAFETY: The capability type is `Notify`, so its safe to access
|
|
// the `notify_multiplier` field.
|
|
let multiplier = unsafe {
|
|
(&*(raw_capability.data.as_ptr() as *const PciCapability
|
|
as *const PciCapabilityNotify))
|
|
.notify_off_multiplier()
|
|
};
|
|
notify_addr = Some((address, multiplier));
|
|
}
|
|
|
|
CfgType::Device => {
|
|
debug_assert!(device_addr.is_none());
|
|
device_addr = Some(address);
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
let common_addr = common_addr.expect("virtio common capability missing");
|
|
let device_addr = device_addr.expect("virtio device capability missing");
|
|
let (notify_addr, notify_multiplier) = notify_addr.expect("virtio notify capability missing");
|
|
|
|
// FIXME this is explicitly allowed by the virtio specification to happen
|
|
assert!(
|
|
notify_multiplier != 0,
|
|
"virtio-core::device_probe: device uses the same Queue Notify addresses for all queues"
|
|
);
|
|
|
|
let common = unsafe { &mut *(common_addr as *mut CommonCfg) };
|
|
let device_space = unsafe { &mut *(device_addr as *mut u8) };
|
|
|
|
let transport = StandardTransport::new(
|
|
common,
|
|
notify_addr as *const u8,
|
|
notify_multiplier,
|
|
device_space,
|
|
);
|
|
|
|
// Setup interrupts.
|
|
let all_pci_features = pcid_handle.fetch_all_features();
|
|
let has_msix = all_pci_features.iter().any(|feature| feature.is_msix());
|
|
|
|
// According to the virtio specification, the device REQUIRED to support MSI-X.
|
|
assert!(has_msix, "virtio: device does not support MSI-X");
|
|
let irq_handle = crate::arch::enable_msix(pcid_handle)?;
|
|
|
|
log::debug!("virtio: using standard PCI transport");
|
|
|
|
let device = Device {
|
|
transport,
|
|
device_space,
|
|
irq_handle,
|
|
};
|
|
|
|
device.transport.reset();
|
|
reinit(&device)?;
|
|
|
|
Ok(device)
|
|
}
|
|
|
|
pub fn reinit(device: &Device) -> Result<(), Error> {
|
|
// XXX: According to the virtio specification v1.2, setting the ACKNOWLEDGE and DRIVER bits
|
|
// in `device_status` is required to be done in two steps.
|
|
device
|
|
.transport
|
|
.insert_status(DeviceStatusFlags::ACKNOWLEDGE);
|
|
|
|
device.transport.insert_status(DeviceStatusFlags::DRIVER);
|
|
Ok(())
|
|
}
|