Alioth Code Coverage

segment.rs41.60%

1// Copyright 2024 Google LLC
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// https://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
15use std::collections::HashMap;
16use std::iter::zip;
17use std::sync::Arc;
18
19use parking_lot::{Mutex, RwLock};
20
21use crate::device::{self, Pause};
22use crate::mem::emulated::{Action, Mmio};
23use crate::pci::config::{BAR_IO, BAR_MEM64, BAR_PREFETCHABLE, PciConfig};
24use crate::pci::{Bdf, Pci, Result};
25use crate::{align_up, mem};
26
27#[derive(Debug)]
28struct EmptyDevice;
29
30impl Pause for EmptyDevice {
31 fn pause(&self) -> device::Result<()> {
32 Ok(())
33 }
34
35 fn resume(&self) -> device::Result<()> {
36 Ok(())
37 }
38}
39
40impl Pci for EmptyDevice {
41 fn name(&self) -> &str {
42 "empty_device"
43 }
44
45 fn config(&self) -> &dyn PciConfig {
46 unreachable!()
47 }
48
49 fn reset(&self) -> Result<()> {
50 unreachable!()
51 }
52}
53
54#[derive(Debug)]
55pub struct PciSegment {
56 devices: RwLock<HashMap<Bdf, Arc<dyn Pci>>>,
57 next_bdf: Mutex<Bdf>,
58 placeholder: Arc<dyn Pci>,
59}
60
61impl PciSegment {
62 pub fn new() -> Self {31x
63 Self {31x
64 devices: RwLock::new(HashMap::new()),31x
65 next_bdf: Mutex::new(Bdf::new(0, 0, 0)),31x
66 placeholder: Arc::new(EmptyDevice),31x
67 }31x
68 }31x
69
70 pub fn max_bus(&self) -> Option<u8> {
71 let devices = self.devices.read();
72 devices.keys().map(|bdf| bdf.bus()).max()
73 }
74
75 pub fn add(&self, bdf: Bdf, dev: Arc<dyn Pci>) -> Option<Arc<dyn Pci>> {71x
76 let mut configs = self.devices.write();71x
77 if let Some(exist_dev) = configs.insert(bdf, dev) {71x
78 if Arc::ptr_eq(&exist_dev, &self.placeholder) {27x
79 None12x
80 } else {
81 configs.insert(bdf, exist_dev)15x
82 }
83 } else {
84 None44x
85 }
86 }71x
87
88 pub fn reserve(&self, bdf: Option<Bdf>) -> Option<Bdf> {18x
89 let mut empty_dev = self.placeholder.clone();18x
90 match bdf {18x
91 Some(bdf) => {9x
92 if self.add(bdf, empty_dev).is_none() {9x
93 Some(bdf)6x
94 } else {
95 None3x
96 }
97 }
98 None => {
99 let mut next_bdf = self.next_bdf.lock();9x
100 let init = *next_bdf;9x
101 loop {
102 let bdf = *next_bdf;21x
103 *next_bdf = Bdf(next_bdf.0.wrapping_add(8));21x
104 match self.add(bdf, empty_dev) {21x
105 None => break Some(bdf),9x
106 Some(d) => empty_dev = d,12x
107 }
108 if *next_bdf == init {12x
109 break None;
110 }12x
111 }
112 }
113 }
114 }18x
115
116 /// Assigns addresses to all devices' base address registers
117 ///
118 /// `resources` is an array of 4 `(start, end)` tuples, corresponds to
119 ///
120 /// - IO space,
121 /// - 32-bit non-prefetchable memory space,
122 /// - 32-bit prefetchable memory space,
123 /// - 64-bit prefetchable memory space,
124 ///
125 /// respectively.
126 pub fn assign_resources(&self, resources: &[(u64, u64); 4]) {
127 let mut bar_lists = [const { vec![] }; 4];
128 let devices = self.devices.read();
129 for (bdf, dev) in devices.iter() {
130 let config = dev.config();
131 let header = config.get_header().data.read();
132 let mut index = 0;
133 while index < 6 {
134 let bar_index = index;
135 index += 1;
136 let (val, mask) = header.get_bar(bar_index);
137 let mut mask = mask as u64;
138 if val & BAR_MEM64 == BAR_MEM64 {
139 let (_, mask_hi) = header.get_bar(bar_index + 1);
140 mask |= (mask_hi as u64) << 32;
141 index += 1;
142 }
143 if mask == 0 {
144 continue;
145 }
146 let bar_list = if val & BAR_IO == BAR_IO {
147 &mut bar_lists[0]
148 } else if val & (BAR_MEM64 | BAR_PREFETCHABLE) == BAR_MEM64 | BAR_PREFETCHABLE {
149 &mut bar_lists[3]
150 } else if val & (BAR_MEM64 | BAR_PREFETCHABLE) == BAR_MEM64 {
151 unreachable!("{bdf}: BAR {index} is 64-bit but not prefetchable")
152 } else if val & BAR_PREFETCHABLE == BAR_PREFETCHABLE {
153 &mut bar_lists[2]
154 } else {
155 &mut bar_lists[1]
156 };
157 bar_list.push((*bdf, dev, bar_index, 1 << mask.trailing_zeros()));
158 }
159 }
160 for bar_list in bar_lists.iter_mut() {
161 bar_list.sort_by_key(|(bdf, _, index, size)| (u64::MAX - size, *bdf, *index));
162 }
163 for (bar_list, (start, end)) in zip(bar_lists, resources) {
164 let mut addr = *start;
165 for (bdf, dev, index, size) in bar_list {
166 let config = dev.config();
167 let mut header = config.get_header().data.write();
168 let aligned_addr = align_up!(addr, size.trailing_zeros());
169 if aligned_addr + size > *end {
170 log::error!(
171 "{bdf}: cannot map BAR {index} into address range {start:#x}..{end:#x}"
172 );
173 continue;
174 }
175 header.set_bar(index, aligned_addr as u32);
176 if aligned_addr > u32::MAX as u64 {
177 header.set_bar(index + 1, (aligned_addr >> 32) as u32);
178 }
179 addr = aligned_addr + size;
180 }
181 }
182 }
183
184 pub fn reset(&self) -> Result<()> {
185 let devices = self.devices.read();
186 for (_, dev) in devices.iter() {
187 dev.reset()?;
188 dev.config().reset()?;
189 }
190 Ok(())
191 }
192}
193
194impl Default for PciSegment {
195 fn default() -> Self {12x
196 Self::new()12x
197 }12x
198}
199
200impl Mmio for PciSegment {
201 fn size(&self) -> u64 {3x
202 // 256 MiB: 256 buses, 32 devices, 8 functions
203 256 * 32 * 8 * 40963x
204 }3x
205
206 fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {22x
207 let bdf = Bdf((offset >> 12) as u16);22x
208 let devices = self.devices.read();22x
209 if let Some(dev) = devices.get(&bdf) {22x
210 dev.config().read(offset & 0xfff, size)19x
211 } else {
212 Ok(u64::MAX)3x
213 }
214 }22x
215
216 fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {9x
217 let bdf = Bdf((offset >> 12) as u16);9x
218 let devices = self.devices.read();9x
219 if let Some(dev) = devices.get(&bdf) {9x
220 dev.config().write(offset & 0xfff, size, val)6x
221 } else {
222 Ok(Action::None)3x
223 }
224 }9x
225}
226
227#[cfg(test)]
228#[path = "segment_test.rs"]
229mod tests;
230