|
@@ -2,13 +2,18 @@
|
|
|
#![no_main]
|
|
|
|
|
|
mod exceptions;
|
|
|
+mod hal;
|
|
|
mod logger;
|
|
|
mod pl011;
|
|
|
|
|
|
-use core::panic::PanicInfo;
|
|
|
+use core::{mem::size_of, panic::PanicInfo, ptr::NonNull};
|
|
|
use fdt::{standard_nodes::Compatible, Fdt};
|
|
|
-use log::{debug, error, info, trace, LevelFilter};
|
|
|
+use hal::HalImpl;
|
|
|
+use log::{debug, error, info, trace, warn, LevelFilter};
|
|
|
use psci::system_off;
|
|
|
+use virtio_drivers::{
|
|
|
+ DeviceType, MmioTransport, Transport, VirtIOBlk, VirtIOGpu, VirtIOHeader, VirtIONet,
|
|
|
+};
|
|
|
|
|
|
#[no_mangle]
|
|
|
extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
|
@@ -39,11 +44,88 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // Check whether it is a VirtIO MMIO device.
|
|
|
+ if let (Some(compatible), Some(region)) =
|
|
|
+ (node.compatible(), node.reg().and_then(|mut reg| reg.next()))
|
|
|
+ {
|
|
|
+ if compatible.all().any(|s| s == "virtio,mmio")
|
|
|
+ && region.size.unwrap_or(0) > size_of::<VirtIOHeader>()
|
|
|
+ {
|
|
|
+ debug!("Found VirtIO MMIO device at {:?}", region);
|
|
|
+
|
|
|
+ let header = NonNull::new(region.starting_address 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
system_off().unwrap();
|
|
|
}
|
|
|
|
|
|
+fn virtio_device(transport: impl Transport) {
|
|
|
+ match transport.device_type() {
|
|
|
+ DeviceType::Block => virtio_blk(transport),
|
|
|
+ DeviceType::GPU => virtio_gpu(transport),
|
|
|
+ DeviceType::Network => virtio_net(transport),
|
|
|
+ t => warn!("Unrecognized virtio device: {:?}", t),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn virtio_blk<T: Transport>(transport: T) {
|
|
|
+ let mut blk = VirtIOBlk::<HalImpl, T>::new(transport).expect("failed to create blk driver");
|
|
|
+ let mut input = [0xffu8; 512];
|
|
|
+ let mut output = [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<T: Transport>(transport: T) {
|
|
|
+ let mut gpu = VirtIOGpu::<HalImpl, T>::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");
|
|
|
+ info!("virtio-gpu test finished");
|
|
|
+}
|
|
|
+
|
|
|
+fn virtio_net<T: Transport>(transport: T) {
|
|
|
+ let mut net = VirtIONet::<HalImpl, T>::new(transport).expect("failed to create net driver");
|
|
|
+ let mut buf = [0u8; 0x100];
|
|
|
+ let len = net.recv(&mut buf).expect("failed to recv");
|
|
|
+ info!("recv: {:?}", &buf[..len]);
|
|
|
+ net.send(&buf[..len]).expect("failed to send");
|
|
|
+ info!("virtio-net test finished");
|
|
|
+}
|
|
|
+
|
|
|
#[panic_handler]
|
|
|
fn panic(info: &PanicInfo) -> ! {
|
|
|
error!("{}", info);
|