Quellcode durchsuchen

Test VirtIO MMIO devices in aarch64 example.

Andrew Walbran vor 2 Jahren
Ursprung
Commit
f244b2495f

+ 1 - 0
examples/aarch64/Cargo.toml

@@ -6,6 +6,7 @@ edition = "2021"
 
 [dependencies]
 fdt = "0.1.4"
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
 log = "0.4.17"
 psci = "0.1.1"
 spin = "0.9.4"

+ 10 - 2
examples/aarch64/Makefile

@@ -2,6 +2,7 @@ target := aarch64-unknown-none
 mode := release
 kernel := target/$(target)/$(mode)/aarch64
 kernel_bin := target/$(target)/$(mode)/aarch64.bin
+img := target/$(target)/$(mode)/img
 
 sysroot := $(shell rustc --print sysroot)
 objdump := $(shell find $(sysroot) -name llvm-objdump) --arch-name=aarch64
@@ -36,11 +37,18 @@ header: kernel
 clean:
 	cargo clean
 
-qemu: $(kernel_bin)
+qemu: $(kernel_bin) $(img)
 	qemu-system-aarch64 \
 		-machine virt \
 		-cpu max \
 		-serial mon:stdio \
-		-kernel $(kernel_bin)
+		-kernel $(kernel_bin) \
+		-global virtio-mmio.force-legacy=false \
+		-drive file=$(img),if=none,format=raw,id=x0 \
+		-device virtio-blk-device,drive=x0 \
+		-device virtio-gpu-device
+
+$(img):
+	dd if=/dev/zero of=$@ bs=512 count=32
 
 run: qemu

+ 3 - 0
examples/aarch64/image.ld

@@ -70,6 +70,9 @@ SECTIONS
 		boot_stack_end = .;
 	} >image
 
+	. = ALIGN(4K);
+	PROVIDE(dma_region = .);
+
 	/*
 	 * Remove unused sections from the image.
 	 */

+ 38 - 0
examples/aarch64/src/hal.rs

@@ -0,0 +1,38 @@
+use core::sync::atomic::*;
+use lazy_static::lazy_static;
+use log::trace;
+use virtio_drivers::{Hal, PhysAddr, VirtAddr};
+
+const PAGE_SIZE: usize = 0x1000;
+
+extern "C" {
+    static dma_region: u8;
+}
+
+lazy_static! {
+    static ref DMA_PADDR: AtomicUsize =
+        AtomicUsize::new(unsafe { &dma_region as *const u8 as usize });
+}
+
+pub struct HalImpl;
+
+impl Hal for HalImpl {
+    fn dma_alloc(pages: usize) -> PhysAddr {
+        let paddr = DMA_PADDR.fetch_add(PAGE_SIZE * pages, Ordering::SeqCst);
+        trace!("alloc DMA: paddr={:#x}, pages={}", paddr, pages);
+        paddr
+    }
+
+    fn dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
+        trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages);
+        0
+    }
+
+    fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
+        paddr
+    }
+
+    fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
+        vaddr
+    }
+}

+ 84 - 2
examples/aarch64/src/main.rs

@@ -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);