blob: c1c8ae66c3fadc440b1dea1da310b678565d6ddd [file] [log] [blame]
Andrew Walbran848decf2022-12-15 14:39:38 +00001use crate::memory::{alloc_shared, dealloc_shared, virt_to_phys};
2use core::ptr::{copy_nonoverlapping, NonNull};
3use log::debug;
4use virtio_drivers::{BufferDirection, Hal, PhysAddr, VirtAddr, PAGE_SIZE};
5
6pub struct HalImpl;
7
8impl Hal for HalImpl {
9 fn dma_alloc(pages: usize) -> PhysAddr {
10 debug!("dma_alloc: pages={}", pages);
11 let size = pages * PAGE_SIZE;
12 let vaddr = alloc_shared(size)
13 .expect("Failed to allocate and share VirtIO DMA range with host")
14 .as_ptr() as VirtAddr;
15 virt_to_phys(vaddr)
16 }
17
18 fn dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
19 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
20 let vaddr = Self::phys_to_virt(paddr);
21 let size = pages * PAGE_SIZE;
22 // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
23 // the layout is the same as was used then.
24 unsafe {
25 dealloc_shared(vaddr, size).expect("Failed to unshare VirtIO DMA range with host");
26 }
27 0
28 }
29
30 fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
31 paddr
32 }
33
34 fn share(buffer: NonNull<[u8]>, direction: BufferDirection) -> PhysAddr {
35 let size = buffer.len();
36
37 // TODO: Copy to a pre-shared region rather than allocating and sharing each time.
38 // Allocate a range of pages, copy the buffer if necessary, and share the new range instead.
39 let copy =
40 alloc_shared(size).expect("Failed to allocate and share VirtIO buffer with host");
41 if direction == BufferDirection::DriverToDevice {
42 unsafe {
43 copy_nonoverlapping(buffer.as_ptr() as *mut u8, copy.as_ptr(), size);
44 }
45 }
46 virt_to_phys(copy.as_ptr() as VirtAddr)
47 }
48
49 fn unshare(paddr: PhysAddr, buffer: NonNull<[u8]>, direction: BufferDirection) {
50 let vaddr = Self::phys_to_virt(paddr);
51 let size = buffer.len();
52 if direction == BufferDirection::DeviceToDriver {
53 debug!(
54 "Copying VirtIO buffer back from {:#x} to {:#x}.",
55 paddr,
56 buffer.as_ptr() as *mut u8 as usize
57 );
58 unsafe {
59 copy_nonoverlapping(vaddr as *const u8, buffer.as_ptr() as *mut u8, size);
60 }
61 }
62
63 // Unshare and deallocate the shared copy of the buffer.
64 debug!("Unsharing VirtIO buffer {:#x}", paddr);
65 // Safe because the memory was allocated by `share` using `alloc_shared`, and the size is
66 // the same as was used then.
67 unsafe {
68 dealloc_shared(vaddr, size).expect("Failed to unshare VirtIO buffer with host");
69 }
70 }
71}