blob: bd5b5ba572e9f8d97da8d86d09d68be618b76cc8 [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 Walbranb713baa2022-12-07 14:34:49 +000019use core::{ffi::CStr, mem::size_of};
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000020use libfdt::{AddressRange, Fdt, FdtNode, Reg};
Andrew Walbran8217d062022-11-22 16:56:18 +000021use log::{debug, info};
22use virtio_drivers::{
23 pci::{
Andrew Walbran0a8dac72022-12-21 13:49:06 +000024 bus::{Cam, PciRoot},
Andrew Walbran8217d062022-11-22 16:56:18 +000025 virtio_device_type, PciTransport,
26 },
27 DeviceType, Hal, PhysAddr, Transport, VirtAddr, VirtIOBlk, PAGE_SIZE,
28};
29
30/// The standard sector size of a VirtIO block device, in bytes.
Andrew Walbranb713baa2022-12-07 14:34:49 +000031const SECTOR_SIZE_BYTES: usize = 512;
32
33/// The size in sectors of the test block device we expect.
34const EXPECTED_SECTOR_COUNT: usize = 4;
Andrew Walbran8217d062022-11-22 16:56:18 +000035
36/// Finds an FDT node with compatible=pci-host-cam-generic.
37pub fn pci_node(fdt: &Fdt) -> FdtNode {
38 fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
39 .unwrap()
40 .next()
41 .unwrap()
42}
43
Andrew Walbran0a8dac72022-12-21 13:49:06 +000044pub fn check_pci(reg: Reg<u64>) {
Andrew Walbran8217d062022-11-22 16:56:18 +000045 let mut pci_root = unsafe { PciRoot::new(reg.addr as *mut u8, Cam::MmioCam) };
Andrew Walbranb713baa2022-12-07 14:34:49 +000046 let mut checked_virtio_device_count = 0;
Andrew Walbran8217d062022-11-22 16:56:18 +000047 for (device_function, info) in pci_root.enumerate_bus(0) {
48 let (status, command) = pci_root.get_status_command(device_function);
49 info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
50 if let Some(virtio_type) = virtio_device_type(&info) {
51 info!(" VirtIO {:?}", virtio_type);
Andrew Walbran8217d062022-11-22 16:56:18 +000052 let mut transport =
53 PciTransport::new::<HalImpl>(&mut pci_root, device_function).unwrap();
54 info!(
55 "Detected virtio PCI device with device type {:?}, features {:#018x}",
56 transport.device_type(),
57 transport.read_device_features(),
58 );
Andrew Walbranb713baa2022-12-07 14:34:49 +000059 if check_virtio_device(transport, virtio_type) {
60 checked_virtio_device_count += 1;
61 }
Andrew Walbran8217d062022-11-22 16:56:18 +000062 }
63 }
Andrew Walbranb713baa2022-12-07 14:34:49 +000064
65 assert_eq!(checked_virtio_device_count, 1);
Andrew Walbran8217d062022-11-22 16:56:18 +000066}
67
Andrew Walbranb713baa2022-12-07 14:34:49 +000068/// Checks the given VirtIO device, if we know how to.
69///
70/// Returns true if the device was checked, or false if it was ignored.
71fn check_virtio_device(transport: impl Transport, device_type: DeviceType) -> bool {
Andrew Walbran8217d062022-11-22 16:56:18 +000072 if device_type == DeviceType::Block {
Andrew Walbranb713baa2022-12-07 14:34:49 +000073 let mut blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
74 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
75 assert_eq!(blk.capacity(), EXPECTED_SECTOR_COUNT as u64);
76 let mut data = [0; SECTOR_SIZE_BYTES * EXPECTED_SECTOR_COUNT];
77 for i in 0..EXPECTED_SECTOR_COUNT {
78 blk.read_block(i, &mut data[i * SECTOR_SIZE_BYTES..(i + 1) * SECTOR_SIZE_BYTES])
79 .expect("Failed to read block device.");
80 }
81 for (i, chunk) in data.chunks(size_of::<u32>()).enumerate() {
82 assert_eq!(chunk, &(i as u32).to_le_bytes());
83 }
84 info!("Read expected data from block device.");
85 true
86 } else {
87 false
Andrew Walbran8217d062022-11-22 16:56:18 +000088 }
89}
90
91#[derive(Copy, Clone, Debug, Eq, PartialEq)]
92struct PciMemoryFlags(u32);
93
94impl PciMemoryFlags {
95 pub fn prefetchable(self) -> bool {
96 self.0 & 0x80000000 != 0
97 }
98
99 pub fn range_type(self) -> PciRangeType {
100 PciRangeType::from((self.0 & 0x3000000) >> 24)
101 }
102}
103
104/// Allocates 32-bit memory addresses for PCI BARs.
105pub struct PciMemory32Allocator {
106 start: u32,
107 end: u32,
108}
109
110impl PciMemory32Allocator {
111 /// Creates a new allocator based on the ranges property of the given PCI node.
112 pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
113 let mut memory_32_address = 0;
114 let mut memory_32_size = 0;
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000115 for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
116 .ranges::<(u32, u64), u64, u64>()
Andrew Walbran8217d062022-11-22 16:56:18 +0000117 .expect("Error getting ranges property from PCI node")
118 .expect("PCI node missing ranges property.")
119 {
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000120 let flags = PciMemoryFlags(flags);
Andrew Walbran8217d062022-11-22 16:56:18 +0000121 let prefetchable = flags.prefetchable();
122 let range_type = flags.range_type();
Andrew Walbran8217d062022-11-22 16:56:18 +0000123 info!(
124 "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
125 range_type,
126 if prefetchable { "" } else { "non-" },
127 bus_address,
128 cpu_physical,
129 size,
130 );
131 if !prefetchable
132 && ((range_type == PciRangeType::Memory32 && size > memory_32_size.into())
133 || (range_type == PciRangeType::Memory64
134 && size > memory_32_size.into()
135 && bus_address + size < u32::MAX.into()))
136 {
137 // Use the 64-bit range for 32-bit memory, if it is low enough.
138 assert_eq!(bus_address, cpu_physical);
139 memory_32_address = u32::try_from(cpu_physical).unwrap();
140 memory_32_size = u32::try_from(size).unwrap();
141 }
142 }
143 if memory_32_size == 0 {
144 panic!("No PCI memory regions found.");
145 }
146
147 Self { start: memory_32_address, end: memory_32_address + memory_32_size }
148 }
149
150 /// Gets a memory region covering the address space from which this allocator will allocate.
151 pub fn get_region(&self) -> MemoryRegion {
152 MemoryRegion::new(self.start as usize, self.end as usize)
153 }
Andrew Walbran8217d062022-11-22 16:56:18 +0000154}
155
156#[derive(Copy, Clone, Debug, Eq, PartialEq)]
157enum PciRangeType {
158 ConfigurationSpace,
159 IoSpace,
160 Memory32,
161 Memory64,
162}
163
164impl From<u32> for PciRangeType {
165 fn from(value: u32) -> Self {
166 match value {
167 0 => Self::ConfigurationSpace,
168 1 => Self::IoSpace,
169 2 => Self::Memory32,
170 3 => Self::Memory64,
171 _ => panic!("Tried to convert invalid range type {}", value),
172 }
173 }
174}
175
Andrew Walbran8217d062022-11-22 16:56:18 +0000176struct HalImpl;
177
178impl Hal for HalImpl {
179 fn dma_alloc(pages: usize) -> PhysAddr {
180 debug!("dma_alloc: pages={}", pages);
181 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
182 // Safe because the layout has a non-zero size.
183 let vaddr = unsafe { alloc(layout) } as VirtAddr;
184 Self::virt_to_phys(vaddr)
185 }
186
187 fn dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
188 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
189 let vaddr = Self::phys_to_virt(paddr);
190 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
191 // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
192 // the layout is the same as was used then.
193 unsafe {
194 dealloc(vaddr as *mut u8, layout);
195 }
196 0
197 }
198
199 fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
200 paddr
201 }
202
203 fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
204 vaddr
205 }
206}