//! virtio devices //! WARNING: virtio is currently completely broken! don't use it! use core::sync::atomic::Ordering; use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP}; use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; use crate::rough_panic; use crate::spinlock::Spinlock; use crate::strprint::twodigit; use crate::trafficcontrol::{TrafficControl}; mod block; mod gpu; pub const VIRTIO_MMIO_START: usize = 0x1000_1000; pub const VIRTIO_MMIO_END: usize = 0x1000_8000; pub const VIRTIO_MMIO_DEVSIZE: usize = 0x1000; pub const VIRTIO_MMIO_DEVCOUNT: usize = ((VIRTIO_MMIO_END - VIRTIO_MMIO_START) / VIRTIO_MMIO_DEVSIZE) + 1; pub const VIRTIO_MMIO_STATUS: usize = 0x70; pub const VIRTIO_MMIO_HOST_FEATURES: usize = 0x10; pub const VIRTIO_MMIO_GUEST_FEATURES: usize = 0x20; pub const VIRTIO_MMIO_GUEST_PAGE_SIZE: usize = 0x28; pub const VIRTIO_MMIO_QUEUE_SEL: usize = 0x30; pub const VIRTIO_MMIO_QUEUE_NUM_MAX: usize = 0x34; pub const VIRTIO_MMIO_QUEUE_NUM: usize = 0x38; pub const VIRTIO_MMIO_QUEUE_PFN: usize = 0x40; pub const VIRTIO_MMIO_QUEUE_NOTIFY: usize = 0x50; pub const VIRTIO_MMIO_STATUS_ACKNOWLEDGE: u32 = 1 << 0; pub const VIRTIO_MMIO_STATUS_DRIVER: u32 = 1 << 1; pub const VIRTIO_MMIO_STATUS_DRIVER_OK: u32 = 1 << 2; pub const VIRTIO_MMIO_STATUS_FEATURES_OK: u32 = 1 << 3; pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7; pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0; pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1; pub const VIRTIO_QUEUE_SIZE: usize = 16; pub static VIRTIO_DEVICES: Spinlock<[Option; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]); pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock> = Spinlock::new(None); pub static VIRTIO_GPU_DEVICE: Spinlock> = Spinlock::new(None); pub enum VirtIoDevice { BlockDevice(VirtIoBlockDevice), GPUDevice(VirtIoGpuDevice), } #[repr(C)] pub struct Descriptor { // 16 bytes pub addr: u64, pub len: u32, pub flags: u16, pub next: u16, } #[repr(C)] pub struct Available { // 14 bytes pub flags: u16, pub idx: u16, pub ring: [u16; VIRTIO_QUEUE_SIZE], pub event: u16, } #[repr(C)] pub struct UsedElem { // 8 bytes pub id: u32, pub len: u32, } #[repr(C)] pub struct Used { // 38 bytes pub flags: u16, pub idx: u16, pub ring: [UsedElem; VIRTIO_QUEUE_SIZE], pub event: u16, } #[repr(C)] pub struct VirtQueue { pub desc: [Descriptor; VIRTIO_QUEUE_SIZE], pub avail: Available, pub pad: [u8; 4096 - (size_of::() * VIRTIO_QUEUE_SIZE) - size_of::()], pub used: Used, } pub fn heap_allocate_type(tc: &mut TrafficControl) -> &'static mut T { let num_blocks = size_of::().div_ceil(512); if num_blocks != 1 { rough_panic(['f', 'm', 'l']); } let addr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; unsafe { &mut (*(addr as *mut T)) } } pub fn probe_virtio_devices(tc: &mut TrafficControl) { let serial_port = crate::arch::serial_port(); let mut devices = VIRTIO_DEVICES.lock(); for (i, addr) in (VIRTIO_MMIO_START..=VIRTIO_MMIO_END).step_by(VIRTIO_MMIO_DEVSIZE).enumerate() { let magic = unsafe { (addr as *const u32).read_volatile() }; const VIRTIO_MAGIC: u32 = 0x7472_6976; if magic != VIRTIO_MAGIC { continue; } let device_type = unsafe { ((addr + 8) as *const u32).read_volatile() }; if device_type == 0 { // not connected continue; } #[allow(clippy::single_match)] match device_type { 2 => { // block device let block_device = VirtIoBlockDevice::new_and_init(tc, addr); if let Ok(block_device) = block_device { devices[i] = Some(VirtIoDevice::BlockDevice(block_device)); *VIRTIO_HARD_BLOCK_DEVICE.lock() = Some(i as u8); if let Some(serial_port) = &serial_port { serial_port.putstr("virtio block device found\n"); } } else if let Err(e) = block_device { match e { VirtIoBlockDeviceError::FeatureSetMismatch => { if let Some(serial_port) = &serial_port { serial_port.putstr("virtio block device feature mismatch\n"); } } VirtIoBlockDeviceError::QueueSetupFailed => { if let Some(serial_port) = &serial_port { serial_port.putstr("virtio block device queue setup failed\n"); } } } } } 16 => { // gpu device let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr); if let Ok(gpu_device) = gpu_device { FRAMEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); FRAMEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device)); *VIRTIO_GPU_DEVICE.lock() = Some(i as u8); if let Some(serial_port) = &serial_port { serial_port.putstr("virtio gpu device found\n"); } } else if let Err(e) = gpu_device { match e { VirtIoGpuDeviceError::FeatureSetMismatch => { if let Some(serial_port) = &serial_port { serial_port.putstr("virtio gpu device feature mismatch\n"); } } VirtIoGpuDeviceError::QueueSetupFailed => { if let Some(serial_port) = &serial_port { serial_port.putstr("virtio gpu device queue setup failed\n"); } } } } } x => { if let Some(serial_port) = &serial_port { serial_port.putstr("unsupported device type "); serial_port.put_bytes(&twodigit(x as u8)); serial_port.putstr("\n"); } } // don't handle unsupported devices yet } } } pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { let idx = interrupt as usize - 1; let mut devices = VIRTIO_DEVICES.lock(); if let Some(Some(dev)) = devices.get_mut(idx) { match dev { VirtIoDevice::BlockDevice(blockdev) => { let hbd = { let lock = VIRTIO_HARD_BLOCK_DEVICE.lock(); *lock }; if let Some(hbd) = hbd { if hbd as usize == idx { blockdev.pending(tc); } } } VirtIoDevice::GPUDevice(gpudev) => { return; let gpu = { let lock = VIRTIO_GPU_DEVICE.lock(); *lock }; if let Some(gpu) = gpu { if gpu as usize == idx { gpudev.pending(tc); } } } } } } pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u64) -> bool { let idx = { let lock = VIRTIO_HARD_BLOCK_DEVICE.lock(); *lock }; let mut devices = VIRTIO_DEVICES.lock(); if let Some(idx) = idx { if let Some(device) = devices[idx as usize].as_mut() { return match device { VirtIoDevice::BlockDevice(device) => { device.operation(tc, buffer, size, sector, false); true } _ => { if let Some(serial_port) = &crate::arch::serial_port() { serial_port.putstr("unexpected device type (not block)\n"); } false } }; } else { let uart = crate::uart::UART::new(0x1000_0000); uart.putstr("no device") } } else { let uart = crate::uart::UART::new(0x1000_0000); uart.putstr("no idx") } false } pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, w: u32, h: u32) -> bool { let idx = { let lock = VIRTIO_GPU_DEVICE.lock(); *lock }; let mut devices = VIRTIO_DEVICES.lock(); if let Some(idx) = idx { if let Some(device) = devices[idx as usize].as_mut() { return match device { VirtIoDevice::GPUDevice(device) => { device.transfer(tc, x, y, w, h); true } _ => { if let Some(serial_port) = &crate::arch::serial_port() { serial_port.putstr("unexpected device type (not gpu)\n"); } false } } } else { let uart = crate::uart::UART::new(0x1000_0000); uart.putstr("no device") } } else { let uart = crate::uart::UART::new(0x1000_0000); uart.putstr("no idx") } false }