1use std::ops::Deref;
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27pub enum ResourceConstraint {
28 PioAddress {
30 range: Option<(u16, u16)>,
32 align: u16,
34 size: u16,
36 },
37 MmioAddress {
39 range: Option<(u64, u64)>,
41 align: u64,
43 size: u64,
45 },
46 MemAddress {
48 range: Option<(u64, u64)>,
50 align: u64,
52 size: u64,
54 },
55 LegacyIrq {
57 irq: Option<u32>,
59 },
60 PciMsiIrq {
62 size: u32,
64 },
65 PciMsixIrq {
67 size: u32,
69 },
70 GenericIrq {
72 size: u32,
74 },
75 KvmMemSlot {
77 slot: Option<u32>,
80 size: u32,
82 },
83}
84
85impl ResourceConstraint {
86 pub fn new_pio(size: u16) -> Self {
88 ResourceConstraint::PioAddress {
89 range: None,
90 align: 0x1,
91 size,
92 }
93 }
94
95 pub fn pio_with_constraints(size: u16, range: Option<(u16, u16)>, align: u16) -> Self {
97 ResourceConstraint::PioAddress { range, align, size }
98 }
99
100 pub fn new_mmio(size: u64) -> Self {
102 ResourceConstraint::MmioAddress {
103 range: None,
104 align: 0x1000,
105 size,
106 }
107 }
108
109 pub fn mmio_with_constraints(size: u64, range: Option<(u64, u64)>, align: u64) -> Self {
111 ResourceConstraint::MmioAddress { range, align, size }
112 }
113
114 pub fn new_mem(size: u64) -> Self {
116 ResourceConstraint::MemAddress {
117 range: None,
118 align: 0x1000,
119 size,
120 }
121 }
122
123 pub fn mem_with_constraints(size: u64, range: Option<(u64, u64)>, align: u64) -> Self {
125 ResourceConstraint::MemAddress { range, align, size }
126 }
127
128 pub fn new_legacy_irq(irq: Option<u32>) -> Self {
132 ResourceConstraint::LegacyIrq { irq }
133 }
134
135 pub fn new_pci_msi_irq(size: u32) -> Self {
137 ResourceConstraint::PciMsiIrq { size }
138 }
139
140 pub fn new_pci_msix_irq(size: u32) -> Self {
142 ResourceConstraint::PciMsixIrq { size }
143 }
144
145 pub fn new_generic_irq(size: u32) -> Self {
147 ResourceConstraint::GenericIrq { size }
148 }
149
150 pub fn new_kvm_mem_slot(size: u32, slot: Option<u32>) -> Self {
154 ResourceConstraint::KvmMemSlot { slot, size }
155 }
156}
157
158#[derive(Copy, Clone, Debug, Eq, PartialEq)]
160pub enum MsiIrqType {
161 PciMsi,
163 PciMsix,
165 GenericMsi,
167}
168
169#[derive(Clone, Debug, Eq, PartialEq)]
171pub enum Resource {
172 PioAddressRange {
174 base: u16,
176 size: u16,
178 },
179 MmioAddressRange {
181 base: u64,
183 size: u64,
185 },
186 MemAddressRange {
188 base: u64,
190 size: u64,
192 },
193 LegacyIrq(u32),
195 MsiIrq {
197 ty: MsiIrqType,
199 base: u32,
201 size: u32,
203 },
204 MacAddresss(String),
206 KvmMemSlot(u32),
208}
209
210#[derive(Clone, Debug, Default, Eq, PartialEq)]
212pub struct DeviceResources(Vec<Resource>);
213
214impl DeviceResources {
215 pub fn new() -> Self {
217 DeviceResources(Vec::new())
218 }
219
220 pub fn append(&mut self, entry: Resource) {
222 self.0.push(entry);
223 }
224
225 pub fn get_pio_address_ranges(&self) -> Vec<(u16, u16)> {
227 let mut vec = Vec::new();
228 for entry in self.0.iter().as_ref() {
229 if let Resource::PioAddressRange { base, size } = entry {
230 vec.push((*base, *size));
231 }
232 }
233 vec
234 }
235
236 pub fn get_mmio_address_ranges(&self) -> Vec<(u64, u64)> {
238 let mut vec = Vec::new();
239 for entry in self.0.iter().as_ref() {
240 if let Resource::MmioAddressRange { base, size } = entry {
241 vec.push((*base, *size));
242 }
243 }
244 vec
245 }
246
247 pub fn get_mem_address_ranges(&self) -> Vec<(u64, u64)> {
249 let mut vec = Vec::new();
250 for entry in self.0.iter().as_ref() {
251 if let Resource::MemAddressRange { base, size } = entry {
252 vec.push((*base, *size));
253 }
254 }
255 vec
256 }
257
258 pub fn get_legacy_irq(&self) -> Option<u32> {
260 for entry in self.0.iter().as_ref() {
261 if let Resource::LegacyIrq(base) = entry {
262 return Some(*base);
263 }
264 }
265 None
266 }
267
268 pub fn get_pci_msi_irqs(&self) -> Option<(u32, u32)> {
270 self.get_msi_irqs(MsiIrqType::PciMsi)
271 }
272
273 pub fn get_pci_msix_irqs(&self) -> Option<(u32, u32)> {
275 self.get_msi_irqs(MsiIrqType::PciMsix)
276 }
277
278 pub fn get_generic_msi_irqs(&self) -> Option<(u32, u32)> {
280 self.get_msi_irqs(MsiIrqType::GenericMsi)
281 }
282
283 fn get_msi_irqs(&self, ty: MsiIrqType) -> Option<(u32, u32)> {
284 for entry in self.0.iter().as_ref() {
285 if let Resource::MsiIrq {
286 ty: msi_type,
287 base,
288 size,
289 } = entry
290 {
291 if ty == *msi_type {
292 return Some((*base, *size));
293 }
294 }
295 }
296 None
297 }
298
299 pub fn get_kvm_mem_slots(&self) -> Vec<u32> {
301 let mut vec = Vec::new();
302 for entry in self.0.iter().as_ref() {
303 if let Resource::KvmMemSlot(index) = entry {
304 vec.push(*index);
305 }
306 }
307 vec
308 }
309
310 pub fn get_mac_address(&self) -> Option<String> {
312 for entry in self.0.iter().as_ref() {
313 if let Resource::MacAddresss(addr) = entry {
314 return Some(addr.clone());
315 }
316 }
317 None
318 }
319
320 pub fn get_all_resources(&self) -> &[Resource] {
322 &self.0
323 }
324}
325
326impl Deref for DeviceResources {
327 type Target = [Resource];
328
329 fn deref(&self) -> &Self::Target {
330 &self.0
331 }
332}
333
334#[cfg(test)]
335pub(crate) mod tests {
336 use super::*;
337
338 const PIO_ADDRESS_SIZE: u16 = 5;
339 const PIO_ADDRESS_BASE: u16 = 0;
340 const MMIO_ADDRESS_SIZE: u64 = 0x8765_4321;
341 const MMIO_ADDRESS_BASE: u64 = 0x1234_5678;
342 const MEM_ADDRESS_SIZE: u64 = 0x8765_4321;
343 const MEM_ADDRESS_BASE: u64 = 0x1234_5678;
344 const LEGACY_IRQ: u32 = 0x168;
345 const PCI_MSI_IRQ_SIZE: u32 = 0x8888;
346 const PCI_MSI_IRQ_BASE: u32 = 0x6666;
347 const PCI_MSIX_IRQ_SIZE: u32 = 0x16666;
348 const PCI_MSIX_IRQ_BASE: u32 = 0x8888;
349 const GENERIC_MSI_IRQS_SIZE: u32 = 0x16888;
350 const GENERIC_MSI_IRQS_BASE: u32 = 0x16688;
351 const MAC_ADDRESS: &str = "00:08:63:66:86:88";
352 const KVM_SLOT_ID: u32 = 0x0100;
353
354 pub fn get_device_resource() -> DeviceResources {
355 let mut resource = DeviceResources::new();
356
357 let entry = Resource::PioAddressRange {
358 base: PIO_ADDRESS_BASE,
359 size: PIO_ADDRESS_SIZE,
360 };
361 resource.append(entry.clone());
362 assert_eq!(entry, resource[0]);
363
364 let entry = Resource::MmioAddressRange {
365 base: MMIO_ADDRESS_BASE,
366 size: MMIO_ADDRESS_SIZE,
367 };
368 resource.append(entry.clone());
369 assert_eq!(entry, resource[1]);
370
371 let entry = Resource::MemAddressRange {
372 base: MEM_ADDRESS_BASE,
373 size: MEM_ADDRESS_SIZE,
374 };
375 resource.append(entry.clone());
376 assert_eq!(entry, resource[2]);
377
378 let entry = Resource::LegacyIrq(LEGACY_IRQ);
379 resource.append(entry.clone());
380 assert_eq!(entry, resource[3]);
381
382 let entry = Resource::MsiIrq {
383 ty: MsiIrqType::PciMsi,
384 base: PCI_MSI_IRQ_BASE,
385 size: PCI_MSI_IRQ_SIZE,
386 };
387 resource.append(entry.clone());
388 assert_eq!(entry, resource[4]);
389
390 let entry = Resource::MsiIrq {
391 ty: MsiIrqType::PciMsix,
392 base: PCI_MSIX_IRQ_BASE,
393 size: PCI_MSIX_IRQ_SIZE,
394 };
395 resource.append(entry.clone());
396 assert_eq!(entry, resource[5]);
397
398 let entry = Resource::MsiIrq {
399 ty: MsiIrqType::GenericMsi,
400 base: GENERIC_MSI_IRQS_BASE,
401 size: GENERIC_MSI_IRQS_SIZE,
402 };
403 resource.append(entry.clone());
404 assert_eq!(entry, resource[6]);
405
406 let entry = Resource::MacAddresss(MAC_ADDRESS.to_string());
407 resource.append(entry.clone());
408 assert_eq!(entry, resource[7]);
409
410 let entry = Resource::KvmMemSlot(KVM_SLOT_ID);
411 resource.append(entry.clone());
412 assert_eq!(entry, resource[8]);
413
414 resource
415 }
416
417 #[test]
418 fn get_pio_address_ranges() {
419 let resources = get_device_resource();
420 assert!(
421 resources.get_pio_address_ranges()[0].0 == PIO_ADDRESS_BASE
422 && resources.get_pio_address_ranges()[0].1 == PIO_ADDRESS_SIZE
423 );
424 assert_eq!(
425 resources[0],
426 Resource::PioAddressRange {
427 base: PIO_ADDRESS_BASE,
428 size: PIO_ADDRESS_SIZE,
429 }
430 );
431 assert_ne!(resources[0], resources[1]);
432
433 let resources2 = resources.clone();
434 assert_eq!(resources.len(), resources2.len());
435 drop(resources);
436 assert_eq!(
437 resources2[0],
438 Resource::PioAddressRange {
439 base: PIO_ADDRESS_BASE,
440 size: PIO_ADDRESS_SIZE,
441 }
442 );
443 }
444
445 #[test]
446 fn test_get_mmio_address_ranges() {
447 let resources = get_device_resource();
448 assert!(
449 resources.get_mmio_address_ranges()[0].0 == MMIO_ADDRESS_BASE
450 && resources.get_mmio_address_ranges()[0].1 == MMIO_ADDRESS_SIZE
451 );
452 }
453
454 #[test]
455 fn test_get_mem_address_ranges() {
456 let resources = get_device_resource();
457 assert!(
458 resources.get_mem_address_ranges()[0].0 == MEM_ADDRESS_BASE
459 && resources.get_mem_address_ranges()[0].1 == MEM_ADDRESS_SIZE
460 );
461 }
462
463 #[test]
464 fn test_get_legacy_irq() {
465 let resources = get_device_resource();
466 assert!(resources.get_legacy_irq().unwrap() == LEGACY_IRQ);
467
468 let resources = DeviceResources::new();
470 assert!(resources.get_legacy_irq().is_none());
471 }
472
473 #[test]
474 fn test_get_pci_msi_irqs() {
475 let resources = get_device_resource();
476 assert!(
477 resources.get_pci_msi_irqs().unwrap().0 == PCI_MSI_IRQ_BASE
478 && resources.get_pci_msi_irqs().unwrap().1 == PCI_MSI_IRQ_SIZE
479 );
480
481 let resources = DeviceResources::new();
483 assert!(resources.get_generic_msi_irqs().is_none());
484 }
485
486 #[test]
487 fn test_get_pci_msix_irqs() {
488 let resources = get_device_resource();
489 assert!(
490 resources.get_pci_msix_irqs().unwrap().0 == PCI_MSIX_IRQ_BASE
491 && resources.get_pci_msix_irqs().unwrap().1 == PCI_MSIX_IRQ_SIZE
492 );
493
494 let resources = DeviceResources::new();
496 assert!(resources.get_generic_msi_irqs().is_none());
497 }
498
499 #[test]
500 fn test_get_generic_msi_irqs() {
501 let resources = get_device_resource();
502 assert!(
503 resources.get_generic_msi_irqs().unwrap().0 == GENERIC_MSI_IRQS_BASE
504 && resources.get_generic_msi_irqs().unwrap().1 == GENERIC_MSI_IRQS_SIZE
505 );
506
507 let resources = DeviceResources::new();
509 assert!(resources.get_generic_msi_irqs().is_none());
510 }
511
512 #[test]
513 fn test_get_mac_address() {
514 let resources = get_device_resource();
515 assert_eq!(resources.get_mac_address().unwrap(), MAC_ADDRESS);
516
517 let resources = DeviceResources::new();
519 assert!(resources.get_mac_address().is_none());
520 }
521
522 #[test]
523 fn test_get_kvm_slot() {
524 let resources = get_device_resource();
525 assert_eq!(resources.get_kvm_mem_slots(), vec![KVM_SLOT_ID]);
526 }
527
528 #[test]
529 fn test_get_all_resources() {
530 let resources = get_device_resource();
531 assert_eq!(resources.get_all_resources().len(), 9);
532 }
533
534 #[test]
535 fn test_resource_constraint() {
536 let pio = ResourceConstraint::new_pio(2);
537 let pio2 = pio;
538 let mmio = ResourceConstraint::new_mmio(0x1000);
539 assert_eq!(pio, pio2);
540 assert_ne!(pio, mmio);
541
542 if let ResourceConstraint::PioAddress { range, align, size } =
543 ResourceConstraint::new_pio(2)
544 {
545 assert_eq!(range, None);
546 assert_eq!(align, 1);
547 assert_eq!(size, 2);
548 } else {
549 panic!("Pio resource constraint is invalid.");
550 }
551
552 if let ResourceConstraint::PioAddress { range, align, size } =
553 ResourceConstraint::pio_with_constraints(2, Some((15, 16)), 2)
554 {
555 assert_eq!(range, Some((15, 16)));
556 assert_eq!(align, 2);
557 assert_eq!(size, 2);
558 } else {
559 panic!("Pio resource constraint is invalid.");
560 }
561
562 if let ResourceConstraint::MmioAddress { range, align, size } =
563 ResourceConstraint::new_mmio(0x2000)
564 {
565 assert_eq!(range, None);
566 assert_eq!(align, 0x1000);
567 assert_eq!(size, 0x2000);
568 } else {
569 panic!("Mmio resource constraint is invalid.");
570 }
571
572 if let ResourceConstraint::MmioAddress { range, align, size } =
573 ResourceConstraint::mmio_with_constraints(0x2000, Some((0x0, 0x2000)), 0x2000)
574 {
575 assert_eq!(range, Some((0x0, 0x2000)));
576 assert_eq!(align, 0x2000);
577 assert_eq!(size, 0x2000);
578 } else {
579 panic!("Mmio resource constraint is invalid.");
580 }
581
582 if let ResourceConstraint::MemAddress { range, align, size } =
583 ResourceConstraint::new_mem(0x2000)
584 {
585 assert_eq!(range, None);
586 assert_eq!(align, 0x1000);
587 assert_eq!(size, 0x2000);
588 } else {
589 panic!("Mem resource constraint is invalid.");
590 }
591
592 if let ResourceConstraint::MemAddress { range, align, size } =
593 ResourceConstraint::mem_with_constraints(0x2000, Some((0x0, 0x2000)), 0x2000)
594 {
595 assert_eq!(range, Some((0x0, 0x2000)));
596 assert_eq!(align, 0x2000);
597 assert_eq!(size, 0x2000);
598 } else {
599 panic!("Mem resource constraint is invalid.");
600 }
601
602 if let ResourceConstraint::LegacyIrq { irq } =
603 ResourceConstraint::new_legacy_irq(Some(0x123))
604 {
605 assert_eq!(irq, Some(0x123));
606 } else {
607 panic!("IRQ resource constraint is invalid.");
608 }
609
610 if let ResourceConstraint::PciMsiIrq { size } = ResourceConstraint::new_pci_msi_irq(0x123) {
611 assert_eq!(size, 0x123);
612 } else {
613 panic!("Pci MSI irq resource constraint is invalid.");
614 }
615
616 if let ResourceConstraint::PciMsixIrq { size } = ResourceConstraint::new_pci_msix_irq(0x123)
617 {
618 assert_eq!(size, 0x123);
619 } else {
620 panic!("Pci MSIx irq resource constraint is invalid.");
621 }
622
623 if let ResourceConstraint::GenericIrq { size } = ResourceConstraint::new_generic_irq(0x123)
624 {
625 assert_eq!(size, 0x123);
626 } else {
627 panic!("generic irq resource constraint is invalid.");
628 }
629
630 if let ResourceConstraint::KvmMemSlot { slot, size } =
631 ResourceConstraint::new_kvm_mem_slot(0x1000, Some(0x2000))
632 {
633 assert_eq!(slot, Some(0x2000));
634 assert_eq!(size, 0x1000);
635 } else {
636 panic!("KVM slot resource constraint is invalid.");
637 }
638 }
639
640 #[test]
641 fn test_resources_deref() {
642 let resources = get_device_resource();
643 let mut count = 0;
644 for _res in resources.iter() {
645 count += 1;
646 }
647 assert_eq!(count, resources.0.len());
648 }
649}