blob: 117cbc87c7434b3b1bec9b440d3d99283e4c3018 [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 Walbran272bd7a2023-01-24 14:02:36 +000018use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout};
Andrew Walbran336d0cb2023-01-06 16:33:15 +000019use core::{mem::size_of, ptr::NonNull};
Andrew Walbran730375d2022-12-21 14:04:34 +000020use fdtpci::PciInfo;
Andrew Walbran8217d062022-11-22 16:56:18 +000021use log::{debug, info};
22use virtio_drivers::{
Andrew Walbran8d05dae2023-03-22 16:42:55 +000023 device::{blk::VirtIOBlk, console::VirtIOConsole},
Andrew Walbran336d0cb2023-01-06 16:33:15 +000024 transport::{
25 pci::{bus::PciRoot, virtio_device_type, PciTransport},
26 DeviceType, Transport,
27 },
Andrew Walbran272bd7a2023-01-24 14:02:36 +000028 BufferDirection, Hal, PhysAddr, PAGE_SIZE,
Andrew Walbran8217d062022-11-22 16:56:18 +000029};
30
31/// The standard sector size of a VirtIO block device, in bytes.
Andrew Walbranb713baa2022-12-07 14:34:49 +000032const SECTOR_SIZE_BYTES: usize = 512;
33
34/// The size in sectors of the test block device we expect.
35const EXPECTED_SECTOR_COUNT: usize = 4;
Andrew Walbran8217d062022-11-22 16:56:18 +000036
Andrew Walbran730375d2022-12-21 14:04:34 +000037pub fn check_pci(pci_root: &mut PciRoot) {
Andrew Walbranb713baa2022-12-07 14:34:49 +000038 let mut checked_virtio_device_count = 0;
Andrew Walbran8217d062022-11-22 16:56:18 +000039 for (device_function, info) in pci_root.enumerate_bus(0) {
40 let (status, command) = pci_root.get_status_command(device_function);
41 info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
42 if let Some(virtio_type) = virtio_device_type(&info) {
43 info!(" VirtIO {:?}", virtio_type);
Andrew Walbran730375d2022-12-21 14:04:34 +000044 let mut transport = PciTransport::new::<HalImpl>(pci_root, device_function).unwrap();
Andrew Walbran8217d062022-11-22 16:56:18 +000045 info!(
46 "Detected virtio PCI device with device type {:?}, features {:#018x}",
47 transport.device_type(),
48 transport.read_device_features(),
49 );
Andrew Walbranb713baa2022-12-07 14:34:49 +000050 if check_virtio_device(transport, virtio_type) {
51 checked_virtio_device_count += 1;
52 }
Andrew Walbran8217d062022-11-22 16:56:18 +000053 }
54 }
Andrew Walbranb713baa2022-12-07 14:34:49 +000055
Andrew Walbran8d05dae2023-03-22 16:42:55 +000056 assert_eq!(checked_virtio_device_count, 4);
Andrew Walbran8217d062022-11-22 16:56:18 +000057}
58
Andrew Walbranb713baa2022-12-07 14:34:49 +000059/// Checks the given VirtIO device, if we know how to.
60///
61/// Returns true if the device was checked, or false if it was ignored.
62fn check_virtio_device(transport: impl Transport, device_type: DeviceType) -> bool {
Andrew Walbran8d05dae2023-03-22 16:42:55 +000063 match device_type {
64 DeviceType::Block => {
65 let mut blk =
66 VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
67 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
68 assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
69 let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
70 for i in 0..EXPECTED_SECTOR_COUNT {
71 blk.read_block(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
72 .expect("Failed to read block device.");
73 }
74 for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
75 assert_eq!(chunk, &(i as u32).to_le_bytes());
76 }
77 info!("Read expected data from block device.");
78 true
Andrew Walbranb713baa2022-12-07 14:34:49 +000079 }
Andrew Walbran8d05dae2023-03-22 16:42:55 +000080 DeviceType::Console => {
81 let mut console = VirtIOConsole::<HalImpl, _>::new(transport)
82 .expect("Failed to create VirtIO console driver");
83 info!("Found console device: {:?}", console.info());
84 for &c in b"Hello VirtIO console\n" {
85 console.send(c).expect("Failed to send character to VirtIO console device");
86 }
87 info!("Wrote to VirtIO console.");
88 true
Andrew Walbranb713baa2022-12-07 14:34:49 +000089 }
Andrew Walbran8d05dae2023-03-22 16:42:55 +000090 _ => false,
Andrew Walbran8217d062022-11-22 16:56:18 +000091 }
92}
93
Andrew Walbran730375d2022-12-21 14:04:34 +000094/// Gets the memory region in which BARs are allocated.
95pub fn get_bar_region(pci_info: &PciInfo) -> MemoryRegion {
96 MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
Andrew Walbran8217d062022-11-22 16:56:18 +000097}
98
Andrew Walbran8217d062022-11-22 16:56:18 +000099struct HalImpl;
100
101impl Hal for HalImpl {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000102 fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>) {
Andrew Walbran8217d062022-11-22 16:56:18 +0000103 debug!("dma_alloc: pages={}", pages);
104 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
105 // Safe because the layout has a non-zero size.
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000106 let vaddr = unsafe { alloc(layout) };
107 let vaddr =
108 if let Some(vaddr) = NonNull::new(vaddr) { vaddr } else { handle_alloc_error(layout) };
109 let paddr = virt_to_phys(vaddr);
110 (paddr, vaddr)
Andrew Walbran8217d062022-11-22 16:56:18 +0000111 }
112
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000113 fn dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32 {
Andrew Walbran8217d062022-11-22 16:56:18 +0000114 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
Andrew Walbran8217d062022-11-22 16:56:18 +0000115 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
116 // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
117 // the layout is the same as was used then.
118 unsafe {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000119 dealloc(vaddr.as_ptr(), layout);
Andrew Walbran8217d062022-11-22 16:56:18 +0000120 }
121 0
122 }
123
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000124 fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
125 NonNull::new(paddr as _).unwrap()
Andrew Walbran8217d062022-11-22 16:56:18 +0000126 }
127
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000128 fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr {
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000129 let vaddr = buffer.cast();
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000130 // Nothing to do, as the host already has access to all memory.
131 virt_to_phys(vaddr)
Andrew Walbran8217d062022-11-22 16:56:18 +0000132 }
Andrew Walbran336d0cb2023-01-06 16:33:15 +0000133
134 fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {
135 // Nothing to do, as the host already has access to all memory and we didn't copy the buffer
136 // anywhere else.
137 }
138}
139
Andrew Walbran272bd7a2023-01-24 14:02:36 +0000140fn virt_to_phys(vaddr: NonNull<u8>) -> PhysAddr {
141 vaddr.as_ptr() as _
Andrew Walbran8217d062022-11-22 16:56:18 +0000142}