blob: 379425d46543bed9d28f4b5fce1c9db9504b2dfa [file] [log] [blame]
Andrew Walbran8217d062022-11-22 16:56:18 +00001// 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
Andrew Walbran0a8dac72022-12-21 13:49:06 +000015//! Functions to scan the PCI bus for VirtIO device.
Andrew Walbran8217d062022-11-22 16:56:18 +000016
17use aarch64_paging::paging::MemoryRegion;
Andrew Walbrancf9333e2023-05-16 16:00:35 +000018use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout};
Andrew Walbran336d0cb2023-01-06 16:33:15 +000019use core::{mem::size_of, ptr::NonNull};
Andrew Walbran8217d062022-11-22 16:56:18 +000020use log::{debug, info};
21use virtio_drivers::{
Alice Wang7c55c7d2023-07-05 14:51:40 +000022 device::console::VirtIOConsole,
Andrew Walbran336d0cb2023-01-06 16:33:15 +000023 transport::{
Alice Wang7c55c7d2023-07-05 14:51:40 +000024 pci::{bus::PciRoot, PciTransport},
Andrew Walbran336d0cb2023-01-06 16:33:15 +000025 DeviceType, Transport,
26 },
Andrew Walbran6ac174e2023-06-23 14:58:51 +000027 BufferDirection, Error, Hal, PhysAddr, PAGE_SIZE,
Andrew Walbran8217d062022-11-22 16:56:18 +000028};
Pierre-Clément Tosif2c19d42024-10-01 17:42:04 +010029use vmbase::{
30 fdt::pci::PciInfo,
31 virtio::pci::{self, PciTransportIterator},
32};
Andrew Walbran8217d062022-11-22 16:56:18 +000033
34/// The standard sector size of a VirtIO block device, in bytes.
Andrew Walbranb713baa2022-12-07 14:34:49 +000035const SECTOR_SIZE_BYTES: usize = 512;
36
37/// The size in sectors of the test block device we expect.
38const EXPECTED_SECTOR_COUNT: usize = 4;
Andrew Walbran8217d062022-11-22 16:56:18 +000039
Andrew Walbran730375d2022-12-21 14:04:34 +000040pub fn check_pci(pci_root: &mut PciRoot) {
Andrew Walbranb713baa2022-12-07 14:34:49 +000041 let mut checked_virtio_device_count = 0;
Andrew Walbran6ac174e2023-06-23 14:58:51 +000042 let mut block_device_count = 0;
Alice Wang16e7c3f2023-07-06 08:06:00 +000043 let mut socket_device_count = 0;
Alice Wang7c55c7d2023-07-05 14:51:40 +000044 for mut transport in PciTransportIterator::<HalImpl>::new(pci_root) {
45 info!(
46 "Detected virtio PCI device with device type {:?}, features {:#018x}",
47 transport.device_type(),
48 transport.read_device_features(),
49 );
50 match transport.device_type() {
51 DeviceType::Block => {
52 check_virtio_block_device(transport, block_device_count);
53 block_device_count += 1;
54 checked_virtio_device_count += 1;
Andrew Walbranb713baa2022-12-07 14:34:49 +000055 }
Alice Wang7c55c7d2023-07-05 14:51:40 +000056 DeviceType::Console => {
57 check_virtio_console_device(transport);
58 checked_virtio_device_count += 1;
59 }
Alice Wang16e7c3f2023-07-06 08:06:00 +000060 DeviceType::Socket => {
61 check_virtio_socket_device(transport);
62 socket_device_count += 1;
63 checked_virtio_device_count += 1;
64 }
Alice Wang7c55c7d2023-07-05 14:51:40 +000065 _ => {}
Andrew Walbran8217d062022-11-22 16:56:18 +000066 }
67 }
Andrew Walbranb713baa2022-12-07 14:34:49 +000068
Alice Wang16e7c3f2023-07-06 08:06:00 +000069 assert_eq!(checked_virtio_device_count, 6);
Andrew Walbran6ac174e2023-06-23 14:58:51 +000070 assert_eq!(block_device_count, 2);
Alice Wang16e7c3f2023-07-06 08:06:00 +000071 assert_eq!(socket_device_count, 1);
Andrew Walbran8217d062022-11-22 16:56:18 +000072}
73
Andrew Walbran6ac174e2023-06-23 14:58:51 +000074/// Checks the given VirtIO block device.
Alice Wang7c55c7d2023-07-05 14:51:40 +000075fn check_virtio_block_device(transport: PciTransport, index: usize) {
76 let mut blk = pci::VirtIOBlk::<HalImpl>::new(transport).expect("failed to create blk driver");
Andrew Walbran6ac174e2023-06-23 14:58:51 +000077 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
78 match index {
79 0 => {
Andrew Walbran8d05dae2023-03-22 16:42:55 +000080 assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
81 let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
82 for i in 0..EXPECTED_SECTOR_COUNT {
Alice Wangb27ec8f2023-07-27 13:19:26 +000083 blk.read_blocks(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
Andrew Walbran8d05dae2023-03-22 16:42:55 +000084 .expect("Failed to read block device.");
85 }
86 for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
87 assert_eq!(chunk, &(i as u32).to_le_bytes());
88 }
89 info!("Read expected data from block device.");
Andrew Walbranb713baa2022-12-07 14:34:49 +000090 }
Andrew Walbran6ac174e2023-06-23 14:58:51 +000091 1 => {
92 assert_eq!(blk.capacity(), 0);
93 let mut data = [0; SECTOR_SIZE_BYTES];
Alice Wangb27ec8f2023-07-27 13:19:26 +000094 assert_eq!(blk.read_blocks(0, &mut data), Err(Error::IoError));
Andrew Walbranb713baa2022-12-07 14:34:49 +000095 }
Andrew Walbran6ac174e2023-06-23 14:58:51 +000096 _ => panic!("Unexpected VirtIO block device index {}.", index),
Andrew Walbran8217d062022-11-22 16:56:18 +000097 }
98}
99
Alice Wang16e7c3f2023-07-06 08:06:00 +0000100/// Checks the given VirtIO socket device.
101fn check_virtio_socket_device(transport: PciTransport) {
102 let socket = pci::VirtIOSocket::<HalImpl>::new(transport)
103 .expect("Failed to create VirtIO socket driver");
104 info!("Found socket device: guest_cid={}", socket.guest_cid());
105}
106
Andrew Walbran6ac174e2023-06-23 14:58:51 +0000107/// Checks the given VirtIO console device.
Alice Wang7c55c7d2023-07-05 14:51:40 +0000108fn check_virtio_console_device(transport: PciTransport) {
109 let mut console = VirtIOConsole::<HalImpl, PciTransport>::new(transport)
Andrew Walbran6ac174e2023-06-23 14:58:51 +0000110 .expect("Failed to create VirtIO console driver");
111 info!("Found console device: {:?}", console.info());
112 for &c in b"Hello VirtIO console\n" {
113 console.send(c).expect("Failed to send character to VirtIO console device");
114 }
115 info!("Wrote to VirtIO console.");
116}
117
Andrew Walbran730375d2022-12-21 14:04:34 +0000118/// Gets the memory region in which BARs are allocated.
119pub fn get_bar_region(pci_info: &PciInfo) -> MemoryRegion {
120 MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
Andrew Walbran8217d062022-11-22 16:56:18 +0000121}
122
Pierre-Clément Tosi1ea25a62024-11-02 12:54:40 +0000123/// Gets the PCI CAM memory region.
124pub fn get_cam_region(pci_info: &PciInfo) -> MemoryRegion {
125 MemoryRegion::new(pci_info.cam_range.start, pci_info.cam_range.end)
126}
127
Andrew Walbran8217d062022-11-22 16:56:18 +0000128struct HalImpl;
129
Andrew Walbran47842802023-07-05 16:56:08 +0000130/// SAFETY: See the 'Implementation Safety' comments on methods below for how they fulfill the
131/// safety requirements of the unsafe `Hal` trait.
Alice Wang63e0d0d2023-04-20 14:15:32 +0000132unsafe impl Hal for HalImpl {
Andrew Walbran47842802023-07-05 16:56:08 +0000133 /// # Implementation Safety
134 ///
135 /// `dma_alloc` ensures the returned DMA buffer is not aliased with any other allocation or
136 /// reference in the program until it is deallocated by `dma_dealloc` by allocating a unique
137 /// block of memory using `alloc_zeroed`, which is guaranteed to allocate valid, unique and
138 /// zeroed memory. We request an alignment of at least `PAGE_SIZE` from `alloc_zeroed`.
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000139 fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>) {
Andrew Walbran8217d062022-11-22 16:56:18 +0000140 debug!("dma_alloc: pages={}", pages);
Andrew Walbran47842802023-07-05 16:56:08 +0000141 let layout =
142 Layout::from_size_align(pages.checked_mul(PAGE_SIZE).unwrap(), PAGE_SIZE).unwrap();
143 assert_ne!(layout.size(), 0);
144 // SAFETY: We just checked that the layout has a non-zero size.
Andrew Walbrancf9333e2023-05-16 16:00:35 +0000145 let vaddr = unsafe { alloc_zeroed(layout) };
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000146 let vaddr =
147 if let Some(vaddr) = NonNull::new(vaddr) { vaddr } else { handle_alloc_error(layout) };
148 let paddr = virt_to_phys(vaddr);
149 (paddr, vaddr)
Andrew Walbran8217d062022-11-22 16:56:18 +0000150 }
151
Alice Wang63e0d0d2023-04-20 14:15:32 +0000152 unsafe fn dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32 {
Andrew Walbran8217d062022-11-22 16:56:18 +0000153 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
Andrew Walbran8217d062022-11-22 16:56:18 +0000154 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
Andrew Walbran47842802023-07-05 16:56:08 +0000155 // SAFETY: The memory was allocated by `dma_alloc` above using the same allocator, and the
156 // layout is the same as was used then.
Andrew Walbran8217d062022-11-22 16:56:18 +0000157 unsafe {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000158 dealloc(vaddr.as_ptr(), layout);
Andrew Walbran8217d062022-11-22 16:56:18 +0000159 }
160 0
161 }
162
Andrew Walbran47842802023-07-05 16:56:08 +0000163 /// # Implementation Safety
164 ///
165 /// The returned pointer must be valid because the `paddr` describes a valid MMIO region, and we
166 /// previously mapped the entire PCI MMIO range. It can't alias any other allocations because
167 /// the PCI MMIO range doesn't overlap with any other memory ranges.
Alice Wang63e0d0d2023-04-20 14:15:32 +0000168 unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000169 NonNull::new(paddr as _).unwrap()
Andrew Walbran8217d062022-11-22 16:56:18 +0000170 }
171
Alice Wang63e0d0d2023-04-20 14:15:32 +0000172 unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000173 let vaddr = buffer.cast();
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000174 // Nothing to do, as the host already has access to all memory.
175 virt_to_phys(vaddr)
Andrew Walbran8217d062022-11-22 16:56:18 +0000176 }
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000177
Alice Wang63e0d0d2023-04-20 14:15:32 +0000178 unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000179 // Nothing to do, as the host already has access to all memory and we didn't copy the buffer
180 // anywhere else.
181 }
182}
183
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000184fn virt_to_phys(vaddr: NonNull<u8>) -> PhysAddr {
185 vaddr.as_ptr() as _
Andrew Walbran8217d062022-11-22 16:56:18 +0000186}