blob: a204b90d9f20269e409969fd33d2e0089ffc2971 [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;
18use alloc::alloc::{alloc, dealloc, Layout};
Andrew Walbran730375d2022-12-21 14:04:34 +000019use core::mem::size_of;
20use fdtpci::PciInfo;
Andrew Walbran8217d062022-11-22 16:56:18 +000021use log::{debug, info};
22use virtio_drivers::{
Andrew Walbran730375d2022-12-21 14:04:34 +000023 pci::{bus::PciRoot, virtio_device_type, PciTransport},
Andrew Walbran8217d062022-11-22 16:56:18 +000024 DeviceType, Hal, PhysAddr, Transport, VirtAddr, VirtIOBlk, PAGE_SIZE,
25};
26
27/// The standard sector size of a VirtIO block device, in bytes.
Andrew Walbranb713baa2022-12-07 14:34:49 +000028const SECTOR_SIZE_BYTES: usize = 512;
29
30/// The size in sectors of the test block device we expect.
31const EXPECTED_SECTOR_COUNT: usize = 4;
Andrew Walbran8217d062022-11-22 16:56:18 +000032
Andrew Walbran730375d2022-12-21 14:04:34 +000033pub fn check_pci(pci_root: &mut PciRoot) {
Andrew Walbranb713baa2022-12-07 14:34:49 +000034 let mut checked_virtio_device_count = 0;
Andrew Walbran8217d062022-11-22 16:56:18 +000035 for (device_function, info) in pci_root.enumerate_bus(0) {
36 let (status, command) = pci_root.get_status_command(device_function);
37 info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
38 if let Some(virtio_type) = virtio_device_type(&info) {
39 info!(" VirtIO {:?}", virtio_type);
Andrew Walbran730375d2022-12-21 14:04:34 +000040 let mut transport = PciTransport::new::<HalImpl>(pci_root, device_function).unwrap();
Andrew Walbran8217d062022-11-22 16:56:18 +000041 info!(
42 "Detected virtio PCI device with device type {:?}, features {:#018x}",
43 transport.device_type(),
44 transport.read_device_features(),
45 );
Andrew Walbranb713baa2022-12-07 14:34:49 +000046 if check_virtio_device(transport, virtio_type) {
47 checked_virtio_device_count += 1;
48 }
Andrew Walbran8217d062022-11-22 16:56:18 +000049 }
50 }
Andrew Walbranb713baa2022-12-07 14:34:49 +000051
52 assert_eq!(checked_virtio_device_count, 1);
Andrew Walbran8217d062022-11-22 16:56:18 +000053}
54
Andrew Walbranb713baa2022-12-07 14:34:49 +000055/// Checks the given VirtIO device, if we know how to.
56///
57/// Returns true if the device was checked, or false if it was ignored.
58fn check_virtio_device(transport: impl Transport, device_type: DeviceType) -> bool {
Andrew Walbran8217d062022-11-22 16:56:18 +000059 if device_type == DeviceType::Block {
Andrew Walbranb713baa2022-12-07 14:34:49 +000060 let mut blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
61 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
62 assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
63 let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
64 for i in 0..EXPECTED_SECTOR_COUNT {
65 blk.read_block(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
66 .expect("Failed to read block device.");
67 }
68 for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
69 assert_eq!(chunk, &(i as u32).to_le_bytes());
70 }
71 info!("Read expected data from block device.");
72 true
73 } else {
74 false
Andrew Walbran8217d062022-11-22 16:56:18 +000075 }
76}
77
Andrew Walbran730375d2022-12-21 14:04:34 +000078/// Gets the memory region in which BARs are allocated.
79pub fn get_bar_region(pci_info: &PciInfo) -> MemoryRegion {
80 MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
Andrew Walbran8217d062022-11-22 16:56:18 +000081}
82
Andrew Walbran8217d062022-11-22 16:56:18 +000083struct HalImpl;
84
85impl Hal for HalImpl {
86 fn dma_alloc(pages: usize) -> PhysAddr {
87 debug!("dma_alloc: pages={}", pages);
88 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
89 // Safe because the layout has a non-zero size.
90 let vaddr = unsafe { alloc(layout) } as VirtAddr;
91 Self::virt_to_phys(vaddr)
92 }
93
94 fn dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
95 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
96 let vaddr = Self::phys_to_virt(paddr);
97 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
98 // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
99 // the layout is the same as was used then.
100 unsafe {
101 dealloc(vaddr as *mut u8, layout);
102 }
103 0
104 }
105
106 fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
107 paddr
108 }
109
110 fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
111 vaddr
112 }
113}