1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Functions to scan the PCI bus for VirtIO device.
16
17 use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout};
18 use core::{mem::size_of, ptr::NonNull};
19 use log::{debug, info};
20 use virtio_drivers::{
21 device::console::VirtIOConsole,
22 transport::{
23 pci::{
24 bus::{ConfigurationAccess, PciRoot},
25 PciTransport,
26 },
27 DeviceType, Transport,
28 },
29 BufferDirection, Error, Hal, PhysAddr, PAGE_SIZE,
30 };
31 use vmbase::virtio::pci::{self, PciTransportIterator};
32
33 /// The standard sector size of a VirtIO block device, in bytes.
34 const SECTOR_SIZE_BYTES: usize = 512;
35
36 /// The size in sectors of the test block device we expect.
37 const EXPECTED_SECTOR_COUNT: usize = 4;
38
check_pci(pci_root: &mut PciRoot<impl ConfigurationAccess>)39 pub fn check_pci(pci_root: &mut PciRoot<impl ConfigurationAccess>) {
40 let mut checked_virtio_device_count = 0;
41 let mut block_device_count = 0;
42 let mut socket_device_count = 0;
43 for mut transport in PciTransportIterator::<HalImpl, _>::new(pci_root) {
44 info!(
45 "Detected virtio PCI device with device type {:?}, features {:#018x}",
46 transport.device_type(),
47 transport.read_device_features(),
48 );
49 match transport.device_type() {
50 DeviceType::Block => {
51 check_virtio_block_device(transport, block_device_count);
52 block_device_count += 1;
53 checked_virtio_device_count += 1;
54 }
55 DeviceType::Console => {
56 check_virtio_console_device(transport);
57 checked_virtio_device_count += 1;
58 }
59 DeviceType::Socket => {
60 check_virtio_socket_device(transport);
61 socket_device_count += 1;
62 checked_virtio_device_count += 1;
63 }
64 _ => {}
65 }
66 }
67
68 assert_eq!(checked_virtio_device_count, 6);
69 assert_eq!(block_device_count, 2);
70 assert_eq!(socket_device_count, 1);
71 }
72
73 /// Checks the given VirtIO block device.
check_virtio_block_device(transport: PciTransport, index: usize)74 fn check_virtio_block_device(transport: PciTransport, index: usize) {
75 let mut blk = pci::VirtIOBlk::<HalImpl>::new(transport).expect("failed to create blk driver");
76 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
77 match index {
78 0 => {
79 assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
80 let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
81 for i in 0..EXPECTED_SECTOR_COUNT {
82 blk.read_blocks(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
83 .expect("Failed to read block device.");
84 }
85 for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
86 assert_eq!(chunk, &(i as u32).to_le_bytes());
87 }
88 info!("Read expected data from block device.");
89 }
90 1 => {
91 assert_eq!(blk.capacity(), 0);
92 let mut data = [0; SECTOR_SIZE_BYTES];
93 assert_eq!(blk.read_blocks(0, &mut data), Err(Error::IoError));
94 }
95 _ => panic!("Unexpected VirtIO block device index {}.", index),
96 }
97 }
98
99 /// Checks the given VirtIO socket device.
check_virtio_socket_device(transport: PciTransport)100 fn check_virtio_socket_device(transport: PciTransport) {
101 let socket = pci::VirtIOSocket::<HalImpl>::new(transport)
102 .expect("Failed to create VirtIO socket driver");
103 info!("Found socket device: guest_cid={}", socket.guest_cid());
104 }
105
106 /// Checks the given VirtIO console device.
check_virtio_console_device(transport: PciTransport)107 fn check_virtio_console_device(transport: PciTransport) {
108 let mut console = VirtIOConsole::<HalImpl, PciTransport>::new(transport)
109 .expect("Failed to create VirtIO console driver");
110 info!(
111 "Found console device with size {:?}",
112 console.size().expect("Failed to get size of VirtIO console device")
113 );
114 for &c in b"Hello VirtIO console\n" {
115 console.send(c).expect("Failed to send character to VirtIO console device");
116 }
117 info!("Wrote to VirtIO console.");
118 }
119
120 struct HalImpl;
121
122 /// SAFETY: See the 'Implementation Safety' comments on methods below for how they fulfill the
123 /// safety requirements of the unsafe `Hal` trait.
124 unsafe impl Hal for HalImpl {
125 /// # Implementation Safety
126 ///
127 /// `dma_alloc` ensures the returned DMA buffer is not aliased with any other allocation or
128 /// reference in the program until it is deallocated by `dma_dealloc` by allocating a unique
129 /// block of memory using `alloc_zeroed`, which is guaranteed to allocate valid, unique and
130 /// zeroed memory. We request an alignment of at least `PAGE_SIZE` from `alloc_zeroed`.
dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>)131 fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>) {
132 debug!("dma_alloc: pages={}", pages);
133 let layout =
134 Layout::from_size_align(pages.checked_mul(PAGE_SIZE).unwrap(), PAGE_SIZE).unwrap();
135 assert_ne!(layout.size(), 0);
136 // SAFETY: We just checked that the layout has a non-zero size.
137 let vaddr = unsafe { alloc_zeroed(layout) };
138 let vaddr =
139 if let Some(vaddr) = NonNull::new(vaddr) { vaddr } else { handle_alloc_error(layout) };
140 let paddr = virt_to_phys(vaddr);
141 (paddr, vaddr)
142 }
143
dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32144 unsafe fn dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32 {
145 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
146 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
147 // SAFETY: The memory was allocated by `dma_alloc` above using the same allocator, and the
148 // layout is the same as was used then.
149 unsafe {
150 dealloc(vaddr.as_ptr(), layout);
151 }
152 0
153 }
154
155 /// # Implementation Safety
156 ///
157 /// The returned pointer must be valid because the `paddr` describes a valid MMIO region, and we
158 /// previously mapped the entire PCI MMIO range. It can't alias any other allocations because
159 /// the PCI MMIO range doesn't overlap with any other memory ranges.
mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8>160 unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
161 NonNull::new(paddr as _).unwrap()
162 }
163
share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr164 unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr {
165 let vaddr = buffer.cast();
166 // Nothing to do, as the host already has access to all memory.
167 virt_to_phys(vaddr)
168 }
169
unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection)170 unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {
171 // Nothing to do, as the host already has access to all memory and we didn't copy the buffer
172 // anywhere else.
173 }
174 }
175
virt_to_phys(vaddr: NonNull<u8>) -> PhysAddr176 fn virt_to_phys(vaddr: NonNull<u8>) -> PhysAddr {
177 vaddr.as_ptr() as _
178 }
179