blob: e4a4b04312e94b6f60678e53db01562fa6582c99 [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
15//! Functions to scan the PCI bus for VirtIO device and allocate BARs.
16
17use aarch64_paging::paging::MemoryRegion;
18use alloc::alloc::{alloc, dealloc, Layout};
19use core::ffi::CStr;
20use libfdt::{Fdt, FdtNode, Reg};
21use log::{debug, info};
22use virtio_drivers::{
23 pci::{
24 bus::{BarInfo, Cam, Command, DeviceFunction, MemoryBarType, PciRoot},
25 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.
31const SECTOR_SIZE_BYTES: u64 = 512;
32
33/// Finds an FDT node with compatible=pci-host-cam-generic.
34pub fn pci_node(fdt: &Fdt) -> FdtNode {
35 fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
36 .unwrap()
37 .next()
38 .unwrap()
39}
40
41pub fn check_pci(reg: Reg<u64>, allocator: &mut PciMemory32Allocator) {
42 let mut pci_root = unsafe { PciRoot::new(reg.addr as *mut u8, Cam::MmioCam) };
43 for (device_function, info) in pci_root.enumerate_bus(0) {
44 let (status, command) = pci_root.get_status_command(device_function);
45 info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
46 if let Some(virtio_type) = virtio_device_type(&info) {
47 info!(" VirtIO {:?}", virtio_type);
48 allocate_bars(&mut pci_root, device_function, allocator);
49 let mut transport =
50 PciTransport::new::<HalImpl>(&mut pci_root, device_function).unwrap();
51 info!(
52 "Detected virtio PCI device with device type {:?}, features {:#018x}",
53 transport.device_type(),
54 transport.read_device_features(),
55 );
56 instantiate_virtio_driver(transport, virtio_type);
57 }
58 }
59}
60
61fn instantiate_virtio_driver(transport: impl Transport, device_type: DeviceType) {
62 if device_type == DeviceType::Block {
63 let blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
64 info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES / 1024);
65 }
66}
67
68#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69struct PciMemoryFlags(u32);
70
71impl PciMemoryFlags {
72 pub fn prefetchable(self) -> bool {
73 self.0 & 0x80000000 != 0
74 }
75
76 pub fn range_type(self) -> PciRangeType {
77 PciRangeType::from((self.0 & 0x3000000) >> 24)
78 }
79}
80
81/// Allocates 32-bit memory addresses for PCI BARs.
82pub struct PciMemory32Allocator {
83 start: u32,
84 end: u32,
85}
86
87impl PciMemory32Allocator {
88 /// Creates a new allocator based on the ranges property of the given PCI node.
89 pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
90 let mut memory_32_address = 0;
91 let mut memory_32_size = 0;
92 for range in pci_node
93 .ranges::<u128, u64, u64>()
94 .expect("Error getting ranges property from PCI node")
95 .expect("PCI node missing ranges property.")
96 {
97 let flags = PciMemoryFlags((range.addr >> 64) as u32);
98 let prefetchable = flags.prefetchable();
99 let range_type = flags.range_type();
100 let bus_address = range.addr as u64;
101 let cpu_physical = range.parent_addr;
102 let size = range.size;
103 info!(
104 "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
105 range_type,
106 if prefetchable { "" } else { "non-" },
107 bus_address,
108 cpu_physical,
109 size,
110 );
111 if !prefetchable
112 && ((range_type == PciRangeType::Memory32 && size > memory_32_size.into())
113 || (range_type == PciRangeType::Memory64
114 && size > memory_32_size.into()
115 && bus_address + size < u32::MAX.into()))
116 {
117 // Use the 64-bit range for 32-bit memory, if it is low enough.
118 assert_eq!(bus_address, cpu_physical);
119 memory_32_address = u32::try_from(cpu_physical).unwrap();
120 memory_32_size = u32::try_from(size).unwrap();
121 }
122 }
123 if memory_32_size == 0 {
124 panic!("No PCI memory regions found.");
125 }
126
127 Self { start: memory_32_address, end: memory_32_address + memory_32_size }
128 }
129
130 /// Gets a memory region covering the address space from which this allocator will allocate.
131 pub fn get_region(&self) -> MemoryRegion {
132 MemoryRegion::new(self.start as usize, self.end as usize)
133 }
134
135 /// Allocates a 32-bit memory address region for a PCI BAR of the given power-of-2 size.
136 ///
137 /// It will have alignment matching the size. The size must be a power of 2.
138 pub fn allocate_memory_32(&mut self, size: u32) -> Option<u32> {
139 assert!(size.is_power_of_two());
140 let allocated_address = align_up(self.start, size);
141 if allocated_address + size <= self.end {
142 self.start = allocated_address + size;
143 Some(allocated_address)
144 } else {
145 None
146 }
147 }
148}
149
150#[derive(Copy, Clone, Debug, Eq, PartialEq)]
151enum PciRangeType {
152 ConfigurationSpace,
153 IoSpace,
154 Memory32,
155 Memory64,
156}
157
158impl From<u32> for PciRangeType {
159 fn from(value: u32) -> Self {
160 match value {
161 0 => Self::ConfigurationSpace,
162 1 => Self::IoSpace,
163 2 => Self::Memory32,
164 3 => Self::Memory64,
165 _ => panic!("Tried to convert invalid range type {}", value),
166 }
167 }
168}
169
170/// Allocates appropriately-sized memory regions and assigns them to the device's BARs.
171fn allocate_bars(
172 root: &mut PciRoot,
173 device_function: DeviceFunction,
174 allocator: &mut PciMemory32Allocator,
175) {
176 let mut bar_index = 0;
177 while bar_index < 6 {
178 let info = root.bar_info(device_function, bar_index).unwrap();
179 debug!("BAR {}: {}", bar_index, info);
180 // Ignore I/O bars, as they aren't required for the VirtIO driver.
181 if let BarInfo::Memory { address_type, size, .. } = info {
182 match address_type {
183 _ if size == 0 => {}
184 MemoryBarType::Width32 => {
185 let address = allocator.allocate_memory_32(size).unwrap();
186 debug!("Allocated address {:#010x}", address);
187 root.set_bar_32(device_function, bar_index, address);
188 }
189 MemoryBarType::Width64 => {
190 let address = allocator.allocate_memory_32(size).unwrap();
191 debug!("Allocated address {:#010x}", address);
192 root.set_bar_64(device_function, bar_index, address.into());
193 }
194 _ => panic!("Memory BAR address type {:?} not supported.", address_type),
195 }
196 }
197
198 bar_index += 1;
199 if info.takes_two_entries() {
200 bar_index += 1;
201 }
202 }
203
204 // Enable the device to use its BARs.
205 root.set_command(
206 device_function,
207 Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
208 );
209 let (status, command) = root.get_status_command(device_function);
210 debug!("Allocated BARs and enabled device, status {:?} command {:?}", status, command);
211}
212
213const fn align_up(value: u32, alignment: u32) -> u32 {
214 ((value - 1) | (alignment - 1)) + 1
215}
216
217struct HalImpl;
218
219impl Hal for HalImpl {
220 fn dma_alloc(pages: usize) -> PhysAddr {
221 debug!("dma_alloc: pages={}", pages);
222 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
223 // Safe because the layout has a non-zero size.
224 let vaddr = unsafe { alloc(layout) } as VirtAddr;
225 Self::virt_to_phys(vaddr)
226 }
227
228 fn dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
229 debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
230 let vaddr = Self::phys_to_virt(paddr);
231 let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
232 // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
233 // the layout is the same as was used then.
234 unsafe {
235 dealloc(vaddr as *mut u8, layout);
236 }
237 0
238 }
239
240 fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
241 paddr
242 }
243
244 fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
245 vaddr
246 }
247}