1use std::{u16, u32, u64};
16
17pub enum ResourceConstraint {
19 PioAddress {
21 range: Option<(u16, u16)>,
23 align: u16,
25 size: u16,
27 },
28 MmioAddress {
30 range: Option<(u64, u64)>,
32 align: u64,
34 size: u64,
36 },
37 LegacyIrq {
39 irq: Option<u32>,
41 },
42 PciMsiIrq {
44 size: u32,
46 },
47 PciMsixIrq {
49 size: u32,
51 },
52 GenericIrq {
54 size: u32,
56 },
57 KvmMemSlot {
59 slot: Option<u32>,
61 size: u32,
63 },
64}
65
66impl ResourceConstraint {
67 pub fn new_pio(size: u16) -> Self {
69 ResourceConstraint::PioAddress {
70 range: None,
71 align: 0x1,
72 size,
73 }
74 }
75
76 pub fn pio_with_constraints(size: u16, range: Option<(u16, u16)>, align: u16) -> Self {
78 ResourceConstraint::PioAddress { range, align, size }
79 }
80
81 pub fn new_mmio(size: u64) -> Self {
83 ResourceConstraint::MmioAddress {
84 range: None,
85 align: 0x1000,
86 size,
87 }
88 }
89
90 pub fn mmio_with_constraints(size: u64, range: Option<(u64, u64)>, align: u64) -> Self {
92 ResourceConstraint::MmioAddress { range, align, size }
93 }
94
95 pub fn new_legacy_irq(irq: Option<u32>) -> Self {
99 ResourceConstraint::LegacyIrq { irq }
100 }
101
102 pub fn new_kvm_mem_slot(size: u32, slot: Option<u32>) -> Self {
106 ResourceConstraint::KvmMemSlot { slot, size }
107 }
108}
109
110#[derive(Copy, Clone, PartialEq)]
112pub enum MsiIrqType {
113 PciMsi,
115 PciMsix,
117 GenericMsi,
119}
120
121#[allow(missing_docs)]
123#[derive(Clone)]
124pub enum Resource {
125 PioAddressRange { base: u16, size: u16 },
127 MmioAddressRange { base: u64, size: u64 },
129 LegacyIrq(u32),
131 MsiIrq {
133 ty: MsiIrqType,
134 base: u32,
135 size: u32,
136 },
137 MacAddresss(String),
139 KvmMemSlot(u32),
141}
142
143#[derive(Default, Clone)]
145pub struct DeviceResources(Vec<Resource>);
146
147impl DeviceResources {
148 pub fn new() -> Self {
150 DeviceResources(Vec::new())
151 }
152
153 pub fn append(&mut self, entry: Resource) {
155 self.0.push(entry);
156 }
157
158 pub fn get_pio_address_ranges(&self) -> Vec<(u16, u16)> {
160 let mut vec = Vec::new();
161 for entry in self.0.iter().as_ref() {
162 if let Resource::PioAddressRange { base, size } = entry {
163 vec.push((*base, *size));
164 }
165 }
166 vec
167 }
168
169 pub fn get_mmio_address_ranges(&self) -> Vec<(u64, u64)> {
171 let mut vec = Vec::new();
172 for entry in self.0.iter().as_ref() {
173 if let Resource::MmioAddressRange { base, size } = entry {
174 vec.push((*base, *size));
175 }
176 }
177 vec
178 }
179
180 pub fn get_legacy_irq(&self) -> Option<u32> {
182 for entry in self.0.iter().as_ref() {
183 if let Resource::LegacyIrq(base) = entry {
184 return Some(*base);
185 }
186 }
187 None
188 }
189
190 pub fn get_pci_msi_irqs(&self) -> Option<(u32, u32)> {
192 self.get_msi_irqs(MsiIrqType::PciMsi)
193 }
194
195 pub fn get_pci_msix_irqs(&self) -> Option<(u32, u32)> {
197 self.get_msi_irqs(MsiIrqType::PciMsix)
198 }
199
200 pub fn get_generic_msi_irqs(&self) -> Option<(u32, u32)> {
202 self.get_msi_irqs(MsiIrqType::GenericMsi)
203 }
204
205 fn get_msi_irqs(&self, ty: MsiIrqType) -> Option<(u32, u32)> {
206 for entry in self.0.iter().as_ref() {
207 if let Resource::MsiIrq {
208 ty: msi_type,
209 base,
210 size,
211 } = entry
212 {
213 if ty == *msi_type {
214 return Some((*base, *size));
215 }
216 }
217 }
218 None
219 }
220
221 pub fn get_kvm_mem_slots(&self) -> Vec<u32> {
223 let mut vec = Vec::new();
224 for entry in self.0.iter().as_ref() {
225 if let Resource::KvmMemSlot(index) = entry {
226 vec.push(*index);
227 }
228 }
229 vec
230 }
231
232 pub fn get_mac_address(&self) -> Option<String> {
234 for entry in self.0.iter().as_ref() {
235 if let Resource::MacAddresss(addr) = entry {
236 return Some(addr.clone());
237 }
238 }
239 None
240 }
241
242 pub fn get_all_resources(&self) -> &[Resource] {
244 &self.0
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 const PIO_ADDRESS_SIZE: u16 = 5;
253 const PIO_ADDRESS_BASE: u16 = 0;
254 const MMIO_ADDRESS_SIZE: u64 = 0x8765_4321;
255 const MMIO_ADDRESS_BASE: u64 = 0x1234_5678;
256 const LEGACY_IRQ: u32 = 0x168;
257 const PCI_MSI_IRQ_SIZE: u32 = 0x8888;
258 const PCI_MSI_IRQ_BASE: u32 = 0x6666;
259 const PCI_MSIX_IRQ_SIZE: u32 = 0x16666;
260 const PCI_MSIX_IRQ_BASE: u32 = 0x8888;
261 const GENERIC_MSI_IRQS_SIZE: u32 = 0x16888;
262 const GENERIC_MSI_IRQS_BASE: u32 = 0x16688;
263 const MAC_ADDRESS: &str = "00:08:63:66:86:88";
264 const KVM_SLOT_ID: u32 = 0x0100;
265
266 fn get_device_resource() -> DeviceResources {
267 let entry = Resource::PioAddressRange {
268 base: PIO_ADDRESS_BASE,
269 size: PIO_ADDRESS_SIZE,
270 };
271 let mut resource = DeviceResources::new();
272 resource.append(entry);
273 let entry = Resource::MmioAddressRange {
274 base: MMIO_ADDRESS_BASE,
275 size: MMIO_ADDRESS_SIZE,
276 };
277 resource.append(entry);
278 let entry = Resource::LegacyIrq(LEGACY_IRQ);
279 resource.append(entry);
280 let entry = Resource::MsiIrq {
281 ty: MsiIrqType::PciMsi,
282 base: PCI_MSI_IRQ_BASE,
283 size: PCI_MSI_IRQ_SIZE,
284 };
285 resource.append(entry);
286 let entry = Resource::MsiIrq {
287 ty: MsiIrqType::PciMsix,
288 base: PCI_MSIX_IRQ_BASE,
289 size: PCI_MSIX_IRQ_SIZE,
290 };
291 resource.append(entry);
292 let entry = Resource::MsiIrq {
293 ty: MsiIrqType::GenericMsi,
294 base: GENERIC_MSI_IRQS_BASE,
295 size: GENERIC_MSI_IRQS_SIZE,
296 };
297 resource.append(entry);
298 let entry = Resource::MacAddresss(MAC_ADDRESS.to_string());
299 resource.append(entry);
300
301 resource.append(Resource::KvmMemSlot(KVM_SLOT_ID));
302
303 resource
304 }
305
306 #[test]
307 fn get_pio_address_ranges() {
308 let resources = get_device_resource();
309 assert!(
310 resources.get_pio_address_ranges()[0].0 == PIO_ADDRESS_BASE
311 && resources.get_pio_address_ranges()[0].1 == PIO_ADDRESS_SIZE
312 );
313 }
314
315 #[test]
316 fn test_get_mmio_address_ranges() {
317 let resources = get_device_resource();
318 assert!(
319 resources.get_mmio_address_ranges()[0].0 == MMIO_ADDRESS_BASE
320 && resources.get_mmio_address_ranges()[0].1 == MMIO_ADDRESS_SIZE
321 );
322 }
323
324 #[test]
325 fn test_get_legacy_irq() {
326 let resources = get_device_resource();
327 assert!(resources.get_legacy_irq().unwrap() == LEGACY_IRQ);
328 }
329
330 #[test]
331 fn test_get_pci_msi_irqs() {
332 let resources = get_device_resource();
333 assert!(
334 resources.get_pci_msi_irqs().unwrap().0 == PCI_MSI_IRQ_BASE
335 && resources.get_pci_msi_irqs().unwrap().1 == PCI_MSI_IRQ_SIZE
336 );
337 }
338
339 #[test]
340 fn test_pci_msix_irqs() {
341 let resources = get_device_resource();
342 assert!(
343 resources.get_pci_msix_irqs().unwrap().0 == PCI_MSIX_IRQ_BASE
344 && resources.get_pci_msix_irqs().unwrap().1 == PCI_MSIX_IRQ_SIZE
345 );
346 }
347
348 #[test]
349 fn test_get_generic_msi_irqs() {
350 let resources = get_device_resource();
351 assert!(
352 resources.get_generic_msi_irqs().unwrap().0 == GENERIC_MSI_IRQS_BASE
353 && resources.get_generic_msi_irqs().unwrap().1 == GENERIC_MSI_IRQS_SIZE
354 );
355 }
356
357 #[test]
358 fn test_get_mac_address() {
359 let resources = get_device_resource();
360 assert_eq!(resources.get_mac_address().unwrap(), MAC_ADDRESS);
361 }
362
363 #[test]
364 fn test_get_kvm_slot() {
365 let resources = get_device_resource();
366 assert_eq!(resources.get_kvm_mem_slots(), vec![KVM_SLOT_ID]);
367 }
368
369 #[test]
370 fn test_get_all_resources() {
371 let resources = get_device_resource();
372 assert_eq!(resources.get_all_resources().len(), 8);
373 }
374
375 #[test]
376 fn test_resource_constraint() {
377 if let ResourceConstraint::PioAddress { range, align, size } =
378 ResourceConstraint::new_pio(2)
379 {
380 assert_eq!(range, None);
381 assert_eq!(align, 1);
382 assert_eq!(size, 2);
383 } else {
384 panic!("Pio resource constraint is invalid.");
385 }
386
387 if let ResourceConstraint::PioAddress { range, align, size } =
388 ResourceConstraint::pio_with_constraints(2, Some((15, 16)), 2)
389 {
390 assert_eq!(range, Some((15, 16)));
391 assert_eq!(align, 2);
392 assert_eq!(size, 2);
393 } else {
394 panic!("Pio resource constraint is invalid.");
395 }
396
397 if let ResourceConstraint::MmioAddress { range, align, size } =
398 ResourceConstraint::new_mmio(0x2000)
399 {
400 assert_eq!(range, None);
401 assert_eq!(align, 0x1000);
402 assert_eq!(size, 0x2000);
403 } else {
404 panic!("Mmio resource constraint is invalid.");
405 }
406
407 if let ResourceConstraint::MmioAddress { range, align, size } =
408 ResourceConstraint::mmio_with_constraints(0x2000, Some((0x0, 0x2000)), 0x2000)
409 {
410 assert_eq!(range, Some((0x0, 0x2000)));
411 assert_eq!(align, 0x2000);
412 assert_eq!(size, 0x2000);
413 } else {
414 panic!("Mmio resource constraint is invalid.");
415 }
416
417 if let ResourceConstraint::LegacyIrq { irq } =
418 ResourceConstraint::new_legacy_irq(Some(0x123))
419 {
420 assert_eq!(irq, Some(0x123));
421 } else {
422 panic!("IRQ resource constraint is invalid.");
423 }
424
425 if let ResourceConstraint::KvmMemSlot { slot, size } =
426 ResourceConstraint::new_kvm_mem_slot(0x1000, Some(0x2000))
427 {
428 assert_eq!(slot, Some(0x2000));
429 assert_eq!(size, 0x1000);
430 } else {
431 panic!("KVM slot resource constraint is invalid.");
432 }
433 }
434}