blob: 3d060acac07b437a4b1d6bf3cdbf9f752b9f001a [file] [log] [blame]
Jaewan Kimc6e023b2023-10-12 15:11:05 +09001// Copyright 2023, 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//! Validate device assignment written in crosvm DT with VM DTBO, and apply it
16//! to platform DT.
17//! Declared in separated libs for adding unit tests, which requires libstd.
18
19#[cfg(test)]
20extern crate alloc;
21
Jaewan Kim51ccfed2023-11-08 13:51:58 +090022use alloc::collections::{BTreeMap, BTreeSet};
Jaewan Kimc6e023b2023-10-12 15:11:05 +090023use alloc::ffi::CString;
24use alloc::fmt;
25use alloc::vec;
26use alloc::vec::Vec;
27use core::ffi::CStr;
28use core::iter::Iterator;
29use core::mem;
Jaewan Kim52477ae2023-11-21 21:20:52 +090030use hyp::DeviceAssigningHypervisor;
31use libfdt::{Fdt, FdtError, FdtNode, Phandle, Reg};
32use log::error;
Jaewan Kimc6e023b2023-10-12 15:11:05 +090033
Jaewan Kimc6e023b2023-10-12 15:11:05 +090034// TODO(b/308694211): Use cstr! from vmbase instead.
35macro_rules! cstr {
36 ($str:literal) => {{
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +000037 const S: &str = concat!($str, "\0");
38 const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
39 Ok(v) => v,
40 Err(_) => panic!("string contains interior NUL"),
41 };
42 C
Jaewan Kimc6e023b2023-10-12 15:11:05 +090043 }};
44}
45
Jaewan Kimc6e023b2023-10-12 15:11:05 +090046// TODO(b/277993056): Keep constants derived from platform.dts in one place.
47const CELLS_PER_INTERRUPT: usize = 3; // from /intc node in platform.dts
48
49/// Errors in device assignment.
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum DeviceAssignmentError {
Jaewan Kim52477ae2023-11-21 21:20:52 +090052 /// Invalid VM DTBO
Jaewan Kimc6e023b2023-10-12 15:11:05 +090053 InvalidDtbo,
54 /// Invalid __symbols__
55 InvalidSymbols,
Jaewan Kim19b984f2023-12-04 15:16:50 +090056 /// Malformed <reg>. Can't parse.
57 MalformedReg,
58 /// Invalid <reg>. Failed to validate with HVC.
Jaewan Kim52477ae2023-11-21 21:20:52 +090059 InvalidReg,
Jaewan Kimc6e023b2023-10-12 15:11:05 +090060 /// Invalid <interrupts>
61 InvalidInterrupts,
Jaewan Kim19b984f2023-12-04 15:16:50 +090062 /// Malformed <iommus>
63 MalformedIommus,
Jaewan Kim51ccfed2023-11-08 13:51:58 +090064 /// Invalid <iommus>
65 InvalidIommus,
Jaewan Kim19b984f2023-12-04 15:16:50 +090066 /// Invalid phys IOMMU node
67 InvalidPhysIommu,
Jaewan Kima9200492023-11-21 20:45:31 +090068 /// Invalid pvIOMMU node
69 InvalidPvIommu,
Jaewan Kim51ccfed2023-11-08 13:51:58 +090070 /// Too many pvIOMMU
71 TooManyPvIommu,
Jaewan Kim19b984f2023-12-04 15:16:50 +090072 /// Duplicated phys IOMMU IDs exist
73 DuplicatedIommuIds,
Jaewan Kim51ccfed2023-11-08 13:51:58 +090074 /// Duplicated pvIOMMU IDs exist
75 DuplicatedPvIommuIds,
Jaewan Kimc6e023b2023-10-12 15:11:05 +090076 /// Unsupported overlay target syntax. Only supports <target-path> with full path.
77 UnsupportedOverlayTarget,
Jaewan Kim19b984f2023-12-04 15:16:50 +090078 /// Unsupported PhysIommu,
79 UnsupportedPhysIommu,
80 /// Unsupported (pvIOMMU id, vSID) duplication. Currently the pair should be unique.
81 UnsupportedPvIommusDuplication,
82 /// Unsupported (IOMMU token, SID) duplication. Currently the pair should be unique.
83 UnsupportedIommusDuplication,
Jaewan Kim51ccfed2023-11-08 13:51:58 +090084 /// Internal error
85 Internal,
Jaewan Kimc6e023b2023-10-12 15:11:05 +090086 /// Unexpected error from libfdt
87 UnexpectedFdtError(FdtError),
88}
89
90impl From<FdtError> for DeviceAssignmentError {
91 fn from(e: FdtError) -> Self {
92 DeviceAssignmentError::UnexpectedFdtError(e)
93 }
94}
95
96impl fmt::Display for DeviceAssignmentError {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 match self {
99 Self::InvalidDtbo => write!(f, "Invalid DTBO"),
100 Self::InvalidSymbols => write!(
101 f,
102 "Invalid property in /__symbols__. Must point to valid assignable device node."
103 ),
Jaewan Kim19b984f2023-12-04 15:16:50 +0900104 Self::MalformedReg => write!(f, "Malformed <reg>. Can't parse"),
105 Self::InvalidReg => write!(f, "Invalid <reg>. Failed to validate with hypervisor"),
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900106 Self::InvalidInterrupts => write!(f, "Invalid <interrupts>"),
Jaewan Kim19b984f2023-12-04 15:16:50 +0900107 Self::MalformedIommus => write!(f, "Malformed <iommus>. Can't parse."),
108 Self::InvalidIommus => {
109 write!(f, "Invalid <iommus>. Failed to validate with hypervisor")
110 }
111 Self::InvalidPhysIommu => write!(f, "Invalid phys IOMMU node"),
Jaewan Kima9200492023-11-21 20:45:31 +0900112 Self::InvalidPvIommu => write!(f, "Invalid pvIOMMU node"),
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900113 Self::TooManyPvIommu => write!(
114 f,
115 "Too many pvIOMMU node. Insufficient pre-populated pvIOMMUs in platform DT"
116 ),
Jaewan Kim19b984f2023-12-04 15:16:50 +0900117 Self::DuplicatedIommuIds => {
118 write!(f, "Duplicated IOMMU IDs exist. IDs must unique among iommu node")
119 }
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900120 Self::DuplicatedPvIommuIds => {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900121 write!(f, "Duplicated pvIOMMU IDs exist. IDs must unique among iommu node")
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900122 }
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900123 Self::UnsupportedOverlayTarget => {
124 write!(f, "Unsupported overlay target. Only supports 'target-path = \"/\"'")
125 }
Jaewan Kim19b984f2023-12-04 15:16:50 +0900126 Self::UnsupportedPhysIommu => {
127 write!(f, "Unsupported Phys IOMMU. Currently only supports #iommu-cells = <1>")
128 }
129 Self::UnsupportedPvIommusDuplication => {
130 write!(f, "Unsupported (pvIOMMU id, vSID) duplication. Currently the pair should be unique.")
131 }
132 Self::UnsupportedIommusDuplication => {
133 write!(f, "Unsupported (IOMMU token, SID) duplication. Currently the pair should be unique.")
134 }
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900135 Self::Internal => write!(f, "Internal error"),
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900136 Self::UnexpectedFdtError(e) => write!(f, "Unexpected Error from libfdt: {e}"),
137 }
138 }
139}
140
141pub type Result<T> = core::result::Result<T, DeviceAssignmentError>;
142
143/// Represents VM DTBO
144#[repr(transparent)]
145pub struct VmDtbo(Fdt);
146
147impl VmDtbo {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900148 /// Wraps a mutable slice containing a VM DTBO.
149 ///
150 /// Fails if the VM DTBO does not pass validation.
151 pub fn from_mut_slice(dtbo: &mut [u8]) -> Result<&mut Self> {
152 // This validates DTBO
153 let fdt = Fdt::from_mut_slice(dtbo)?;
154 // SAFETY: VmDtbo is a transparent wrapper around Fdt, so representation is the same.
155 Ok(unsafe { mem::transmute::<&mut Fdt, &mut Self>(fdt) })
156 }
157
158 // Locates device node path as if the given dtbo node path is assigned and VM DTBO is overlaid.
159 // For given dtbo node path, this concatenates <target-path> of the enclosing fragment and
160 // relative path from __overlay__ node.
161 //
162 // Here's an example with sample VM DTBO:
163 // / {
164 // fragment@rng {
165 // target-path = "/"; // Always 'target-path = "/"'. Disallows <target> or other path.
166 // __overlay__ {
167 // rng { ... }; // Actual device node is here. If overlaid, path would be "/rng"
168 // };
169 // };
170 // __symbols__ { // List of assignable devices
171 // // Each property describes an assigned device device information.
172 // // property name is the device label, and property value is the path in the VM DTBO.
173 // rng = "/fragment@rng/__overlay__/rng";
174 // };
175 // };
176 //
177 // Then locate_overlay_target_path(cstr!("/fragment@rng/__overlay__/rng")) is Ok("/rng")
178 //
179 // Contrary to fdt_overlay_target_offset(), this API enforces overlay target property
180 // 'target-path = "/"', so the overlay doesn't modify and/or append platform DT's existing
181 // node and/or properties. The enforcement is for compatibility reason.
Jaewan Kim19b984f2023-12-04 15:16:50 +0900182 fn locate_overlay_target_path(
183 &self,
184 dtbo_node_path: &CStr,
185 dtbo_node: &FdtNode,
186 ) -> Result<CString> {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900187 let dtbo_node_path_bytes = dtbo_node_path.to_bytes();
188 if dtbo_node_path_bytes.first() != Some(&b'/') {
189 return Err(DeviceAssignmentError::UnsupportedOverlayTarget);
190 }
191
Jaewan Kim19b984f2023-12-04 15:16:50 +0900192 let fragment_node = dtbo_node.supernode_at_depth(1)?;
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900193 let target_path = fragment_node
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +0000194 .getprop_str(cstr!("target-path"))?
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900195 .ok_or(DeviceAssignmentError::InvalidDtbo)?;
196 if target_path != cstr!("/") {
197 return Err(DeviceAssignmentError::UnsupportedOverlayTarget);
198 }
199
200 let mut components = dtbo_node_path_bytes
201 .split(|char| *char == b'/')
202 .filter(|&component| !component.is_empty())
203 .skip(1);
204 let overlay_node_name = components.next();
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +0000205 if overlay_node_name != Some(b"__overlay__") {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900206 return Err(DeviceAssignmentError::InvalidDtbo);
207 }
208 let mut overlaid_path = Vec::with_capacity(dtbo_node_path_bytes.len());
209 for component in components {
210 overlaid_path.push(b'/');
211 overlaid_path.extend_from_slice(component);
212 }
213 overlaid_path.push(b'\0');
214
215 Ok(CString::from_vec_with_nul(overlaid_path).unwrap())
216 }
Jaewan Kim19b984f2023-12-04 15:16:50 +0900217
218 fn parse_physical_iommus(physical_node: &FdtNode) -> Result<BTreeMap<Phandle, PhysIommu>> {
219 let mut phys_iommus = BTreeMap::new();
220 for (node, _) in physical_node.descendants() {
221 let Some(phandle) = node.get_phandle()? else {
222 continue; // Skips unreachable IOMMU node
223 };
224 let Some(iommu) = PhysIommu::parse(&node)? else {
225 continue; // Skip if not a PhysIommu.
226 };
227 if phys_iommus.insert(phandle, iommu).is_some() {
228 return Err(FdtError::BadPhandle.into());
229 }
230 }
231 Self::validate_physical_iommus(&phys_iommus)?;
232 Ok(phys_iommus)
233 }
234
235 fn validate_physical_iommus(phys_iommus: &BTreeMap<Phandle, PhysIommu>) -> Result<()> {
236 let unique_iommus: BTreeSet<_> = phys_iommus.values().cloned().collect();
237 if phys_iommus.len() != unique_iommus.len() {
238 return Err(DeviceAssignmentError::DuplicatedIommuIds);
239 }
240 Ok(())
241 }
242
243 fn validate_physical_devices(
244 physical_devices: &BTreeMap<Phandle, PhysicalDeviceInfo>,
245 ) -> Result<()> {
246 // Only need to validate iommus because <reg> will be validated together with PV <reg>
247 // see: DeviceAssignmentInfo::validate_all_regs().
248 let mut all_iommus = BTreeSet::new();
249 for physical_device in physical_devices.values() {
250 for iommu in &physical_device.iommus {
251 if !all_iommus.insert(iommu) {
252 error!("Unsupported phys IOMMU duplication found, <iommus> = {iommu:?}");
253 return Err(DeviceAssignmentError::UnsupportedIommusDuplication);
254 }
255 }
256 }
257 Ok(())
258 }
259
260 fn parse_physical_devices_with_iommus(
261 physical_node: &FdtNode,
262 phys_iommus: &BTreeMap<Phandle, PhysIommu>,
263 ) -> Result<BTreeMap<Phandle, PhysicalDeviceInfo>> {
264 let mut physical_devices = BTreeMap::new();
265 for (node, _) in physical_node.descendants() {
266 let Some(info) = PhysicalDeviceInfo::parse(&node, phys_iommus)? else {
267 continue;
268 };
269 if physical_devices.insert(info.target, info).is_some() {
270 return Err(DeviceAssignmentError::InvalidDtbo);
271 }
272 }
273 Self::validate_physical_devices(&physical_devices)?;
274 Ok(physical_devices)
275 }
276
277 /// Parses Physical devices in VM DTBO
278 fn parse_physical_devices(&self) -> Result<BTreeMap<Phandle, PhysicalDeviceInfo>> {
279 let Some(physical_node) = self.as_ref().node(cstr!("/host"))? else {
280 return Ok(BTreeMap::new());
281 };
282
283 let phys_iommus = Self::parse_physical_iommus(&physical_node)?;
284 Self::parse_physical_devices_with_iommus(&physical_node, &phys_iommus)
285 }
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900286}
287
Jaewan Kimc39974e2023-12-02 01:13:30 +0900288fn is_overlayable_node(dtbo_path: &CStr) -> bool {
289 dtbo_path
290 .to_bytes()
291 .split(|char| *char == b'/')
292 .filter(|&component| !component.is_empty())
293 .nth(1)
294 .map_or(false, |name| name == b"__overlay__")
295}
296
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900297impl AsRef<Fdt> for VmDtbo {
298 fn as_ref(&self) -> &Fdt {
299 &self.0
300 }
301}
302
303impl AsMut<Fdt> for VmDtbo {
304 fn as_mut(&mut self) -> &mut Fdt {
305 &mut self.0
306 }
307}
308
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900309#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
310struct PvIommu {
311 // ID from pvIOMMU node
312 id: u32,
313}
314
315impl PvIommu {
316 fn parse(node: &FdtNode) -> Result<Self> {
Jaewan Kima9200492023-11-21 20:45:31 +0900317 let iommu_cells = node
318 .getprop_u32(cstr!("#iommu-cells"))?
319 .ok_or(DeviceAssignmentError::InvalidPvIommu)?;
Jaewan Kim19b984f2023-12-04 15:16:50 +0900320 // Ensures #iommu-cells = <1>. It means that `<iommus>` entry contains pair of
Jaewan Kima9200492023-11-21 20:45:31 +0900321 // (pvIOMMU ID, vSID)
322 if iommu_cells != 1 {
323 return Err(DeviceAssignmentError::InvalidPvIommu);
324 }
325 let id = node.getprop_u32(cstr!("id"))?.ok_or(DeviceAssignmentError::InvalidPvIommu)?;
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900326 Ok(Self { id })
327 }
328}
329
Jaewan Kima9200492023-11-21 20:45:31 +0900330#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
331struct Vsid(u32);
332
Jaewan Kim19b984f2023-12-04 15:16:50 +0900333#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
334struct Sid(u64);
335
336impl From<u32> for Sid {
337 fn from(sid: u32) -> Self {
338 Self(sid.into())
339 }
340}
341
342#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Jaewan Kim52477ae2023-11-21 21:20:52 +0900343struct DeviceReg {
344 addr: u64,
345 size: u64,
346}
347
348impl TryFrom<Reg<u64>> for DeviceReg {
349 type Error = DeviceAssignmentError;
350
351 fn try_from(reg: Reg<u64>) -> Result<Self> {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900352 Ok(Self { addr: reg.addr, size: reg.size.ok_or(DeviceAssignmentError::MalformedReg)? })
Jaewan Kim52477ae2023-11-21 21:20:52 +0900353 }
354}
355
356fn parse_node_reg(node: &FdtNode) -> Result<Vec<DeviceReg>> {
357 node.reg()?
Jaewan Kim19b984f2023-12-04 15:16:50 +0900358 .ok_or(DeviceAssignmentError::MalformedReg)?
Jaewan Kim52477ae2023-11-21 21:20:52 +0900359 .map(DeviceReg::try_from)
360 .collect::<Result<Vec<_>>>()
361}
362
363fn to_be_bytes(reg: &[DeviceReg]) -> Vec<u8> {
364 let mut reg_cells = vec![];
365 for x in reg {
366 reg_cells.extend_from_slice(&x.addr.to_be_bytes());
367 reg_cells.extend_from_slice(&x.size.to_be_bytes());
368 }
369 reg_cells
370}
371
Jaewan Kim19b984f2023-12-04 15:16:50 +0900372#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
373struct PhysIommu {
374 token: u64,
375}
376
377impl PhysIommu {
378 fn parse(node: &FdtNode) -> Result<Option<Self>> {
379 let Some(token) = node.getprop_u64(cstr!("android,pvmfw,token"))? else {
380 return Ok(None);
381 };
382 let Some(iommu_cells) = node.getprop_u32(cstr!("#iommu-cells"))? else {
383 return Err(DeviceAssignmentError::InvalidPhysIommu);
384 };
385 // Currently only supports #iommu-cells = <1>.
386 // In that case `<iommus>` entry contains pair of (pIOMMU phandle, Sid token)
387 if iommu_cells != 1 {
388 return Err(DeviceAssignmentError::UnsupportedPhysIommu);
389 }
390 Ok(Some(Self { token }))
391 }
392}
393
394#[derive(Debug)]
395struct PhysicalDeviceInfo {
396 target: Phandle,
397 reg: Vec<DeviceReg>,
398 iommus: Vec<(PhysIommu, Sid)>,
399}
400
401impl PhysicalDeviceInfo {
402 fn parse_iommus(
403 node: &FdtNode,
404 phys_iommus: &BTreeMap<Phandle, PhysIommu>,
405 ) -> Result<Vec<(PhysIommu, Sid)>> {
406 let mut iommus = vec![];
407 let Some(mut cells) = node.getprop_cells(cstr!("iommus"))? else {
408 return Ok(iommus);
409 };
410 while let Some(cell) = cells.next() {
411 // Parse pIOMMU ID
412 let phandle =
413 Phandle::try_from(cell).or(Err(DeviceAssignmentError::MalformedIommus))?;
414 let iommu = phys_iommus.get(&phandle).ok_or(DeviceAssignmentError::MalformedIommus)?;
415
416 // Parse Sid
417 let Some(cell) = cells.next() else {
418 return Err(DeviceAssignmentError::MalformedIommus);
419 };
420
421 iommus.push((*iommu, Sid::from(cell)));
422 }
423 Ok(iommus)
424 }
425
426 fn parse(node: &FdtNode, phys_iommus: &BTreeMap<Phandle, PhysIommu>) -> Result<Option<Self>> {
427 let Some(phandle) = node.getprop_u32(cstr!("android,pvmfw,target"))? else {
428 return Ok(None);
429 };
430 let target = Phandle::try_from(phandle)?;
431 let reg = parse_node_reg(node)?;
432 let iommus = Self::parse_iommus(node, phys_iommus)?;
433 Ok(Some(Self { target, reg, iommus }))
434 }
435}
436
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900437/// Assigned device information parsed from crosvm DT.
438/// Keeps everything in the owned data because underlying FDT will be reused for platform DT.
439#[derive(Debug, Eq, PartialEq)]
440struct AssignedDeviceInfo {
441 // Node path of assigned device (e.g. "/rng")
442 node_path: CString,
443 // DTBO node path of the assigned device (e.g. "/fragment@rng/__overlay__/rng")
444 dtbo_node_path: CString,
445 // <reg> property from the crosvm DT
Jaewan Kim52477ae2023-11-21 21:20:52 +0900446 reg: Vec<DeviceReg>,
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900447 // <interrupts> property from the crosvm DT
448 interrupts: Vec<u8>,
Jaewan Kima9200492023-11-21 20:45:31 +0900449 // Parsed <iommus> property from the crosvm DT. Tuple of PvIommu and vSID.
450 iommus: Vec<(PvIommu, Vsid)>,
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900451}
452
453impl AssignedDeviceInfo {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900454 fn validate_reg(
455 device_reg: &[DeviceReg],
456 physical_device_reg: &[DeviceReg],
Jaewan Kim52477ae2023-11-21 21:20:52 +0900457 hypervisor: &dyn DeviceAssigningHypervisor,
Jaewan Kim19b984f2023-12-04 15:16:50 +0900458 ) -> Result<()> {
459 if device_reg.len() != physical_device_reg.len() {
460 return Err(DeviceAssignmentError::InvalidReg);
461 }
462 // PV reg and physical reg should have 1:1 match in order.
463 for (reg, phys_reg) in device_reg.iter().zip(physical_device_reg.iter()) {
464 let addr = hypervisor.get_phys_mmio_token(reg.addr, reg.size).map_err(|e| {
465 error!("Failed to validate device <reg>, error={e:?}, reg={reg:x?}");
Jaewan Kim52477ae2023-11-21 21:20:52 +0900466 DeviceAssignmentError::InvalidReg
467 })?;
Jaewan Kim19b984f2023-12-04 15:16:50 +0900468 // Only check address because hypervisor guaranatees size match when success.
469 if phys_reg.addr != addr {
470 error!("Failed to validate device <reg>. No matching phys reg for reg={reg:x?}");
471 return Err(DeviceAssignmentError::InvalidReg);
472 }
Jaewan Kim52477ae2023-11-21 21:20:52 +0900473 }
Jaewan Kim19b984f2023-12-04 15:16:50 +0900474 Ok(())
Jaewan Kim52477ae2023-11-21 21:20:52 +0900475 }
476
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900477 fn parse_interrupts(node: &FdtNode) -> Result<Vec<u8>> {
478 // Validation: Validate if interrupts cell numbers are multiple of #interrupt-cells.
479 // We can't know how many interrupts would exist.
480 let interrupts_cells = node
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +0000481 .getprop_cells(cstr!("interrupts"))?
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900482 .ok_or(DeviceAssignmentError::InvalidInterrupts)?
483 .count();
484 if interrupts_cells % CELLS_PER_INTERRUPT != 0 {
485 return Err(DeviceAssignmentError::InvalidInterrupts);
486 }
487
488 // Once validated, keep the raw bytes so patch can be done with setprop()
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +0000489 Ok(node.getprop(cstr!("interrupts")).unwrap().unwrap().into())
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900490 }
491
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900492 // TODO(b/277993056): Also validate /__local_fixups__ to ensure that <iommus> has phandle.
Jaewan Kima9200492023-11-21 20:45:31 +0900493 fn parse_iommus(
494 node: &FdtNode,
495 pviommus: &BTreeMap<Phandle, PvIommu>,
496 ) -> Result<Vec<(PvIommu, Vsid)>> {
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900497 let mut iommus = vec![];
Jaewan Kima9200492023-11-21 20:45:31 +0900498 let Some(mut cells) = node.getprop_cells(cstr!("iommus"))? else {
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900499 return Ok(iommus);
500 };
Jaewan Kima9200492023-11-21 20:45:31 +0900501 while let Some(cell) = cells.next() {
502 // Parse pvIOMMU ID
Jaewan Kim19b984f2023-12-04 15:16:50 +0900503 let phandle =
504 Phandle::try_from(cell).or(Err(DeviceAssignmentError::MalformedIommus))?;
505 let pviommu = pviommus.get(&phandle).ok_or(DeviceAssignmentError::MalformedIommus)?;
Jaewan Kima9200492023-11-21 20:45:31 +0900506
507 // Parse vSID
508 let Some(cell) = cells.next() else {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900509 return Err(DeviceAssignmentError::MalformedIommus);
Jaewan Kima9200492023-11-21 20:45:31 +0900510 };
511 let vsid = Vsid(cell);
512
513 iommus.push((*pviommu, vsid));
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900514 }
515 Ok(iommus)
516 }
517
Jaewan Kim19b984f2023-12-04 15:16:50 +0900518 fn validate_iommus(
519 iommus: &[(PvIommu, Vsid)],
520 physical_device_iommu: &[(PhysIommu, Sid)],
521 hypervisor: &dyn DeviceAssigningHypervisor,
522 ) -> Result<()> {
523 if iommus.len() != physical_device_iommu.len() {
524 return Err(DeviceAssignmentError::InvalidIommus);
525 }
526 // pvIOMMU can be reordered, and hypervisor may not guarantee 1:1 mapping.
527 // So we need to mark what's matched or not.
528 let mut physical_device_iommu = physical_device_iommu.to_vec();
529 for (pviommu, vsid) in iommus {
530 let (id, sid) = hypervisor.get_phys_iommu_token(pviommu.id.into(), vsid.0.into())
531 .map_err(|e| {
532 error!("Failed to validate device <iommus>, error={e:?}, pviommu={pviommu:?}, vsid={vsid:?}");
533 DeviceAssignmentError::InvalidIommus
534 })?;
535
536 let pos = physical_device_iommu
537 .iter()
538 .position(|(phys_iommu, phys_sid)| (phys_iommu.token, phys_sid.0) == (id, sid));
539 match pos {
540 Some(pos) => physical_device_iommu.remove(pos),
541 None => {
542 error!("Failed to validate device <iommus>. No matching phys iommu or duplicated mapping for pviommu={pviommu:?}, vsid={vsid:?}");
543 return Err(DeviceAssignmentError::InvalidIommus);
544 }
545 };
546 }
547 Ok(())
548 }
549
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900550 fn parse(
551 fdt: &Fdt,
552 vm_dtbo: &VmDtbo,
553 dtbo_node_path: &CStr,
Jaewan Kim19b984f2023-12-04 15:16:50 +0900554 physical_devices: &BTreeMap<Phandle, PhysicalDeviceInfo>,
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900555 pviommus: &BTreeMap<Phandle, PvIommu>,
Jaewan Kim52477ae2023-11-21 21:20:52 +0900556 hypervisor: &dyn DeviceAssigningHypervisor,
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900557 ) -> Result<Option<Self>> {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900558 let dtbo_node =
559 vm_dtbo.as_ref().node(dtbo_node_path)?.ok_or(DeviceAssignmentError::InvalidSymbols)?;
560 let node_path = vm_dtbo.locate_overlay_target_path(dtbo_node_path, &dtbo_node)?;
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900561
562 let Some(node) = fdt.node(&node_path)? else { return Ok(None) };
563
Jaewan Kim19b984f2023-12-04 15:16:50 +0900564 // Note: Currently can only assign devices backed by physical devices.
565 let phandle = dtbo_node.get_phandle()?.ok_or(DeviceAssignmentError::InvalidDtbo)?;
566 let physical_device =
567 physical_devices.get(&phandle).ok_or(DeviceAssignmentError::InvalidDtbo)?;
568
569 let reg = parse_node_reg(&node)?;
570 Self::validate_reg(&reg, &physical_device.reg, hypervisor)?;
571
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900572 let interrupts = Self::parse_interrupts(&node)?;
Jaewan Kim19b984f2023-12-04 15:16:50 +0900573
574 let iommus = Self::parse_iommus(&node, pviommus)?;
575 Self::validate_iommus(&iommus, &physical_device.iommus, hypervisor)?;
576
Jaewan Kim52477ae2023-11-21 21:20:52 +0900577 Ok(Some(Self { node_path, dtbo_node_path: dtbo_node_path.into(), reg, interrupts, iommus }))
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900578 }
579
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900580 fn patch(&self, fdt: &mut Fdt, pviommu_phandles: &BTreeMap<PvIommu, Phandle>) -> Result<()> {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900581 let mut dst = fdt.node_mut(&self.node_path)?.unwrap();
Jaewan Kim52477ae2023-11-21 21:20:52 +0900582 dst.setprop(cstr!("reg"), &to_be_bytes(&self.reg))?;
Pierre-Clément Tosid701a0b2023-11-07 15:38:59 +0000583 dst.setprop(cstr!("interrupts"), &self.interrupts)?;
Jaewan Kima9200492023-11-21 20:45:31 +0900584 let mut iommus = Vec::with_capacity(8 * self.iommus.len());
585 for (pviommu, vsid) in &self.iommus {
586 let phandle = pviommu_phandles.get(pviommu).unwrap();
587 iommus.extend_from_slice(&u32::from(*phandle).to_be_bytes());
588 iommus.extend_from_slice(&vsid.0.to_be_bytes());
589 }
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900590 dst.setprop(cstr!("iommus"), &iommus)?;
591
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900592 Ok(())
593 }
594}
595
596#[derive(Debug, Default, Eq, PartialEq)]
597pub struct DeviceAssignmentInfo {
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900598 pviommus: BTreeSet<PvIommu>,
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900599 assigned_devices: Vec<AssignedDeviceInfo>,
600 filtered_dtbo_paths: Vec<CString>,
601}
602
603impl DeviceAssignmentInfo {
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900604 const PVIOMMU_COMPATIBLE: &CStr = cstr!("pkvm,pviommu");
605
606 /// Parses pvIOMMUs in fdt
607 // Note: This will validate pvIOMMU ids' uniqueness, even when unassigned.
608 fn parse_pviommus(fdt: &Fdt) -> Result<BTreeMap<Phandle, PvIommu>> {
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900609 let mut pviommus = BTreeMap::new();
610 for compatible in fdt.compatible_nodes(Self::PVIOMMU_COMPATIBLE)? {
611 let Some(phandle) = compatible.get_phandle()? else {
612 continue; // Skips unreachable pvIOMMU node
613 };
614 let pviommu = PvIommu::parse(&compatible)?;
615 if pviommus.insert(phandle, pviommu).is_some() {
616 return Err(FdtError::BadPhandle.into());
617 }
618 }
619 Ok(pviommus)
620 }
621
Jaewan Kim19b984f2023-12-04 15:16:50 +0900622 fn validate_pviommu_topology(assigned_devices: &[AssignedDeviceInfo]) -> Result<()> {
623 let mut all_iommus = BTreeSet::new();
624 for assigned_device in assigned_devices {
625 for iommu in &assigned_device.iommus {
626 if !all_iommus.insert(iommu) {
627 error!("Unsupported pvIOMMU duplication found, <iommus> = {iommu:?}");
628 return Err(DeviceAssignmentError::UnsupportedPvIommusDuplication);
629 }
630 }
631 }
632 Ok(())
633 }
634
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900635 /// Parses fdt and vm_dtbo, and creates new DeviceAssignmentInfo
636 // TODO(b/277993056): Parse __local_fixups__
637 // TODO(b/277993056): Parse __fixups__
Jaewan Kim52477ae2023-11-21 21:20:52 +0900638 pub fn parse(
639 fdt: &Fdt,
640 vm_dtbo: &VmDtbo,
641 hypervisor: &dyn DeviceAssigningHypervisor,
642 ) -> Result<Option<Self>> {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900643 let Some(symbols_node) = vm_dtbo.as_ref().symbols()? else {
644 // /__symbols__ should contain all assignable devices.
645 // If empty, then nothing can be assigned.
646 return Ok(None);
647 };
648
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900649 let pviommus = Self::parse_pviommus(fdt)?;
650 let unique_pviommus: BTreeSet<_> = pviommus.values().cloned().collect();
651 if pviommus.len() != unique_pviommus.len() {
652 return Err(DeviceAssignmentError::DuplicatedPvIommuIds);
653 }
654
Jaewan Kim19b984f2023-12-04 15:16:50 +0900655 let physical_devices = vm_dtbo.parse_physical_devices()?;
656
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900657 let mut assigned_devices = vec![];
658 let mut filtered_dtbo_paths = vec![];
659 for symbol_prop in symbols_node.properties()? {
660 let symbol_prop_value = symbol_prop.value()?;
661 let dtbo_node_path = CStr::from_bytes_with_nul(symbol_prop_value)
662 .or(Err(DeviceAssignmentError::InvalidSymbols))?;
Jaewan Kimc39974e2023-12-02 01:13:30 +0900663 if !is_overlayable_node(dtbo_node_path) {
664 continue;
665 }
Jaewan Kim19b984f2023-12-04 15:16:50 +0900666 let assigned_device = AssignedDeviceInfo::parse(
667 fdt,
668 vm_dtbo,
669 dtbo_node_path,
670 &physical_devices,
671 &pviommus,
672 hypervisor,
673 )?;
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900674 if let Some(assigned_device) = assigned_device {
675 assigned_devices.push(assigned_device);
676 } else {
677 filtered_dtbo_paths.push(dtbo_node_path.into());
678 }
679 }
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900680 if assigned_devices.is_empty() {
681 return Ok(None);
682 }
Jaewan Kimc39974e2023-12-02 01:13:30 +0900683
Jaewan Kim19b984f2023-12-04 15:16:50 +0900684 Self::validate_pviommu_topology(&assigned_devices)?;
685
Jaewan Kimc39974e2023-12-02 01:13:30 +0900686 // Clean up any nodes that wouldn't be overlaid but may contain reference to filtered nodes.
687 // Otherwise, `fdt_apply_overlay()` would fail because of missing phandle reference.
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900688 filtered_dtbo_paths.push(CString::new("/__symbols__").unwrap());
Jaewan Kimc39974e2023-12-02 01:13:30 +0900689 // TODO(b/277993056): Also filter other unused nodes/props in __local_fixups__
690 filtered_dtbo_paths.push(CString::new("/__local_fixups__/host").unwrap());
691
692 // Note: Any node without __overlay__ will be ignored by fdt_apply_overlay,
693 // so doesn't need to be filtered.
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900694
695 Ok(Some(Self { pviommus: unique_pviommus, assigned_devices, filtered_dtbo_paths }))
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900696 }
697
698 /// Filters VM DTBO to only contain necessary information for booting pVM
699 /// In detail, this will remove followings by setting nop node / nop property.
700 /// - Removes unassigned devices
701 /// - Removes /__symbols__ node
702 // TODO(b/277993056): remove unused dependencies in VM DTBO.
703 // TODO(b/277993056): remove supernodes' properties.
704 // TODO(b/277993056): remove unused alises.
705 pub fn filter(&self, vm_dtbo: &mut VmDtbo) -> Result<()> {
706 let vm_dtbo = vm_dtbo.as_mut();
707
708 // Filters unused node in assigned devices
709 for filtered_dtbo_path in &self.filtered_dtbo_paths {
710 let node = vm_dtbo.node_mut(filtered_dtbo_path).unwrap().unwrap();
711 node.nop()?;
712 }
713
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900714 Ok(())
715 }
716
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900717 fn patch_pviommus(&self, fdt: &mut Fdt) -> Result<BTreeMap<PvIommu, Phandle>> {
718 let mut compatible = fdt.root_mut()?.next_compatible(Self::PVIOMMU_COMPATIBLE)?;
719 let mut pviommu_phandles = BTreeMap::new();
720
721 for pviommu in &self.pviommus {
722 let mut node = compatible.ok_or(DeviceAssignmentError::TooManyPvIommu)?;
723 let phandle = node.as_node().get_phandle()?.ok_or(DeviceAssignmentError::Internal)?;
724 node.setprop_inplace(cstr!("id"), &pviommu.id.to_be_bytes())?;
725 if pviommu_phandles.insert(*pviommu, phandle).is_some() {
726 return Err(DeviceAssignmentError::Internal);
727 }
728 compatible = node.next_compatible(Self::PVIOMMU_COMPATIBLE)?;
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900729 }
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900730
731 // Filters pre-populated but unassigned pvIOMMUs.
732 while let Some(filtered_pviommu) = compatible {
733 compatible = filtered_pviommu.delete_and_next_compatible(Self::PVIOMMU_COMPATIBLE)?;
734 }
735
736 Ok(pviommu_phandles)
737 }
738
739 pub fn patch(&self, fdt: &mut Fdt) -> Result<()> {
740 let pviommu_phandles = self.patch_pviommus(fdt)?;
741
742 // Patches assigned devices
743 for device in &self.assigned_devices {
744 device.patch(fdt, &pviommu_phandles)?;
745 }
746
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900747 Ok(())
748 }
749}
750
751#[cfg(test)]
752mod tests {
753 use super::*;
Jaewan Kim52477ae2023-11-21 21:20:52 +0900754 use alloc::collections::{BTreeMap, BTreeSet};
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900755 use std::fs;
756
757 const VM_DTBO_FILE_PATH: &str = "test_pvmfw_devices_vm_dtbo.dtbo";
758 const VM_DTBO_WITHOUT_SYMBOLS_FILE_PATH: &str =
759 "test_pvmfw_devices_vm_dtbo_without_symbols.dtbo";
Jaewan Kim19b984f2023-12-04 15:16:50 +0900760 const VM_DTBO_WITH_DUPLICATED_IOMMUS_FILE_PATH: &str =
761 "test_pvmfw_devices_vm_dtbo_with_duplicated_iommus.dtbo";
Jaewan Kima67e36a2023-11-29 16:50:23 +0900762 const FDT_WITHOUT_IOMMUS_FILE_PATH: &str = "test_pvmfw_devices_without_iommus.dtb";
Jaewan Kim52477ae2023-11-21 21:20:52 +0900763 const FDT_WITHOUT_DEVICE_FILE_PATH: &str = "test_pvmfw_devices_without_device.dtb";
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900764 const FDT_FILE_PATH: &str = "test_pvmfw_devices_with_rng.dtb";
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900765 const FDT_WITH_MULTIPLE_DEVICES_IOMMUS_FILE_PATH: &str =
766 "test_pvmfw_devices_with_multiple_devices_iommus.dtb";
767 const FDT_WITH_IOMMU_SHARING: &str = "test_pvmfw_devices_with_iommu_sharing.dtb";
768 const FDT_WITH_IOMMU_ID_CONFLICT: &str = "test_pvmfw_devices_with_iommu_id_conflict.dtb";
Jaewan Kim19b984f2023-12-04 15:16:50 +0900769 const FDT_WITH_DUPLICATED_PVIOMMUS_FILE_PATH: &str =
770 "test_pvmfw_devices_with_duplicated_pviommus.dtb";
771 const FDT_WITH_MULTIPLE_REG_IOMMU_FILE_PATH: &str =
772 "test_pvmfw_devices_with_multiple_reg_iommus.dtb";
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900773
Jaewan Kim52477ae2023-11-21 21:20:52 +0900774 #[derive(Debug, Default)]
775 struct MockHypervisor {
776 mmio_tokens: BTreeMap<(u64, u64), u64>,
777 iommu_tokens: BTreeMap<(u64, u64), (u64, u64)>,
778 }
779
780 impl DeviceAssigningHypervisor for MockHypervisor {
781 fn get_phys_mmio_token(&self, base_ipa: u64, size: u64) -> hyp::Result<u64> {
782 Ok(*self.mmio_tokens.get(&(base_ipa, size)).ok_or(hyp::Error::KvmError(
783 hyp::KvmError::InvalidParameter,
784 0xc6000012, /* VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID */
785 ))?)
786 }
787
788 fn get_phys_iommu_token(&self, pviommu_id: u64, vsid: u64) -> hyp::Result<(u64, u64)> {
789 Ok(*self.iommu_tokens.get(&(pviommu_id, vsid)).ok_or(hyp::Error::KvmError(
790 hyp::KvmError::InvalidParameter,
791 0xc6000013, /* VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID */
792 ))?)
793 }
794 }
795
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900796 #[derive(Debug, Eq, PartialEq)]
797 struct AssignedDeviceNode {
798 path: CString,
799 reg: Vec<u8>,
800 interrupts: Vec<u8>,
Jaewan Kima67e36a2023-11-29 16:50:23 +0900801 iommus: Vec<u32>, // pvIOMMU id and vSID
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900802 }
803
804 impl AssignedDeviceNode {
805 fn parse(fdt: &Fdt, path: &CStr) -> Result<Self> {
806 let Some(node) = fdt.node(path)? else {
807 return Err(FdtError::NotFound.into());
808 };
809
Jaewan Kim19b984f2023-12-04 15:16:50 +0900810 let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::MalformedReg)?;
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900811 let interrupts = node
812 .getprop(cstr!("interrupts"))?
813 .ok_or(DeviceAssignmentError::InvalidInterrupts)?;
814 let mut iommus = vec![];
Jaewan Kima9200492023-11-21 20:45:31 +0900815 if let Some(mut cells) = node.getprop_cells(cstr!("iommus"))? {
816 while let Some(pviommu_id) = cells.next() {
817 // pvIOMMU id
818 let phandle = Phandle::try_from(pviommu_id)?;
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900819 let pviommu = fdt
820 .node_with_phandle(phandle)?
Jaewan Kim19b984f2023-12-04 15:16:50 +0900821 .ok_or(DeviceAssignmentError::MalformedIommus)?;
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900822 let compatible = pviommu.getprop_str(cstr!("compatible"));
823 if compatible != Ok(Some(cstr!("pkvm,pviommu"))) {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900824 return Err(DeviceAssignmentError::MalformedIommus);
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900825 }
826 let id = pviommu
827 .getprop_u32(cstr!("id"))?
Jaewan Kim19b984f2023-12-04 15:16:50 +0900828 .ok_or(DeviceAssignmentError::MalformedIommus)?;
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900829 iommus.push(id);
Jaewan Kima9200492023-11-21 20:45:31 +0900830
831 // vSID
832 let Some(vsid) = cells.next() else {
Jaewan Kim19b984f2023-12-04 15:16:50 +0900833 return Err(DeviceAssignmentError::MalformedIommus);
Jaewan Kima9200492023-11-21 20:45:31 +0900834 };
835 iommus.push(vsid);
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900836 }
837 }
838 Ok(Self { path: path.into(), reg: reg.into(), interrupts: interrupts.into(), iommus })
839 }
840 }
841
842 fn collect_pviommus(fdt: &Fdt) -> Result<Vec<u32>> {
843 let mut pviommus = BTreeSet::new();
844 for pviommu in fdt.compatible_nodes(cstr!("pkvm,pviommu"))? {
845 if let Ok(Some(id)) = pviommu.getprop_u32(cstr!("id")) {
846 pviommus.insert(id);
847 }
848 }
849 Ok(pviommus.iter().cloned().collect())
850 }
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900851
852 fn into_fdt_prop(native_bytes: Vec<u32>) -> Vec<u8> {
853 let mut v = Vec::with_capacity(native_bytes.len() * 4);
854 for byte in native_bytes {
855 v.extend_from_slice(&byte.to_be_bytes());
856 }
857 v
858 }
859
Jaewan Kim52477ae2023-11-21 21:20:52 +0900860 impl From<[u64; 2]> for DeviceReg {
861 fn from(fdt_cells: [u64; 2]) -> Self {
862 DeviceReg { addr: fdt_cells[0], size: fdt_cells[1] }
863 }
864 }
865
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900866 #[test]
867 fn device_info_new_without_symbols() {
868 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
869 let mut vm_dtbo_data = fs::read(VM_DTBO_WITHOUT_SYMBOLS_FILE_PATH).unwrap();
870 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
871 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
872
Jaewan Kim52477ae2023-11-21 21:20:52 +0900873 let hypervisor: MockHypervisor = Default::default();
874 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
875 assert_eq!(device_info, None);
876 }
877
878 #[test]
879 fn device_info_new_without_device() {
880 let mut fdt_data = fs::read(FDT_WITHOUT_DEVICE_FILE_PATH).unwrap();
881 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
882 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
883 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
884
885 let hypervisor: MockHypervisor = Default::default();
886 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900887 assert_eq!(device_info, None);
888 }
889
890 #[test]
Jaewan Kima67e36a2023-11-29 16:50:23 +0900891 fn device_info_assigned_info_without_iommus() {
892 let mut fdt_data = fs::read(FDT_WITHOUT_IOMMUS_FILE_PATH).unwrap();
893 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
894 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
895 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
896
Jaewan Kim52477ae2023-11-21 21:20:52 +0900897 let hypervisor = MockHypervisor {
898 mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
899 iommu_tokens: BTreeMap::new(),
900 };
901 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kima67e36a2023-11-29 16:50:23 +0900902
903 let expected = [AssignedDeviceInfo {
Jaewan Kimc39974e2023-12-02 01:13:30 +0900904 node_path: CString::new("/bus0/backlight").unwrap(),
905 dtbo_node_path: cstr!("/fragment@backlight/__overlay__/bus0/backlight").into(),
Jaewan Kim52477ae2023-11-21 21:20:52 +0900906 reg: vec![[0x9, 0xFF].into()],
Jaewan Kima67e36a2023-11-29 16:50:23 +0900907 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
908 iommus: vec![],
909 }];
910
911 assert_eq!(device_info.assigned_devices, expected);
912 }
913
914 #[test]
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900915 fn device_info_assigned_info() {
916 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
917 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
918 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
919 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
920
Jaewan Kim52477ae2023-11-21 21:20:52 +0900921 let hypervisor = MockHypervisor {
922 mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
923 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
924 };
925 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900926
927 let expected = [AssignedDeviceInfo {
928 node_path: CString::new("/rng").unwrap(),
929 dtbo_node_path: cstr!("/fragment@rng/__overlay__/rng").into(),
Jaewan Kim52477ae2023-11-21 21:20:52 +0900930 reg: vec![[0x9, 0xFF].into()],
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900931 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
Jaewan Kima67e36a2023-11-29 16:50:23 +0900932 iommus: vec![(PvIommu { id: 0x4 }, Vsid(0xFF0))],
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900933 }];
934
935 assert_eq!(device_info.assigned_devices, expected);
936 }
937
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900938 #[test]
939 fn device_info_filter() {
940 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
941 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
942 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
943 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
944
Jaewan Kim52477ae2023-11-21 21:20:52 +0900945 let hypervisor = MockHypervisor {
946 mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
947 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
948 };
949 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900950 device_info.filter(vm_dtbo).unwrap();
951
952 let vm_dtbo = vm_dtbo.as_mut();
953
954 let rng = vm_dtbo.node(cstr!("/fragment@rng/__overlay__/rng")).unwrap();
955 assert_ne!(rng, None);
956
957 let light = vm_dtbo.node(cstr!("/fragment@rng/__overlay__/light")).unwrap();
958 assert_eq!(light, None);
959
Jaewan Kima67e36a2023-11-29 16:50:23 +0900960 let led = vm_dtbo.node(cstr!("/fragment@led/__overlay__/led")).unwrap();
961 assert_eq!(led, None);
962
Jaewan Kimc39974e2023-12-02 01:13:30 +0900963 let backlight =
964 vm_dtbo.node(cstr!("/fragment@backlight/__overlay__/bus0/backlight")).unwrap();
Jaewan Kima67e36a2023-11-29 16:50:23 +0900965 assert_eq!(backlight, None);
966
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900967 let symbols_node = vm_dtbo.symbols().unwrap();
968 assert_eq!(symbols_node, None);
969 }
970
971 #[test]
972 fn device_info_patch() {
Jaewan Kima67e36a2023-11-29 16:50:23 +0900973 let mut fdt_data = fs::read(FDT_WITHOUT_IOMMUS_FILE_PATH).unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900974 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
975 let mut data = vec![0_u8; fdt_data.len() + vm_dtbo_data.len()];
976 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
977 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
978 let platform_dt = Fdt::create_empty_tree(data.as_mut_slice()).unwrap();
979
Jaewan Kim52477ae2023-11-21 21:20:52 +0900980 let hypervisor = MockHypervisor {
981 mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
982 iommu_tokens: BTreeMap::new(),
983 };
984 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900985 device_info.filter(vm_dtbo).unwrap();
986
987 // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
988 unsafe {
989 platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
990 }
Jaewan Kim0bd637d2023-11-10 13:09:41 +0900991 device_info.patch(platform_dt).unwrap();
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900992
Jaewan Kimc39974e2023-12-02 01:13:30 +0900993 let rng_node = platform_dt.node(cstr!("/bus0/backlight")).unwrap().unwrap();
994 let phandle = rng_node.getprop_u32(cstr!("phandle")).unwrap();
995 assert_ne!(None, phandle);
996
Jaewan Kim51ccfed2023-11-08 13:51:58 +0900997 // Note: Intentionally not using AssignedDeviceNode for matching all props.
Jaewan Kim0bd637d2023-11-10 13:09:41 +0900998 type FdtResult<T> = libfdt::Result<T>;
999 let expected: Vec<(FdtResult<&CStr>, FdtResult<Vec<u8>>)> = vec![
Jaewan Kima67e36a2023-11-29 16:50:23 +09001000 (Ok(cstr!("android,backlight,ignore-gctrl-reset")), Ok(Vec::new())),
1001 (Ok(cstr!("compatible")), Ok(Vec::from(*b"android,backlight\0"))),
Jaewan Kim0bd637d2023-11-10 13:09:41 +09001002 (Ok(cstr!("interrupts")), Ok(into_fdt_prop(vec![0x0, 0xF, 0x4]))),
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001003 (Ok(cstr!("iommus")), Ok(Vec::new())),
Jaewan Kimc39974e2023-12-02 01:13:30 +09001004 (Ok(cstr!("phandle")), Ok(into_fdt_prop(vec![phandle.unwrap()]))),
Jaewan Kim0bd637d2023-11-10 13:09:41 +09001005 (Ok(cstr!("reg")), Ok(into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]))),
Jaewan Kimc6e023b2023-10-12 15:11:05 +09001006 ];
1007
Jaewan Kim0bd637d2023-11-10 13:09:41 +09001008 let mut properties: Vec<_> = rng_node
1009 .properties()
1010 .unwrap()
1011 .map(|prop| (prop.name(), prop.value().map(|x| x.into())))
1012 .collect();
1013 properties.sort_by(|a, b| {
1014 let lhs = a.0.unwrap_or_default();
1015 let rhs = b.0.unwrap_or_default();
1016 lhs.partial_cmp(rhs).unwrap()
1017 });
1018
1019 assert_eq!(properties, expected);
Jaewan Kimc6e023b2023-10-12 15:11:05 +09001020 }
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001021
1022 #[test]
1023 fn device_info_overlay_iommu() {
Jaewan Kima67e36a2023-11-29 16:50:23 +09001024 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001025 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1026 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1027 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1028 let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
1029 platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
1030 let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
1031 platform_dt.unpack().unwrap();
1032
Jaewan Kim52477ae2023-11-21 21:20:52 +09001033 let hypervisor = MockHypervisor {
1034 mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
1035 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
1036 };
1037 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001038 device_info.filter(vm_dtbo).unwrap();
1039
1040 // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
1041 unsafe {
1042 platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
1043 }
1044 device_info.patch(platform_dt).unwrap();
1045
1046 let expected = AssignedDeviceNode {
1047 path: CString::new("/rng").unwrap(),
1048 reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
1049 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
Jaewan Kima9200492023-11-21 20:45:31 +09001050 iommus: vec![0x4, 0xFF0],
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001051 };
1052
1053 let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
1054 assert_eq!(node, Ok(expected));
1055
1056 let pviommus = collect_pviommus(platform_dt);
1057 assert_eq!(pviommus, Ok(vec![0x4]));
1058 }
1059
1060 #[test]
1061 fn device_info_multiple_devices_iommus() {
1062 let mut fdt_data = fs::read(FDT_WITH_MULTIPLE_DEVICES_IOMMUS_FILE_PATH).unwrap();
1063 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1064 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1065 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1066 let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
1067 platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
1068 let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
1069 platform_dt.unpack().unwrap();
1070
Jaewan Kim52477ae2023-11-21 21:20:52 +09001071 let hypervisor = MockHypervisor {
1072 mmio_tokens: [
1073 ((0x9, 0xFF), 0x12F00000),
Jaewan Kim19b984f2023-12-04 15:16:50 +09001074 ((0x10000, 0x1000), 0xF00000),
1075 ((0x20000, 0x1000), 0xF10000),
Jaewan Kim52477ae2023-11-21 21:20:52 +09001076 ]
1077 .into(),
1078 iommu_tokens: [
1079 ((0x4, 0xFF0), (0x12E40000, 3)),
1080 ((0x40, 0xFFA), (0x40000, 0x4)),
1081 ((0x50, 0xFFB), (0x50000, 0x5)),
1082 ]
1083 .into(),
1084 };
1085 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001086 device_info.filter(vm_dtbo).unwrap();
1087
1088 // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
1089 unsafe {
1090 platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
1091 }
1092 device_info.patch(platform_dt).unwrap();
1093
1094 let expected_devices = [
1095 AssignedDeviceNode {
1096 path: CString::new("/rng").unwrap(),
1097 reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
1098 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
Jaewan Kima67e36a2023-11-29 16:50:23 +09001099 iommus: vec![0x4, 0xFF0],
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001100 },
1101 AssignedDeviceNode {
1102 path: CString::new("/light").unwrap(),
Jaewan Kim19b984f2023-12-04 15:16:50 +09001103 reg: into_fdt_prop(vec![0x0, 0x10000, 0x0, 0x1000, 0x0, 0x20000, 0x0, 0x1000]),
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001104 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
Jaewan Kima67e36a2023-11-29 16:50:23 +09001105 iommus: vec![0x40, 0xFFA, 0x50, 0xFFB],
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001106 },
1107 ];
1108
1109 for expected in expected_devices {
1110 let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
1111 assert_eq!(node, Ok(expected));
1112 }
1113 let pviommus = collect_pviommus(platform_dt);
Jaewan Kima67e36a2023-11-29 16:50:23 +09001114 assert_eq!(pviommus, Ok(vec![0x4, 0x40, 0x50]));
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001115 }
1116
1117 #[test]
1118 fn device_info_iommu_sharing() {
1119 let mut fdt_data = fs::read(FDT_WITH_IOMMU_SHARING).unwrap();
1120 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1121 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1122 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1123 let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
1124 platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
1125 let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
1126 platform_dt.unpack().unwrap();
1127
Jaewan Kim52477ae2023-11-21 21:20:52 +09001128 let hypervisor = MockHypervisor {
Jaewan Kim19b984f2023-12-04 15:16:50 +09001129 mmio_tokens: [((0x9, 0xFF), 0x12F00000), ((0x1000, 0x9), 0x12000000)].into(),
1130 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 3)), ((0x4, 0xFF1), (0x12E40000, 9))].into(),
Jaewan Kim52477ae2023-11-21 21:20:52 +09001131 };
1132 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001133 device_info.filter(vm_dtbo).unwrap();
1134
1135 // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
1136 unsafe {
1137 platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
1138 }
1139 device_info.patch(platform_dt).unwrap();
1140
1141 let expected_devices = [
1142 AssignedDeviceNode {
1143 path: CString::new("/rng").unwrap(),
1144 reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
1145 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
Jaewan Kima67e36a2023-11-29 16:50:23 +09001146 iommus: vec![0x4, 0xFF0],
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001147 },
1148 AssignedDeviceNode {
Jaewan Kima67e36a2023-11-29 16:50:23 +09001149 path: CString::new("/led").unwrap(),
Jaewan Kim19b984f2023-12-04 15:16:50 +09001150 reg: into_fdt_prop(vec![0x0, 0x1000, 0x0, 0x9]),
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001151 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
Jaewan Kim19b984f2023-12-04 15:16:50 +09001152 iommus: vec![0x4, 0xFF1],
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001153 },
1154 ];
1155
1156 for expected in expected_devices {
1157 let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
1158 assert_eq!(node, Ok(expected));
1159 }
1160
1161 let pviommus = collect_pviommus(platform_dt);
Jaewan Kima67e36a2023-11-29 16:50:23 +09001162 assert_eq!(pviommus, Ok(vec![0x4]));
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001163 }
1164
1165 #[test]
1166 fn device_info_iommu_id_conflict() {
1167 let mut fdt_data = fs::read(FDT_WITH_IOMMU_ID_CONFLICT).unwrap();
1168 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1169 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1170 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1171
Jaewan Kim52477ae2023-11-21 21:20:52 +09001172 let hypervisor = MockHypervisor {
Jaewan Kim19b984f2023-12-04 15:16:50 +09001173 mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
Jaewan Kim52477ae2023-11-21 21:20:52 +09001174 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
1175 };
1176 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
Jaewan Kim51ccfed2023-11-08 13:51:58 +09001177
1178 assert_eq!(device_info, Err(DeviceAssignmentError::DuplicatedPvIommuIds));
1179 }
Jaewan Kim52477ae2023-11-21 21:20:52 +09001180
1181 #[test]
1182 fn device_info_invalid_reg() {
1183 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
1184 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1185 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1186 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1187
1188 let hypervisor = MockHypervisor {
1189 mmio_tokens: BTreeMap::new(),
1190 iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
1191 };
1192 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1193
1194 assert_eq!(device_info, Err(DeviceAssignmentError::InvalidReg));
1195 }
1196
1197 #[test]
Jaewan Kim19b984f2023-12-04 15:16:50 +09001198 fn device_info_invalid_reg_out_of_order() {
1199 let mut fdt_data = fs::read(FDT_WITH_MULTIPLE_REG_IOMMU_FILE_PATH).unwrap();
1200 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1201 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1202 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1203
1204 let hypervisor = MockHypervisor {
1205 mmio_tokens: [((0xF000, 0x1000), 0xF10000), ((0xF100, 0x1000), 0xF00000)].into(),
1206 iommu_tokens: [((0xFF0, 0xF0), (0x40000, 0x4)), ((0xFF1, 0xF1), (0x50000, 0x5))].into(),
1207 };
1208 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1209
1210 assert_eq!(device_info, Err(DeviceAssignmentError::InvalidReg));
1211 }
1212
1213 #[test]
Jaewan Kim52477ae2023-11-21 21:20:52 +09001214 fn device_info_invalid_iommus() {
1215 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
1216 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1217 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1218 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1219
1220 let hypervisor = MockHypervisor {
1221 mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
1222 iommu_tokens: BTreeMap::new(),
1223 };
1224 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1225
1226 assert_eq!(device_info, Err(DeviceAssignmentError::InvalidIommus));
1227 }
Jaewan Kim19b984f2023-12-04 15:16:50 +09001228
1229 #[test]
1230 fn device_info_duplicated_pv_iommus() {
1231 let mut fdt_data = fs::read(FDT_WITH_DUPLICATED_PVIOMMUS_FILE_PATH).unwrap();
1232 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1233 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1234 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1235
1236 let hypervisor = MockHypervisor {
1237 mmio_tokens: [((0x10000, 0x1000), 0xF00000), ((0x20000, 0xFF), 0xF10000)].into(),
1238 iommu_tokens: [((0xFF, 0xF), (0x40000, 0x4))].into(),
1239 };
1240 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1241
1242 assert_eq!(device_info, Err(DeviceAssignmentError::DuplicatedPvIommuIds));
1243 }
1244
1245 #[test]
1246 fn device_info_duplicated_iommus() {
1247 let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
1248 let mut vm_dtbo_data = fs::read(VM_DTBO_WITH_DUPLICATED_IOMMUS_FILE_PATH).unwrap();
1249 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1250 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1251
1252 let hypervisor = MockHypervisor {
1253 mmio_tokens: [((0x10000, 0x1000), 0xF00000), ((0x20000, 0xFF), 0xF10000)].into(),
1254 iommu_tokens: [((0xFF, 0xF), (0x40000, 0x4))].into(),
1255 };
1256 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1257
1258 assert_eq!(device_info, Err(DeviceAssignmentError::UnsupportedIommusDuplication));
1259 }
1260
1261 #[test]
1262 fn device_info_duplicated_iommu_mapping() {
1263 let mut fdt_data = fs::read(FDT_WITH_MULTIPLE_REG_IOMMU_FILE_PATH).unwrap();
1264 let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
1265 let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
1266 let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
1267
1268 let hypervisor = MockHypervisor {
1269 mmio_tokens: [((0xF000, 0x1000), 0xF00000), ((0xF100, 0x1000), 0xF10000)].into(),
1270 iommu_tokens: [((0xFF0, 0xF0), (0x40000, 0x4)), ((0xFF1, 0xF1), (0x40000, 0x4))].into(),
1271 };
1272 let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
1273
1274 assert_eq!(device_info, Err(DeviceAssignmentError::InvalidIommus));
1275 }
Jaewan Kimc6e023b2023-10-12 15:11:05 +09001276}