#![no_std] #![no_main] #![deny(warnings)] #[macro_use] extern crate log; extern crate alloc; extern crate opensbi_rt; use alloc::vec; use core::ptr::NonNull; use fdt::{node::FdtNode, standard_nodes::Compatible, Fdt}; use log::LevelFilter; use virtio_drivers::{ device::{blk::VirtIOBlk, gpu::VirtIOGpu, input::VirtIOInput, net::VirtIONet}, transport::{ mmio::{MmioTransport, VirtIOHeader}, DeviceType, Transport, }, }; use virtio_impl::HalImpl; mod virtio_impl; #[cfg(feature = "tcp")] mod tcp; const NET_BUFFER_LEN: usize = 2048; const NET_QUEUE_SIZE: usize = 16; #[no_mangle] extern "C" fn main(_hartid: usize, device_tree_paddr: usize) { log::set_max_level(LevelFilter::Info); init_dt(device_tree_paddr); info!("test end"); } fn init_dt(dtb: usize) { info!("device tree @ {:#x}", dtb); // Safe because the pointer is a valid pointer to unaliased memory. let fdt = unsafe { Fdt::from_ptr(dtb as *const u8).unwrap() }; walk_dt(fdt); } fn walk_dt(fdt: Fdt) { for node in fdt.all_nodes() { if let Some(compatible) = node.compatible() { if compatible.all().any(|s| s == "virtio,mmio") { virtio_probe(node); } } } } fn virtio_probe(node: FdtNode) { if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { let paddr = reg.starting_address as usize; let size = reg.size.unwrap(); let vaddr = paddr; info!("walk dt addr={:#x}, size={:#x}", paddr, size); info!( "Device tree node {}: {:?}", node.name, node.compatible().map(Compatible::first), ); let header = NonNull::new(vaddr as *mut VirtIOHeader).unwrap(); match unsafe { MmioTransport::new(header) } { Err(e) => warn!("Error creating VirtIO MMIO transport: {}", e), Ok(transport) => { info!( "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}", transport.vendor_id(), transport.device_type(), transport.version(), ); virtio_device(transport); } } } } fn virtio_device(transport: impl Transport) { match transport.device_type() { DeviceType::Block => virtio_blk(transport), DeviceType::GPU => virtio_gpu(transport), DeviceType::Input => virtio_input(transport), DeviceType::Network => virtio_net(transport), t => warn!("Unrecognized virtio device: {:?}", t), } } fn virtio_blk(transport: T) { let mut blk = VirtIOBlk::::new(transport).expect("failed to create blk driver"); let mut input = vec![0xffu8; 512]; let mut output = vec![0; 512]; for i in 0..32 { for x in input.iter_mut() { *x = i as u8; } blk.write_block(i, &input).expect("failed to write"); blk.read_block(i, &mut output).expect("failed to read"); assert_eq!(input, output); } info!("virtio-blk test finished"); } fn virtio_gpu(transport: T) { let mut gpu = VirtIOGpu::::new(transport).expect("failed to create gpu driver"); let (width, height) = gpu.resolution().expect("failed to get resolution"); let width = width as usize; let height = height as usize; info!("GPU resolution is {}x{}", width, height); let fb = gpu.setup_framebuffer().expect("failed to get fb"); for y in 0..height { for x in 0..width { let idx = (y * width + x) * 4; fb[idx] = x as u8; fb[idx + 1] = y as u8; fb[idx + 2] = (x + y) as u8; } } gpu.flush().expect("failed to flush"); //delay some time info!("virtio-gpu show graphics...."); for _ in 0..10000 { for _ in 0..100000 { unsafe { core::arch::asm!("nop"); } } } info!("virtio-gpu test finished"); } fn virtio_input(transport: T) { //let mut event_buf = [0u64; 32]; let mut _input = VirtIOInput::::new(transport).expect("failed to create input driver"); // loop { // input.ack_interrupt().expect("failed to ack"); // info!("mouse: {:?}", input.mouse_xy()); // } // TODO: handle external interrupt } fn virtio_net(transport: T) { let net = VirtIONet::::new(transport, NET_BUFFER_LEN) .expect("failed to create net driver"); info!("MAC address: {:02x?}", net.mac_address()); #[cfg(not(feature = "tcp"))] { let mut net = net; loop { match net.receive() { Ok(buf) => { info!("RECV {} bytes: {:02x?}", buf.packet_len(), buf.packet()); let tx_buf = virtio_drivers::device::net::TxBuffer::from(buf.packet()); net.send(tx_buf).expect("failed to send"); net.recycle_rx_buffer(buf).unwrap(); break; } Err(virtio_drivers::Error::NotReady) => continue, Err(err) => panic!("failed to recv: {:?}", err), } } info!("virtio-net test finished"); } #[cfg(feature = "tcp")] tcp::test_echo_server(net); }