dbs_device/
resources.rs

1// Copyright (C) 2019 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Descriptors representing device resource allocation requirements and assigned resources.
5//!
6//! There are several components related to resource management:
7//! - the Dragonball Secure Sandbox (VMM), which is responsible for creating and registering devices
8//!   to the device manager.
9//! - the device manager, which manages all devices of a Dragonball Secure Sandbox instance.
10//! - the devices, which implement virtual device backends for the guest.
11//!
12//! They cooperate with each to provide resources required by each device. The high level flow of
13//! resource management is as below:
14//! 1) the VMM creates a new device object.
15//! 2) the device returns an array of [ResourceConstraint](self::ResourceConstraint),
16//!    describing the required resources and resource allocation constraints.
17//! 3) the VMM allocates required resources from a resource manager,
18//! 4) the VMM passes the allocated resources [DeviceResources](self::DeviceResources),
19//!    which is an array of [Resource](self::Resource), to the device object.
20//! 5) the VMM registers the new device onto corresponding device managers according the allocated
21//!    resources.
22
23use std::ops::Deref;
24
25/// Enumeration describing a device's resource allocation requirements and constraints.
26#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27pub enum ResourceConstraint {
28    /// Constraint for an IO Port address range.
29    PioAddress {
30        /// Allocating resource within the range [`min`, `max`] if specified.
31        range: Option<(u16, u16)>,
32        /// Alignment for the allocated address.
33        align: u16,
34        /// Size for the allocated address range.
35        size: u16,
36    },
37    /// Constraint for a Memory Mapped IO address range.
38    MmioAddress {
39        /// Allocating resource within the range [`min`, `max`] if specified.
40        range: Option<(u64, u64)>,
41        /// Alignment for the allocated address.
42        align: u64,
43        /// Size for the allocated address range.
44        size: u64,
45    },
46    /// Constraint for a Guest Mem address range.
47    MemAddress {
48        /// Allocating resource within the range [`min`, `max`] if specified.
49        range: Option<(u64, u64)>,
50        /// Alignment for the allocated address.
51        align: u64,
52        /// Size for the allocated address range.
53        size: u64,
54    },
55    /// Constraint for a legacy IRQ.
56    LegacyIrq {
57        /// Reserving the pre-allocated IRQ if it's specified.
58        irq: Option<u32>,
59    },
60    /// Constraint for PCI MSI IRQs.
61    PciMsiIrq {
62        /// Number of Irqs to allocate.
63        size: u32,
64    },
65    /// Constraint for PCI MSIx IRQs.
66    PciMsixIrq {
67        /// Number of Irqs to allocate.
68        size: u32,
69    },
70    /// Constraint for generic IRQs.
71    GenericIrq {
72        /// Number of Irqs to allocate.
73        size: u32,
74    },
75    /// Constraint for KVM mem_slot indexes to map memory into the guest.
76    KvmMemSlot {
77        /// Allocating kvm memory slots starting from the index `slot` if
78        /// specified.
79        slot: Option<u32>,
80        /// Number of slots to allocate.
81        size: u32,
82    },
83}
84
85impl ResourceConstraint {
86    /// Create a new PIO address constraint object with default configuration.
87    pub fn new_pio(size: u16) -> Self {
88        ResourceConstraint::PioAddress {
89            range: None,
90            align: 0x1,
91            size,
92        }
93    }
94
95    /// Create a new PIO address constraint object.
96    pub fn pio_with_constraints(size: u16, range: Option<(u16, u16)>, align: u16) -> Self {
97        ResourceConstraint::PioAddress { range, align, size }
98    }
99
100    /// Create a new MMIO address constraint object with default configuration.
101    pub fn new_mmio(size: u64) -> Self {
102        ResourceConstraint::MmioAddress {
103            range: None,
104            align: 0x1000,
105            size,
106        }
107    }
108
109    /// Create a new MMIO address constraint object.
110    pub fn mmio_with_constraints(size: u64, range: Option<(u64, u64)>, align: u64) -> Self {
111        ResourceConstraint::MmioAddress { range, align, size }
112    }
113
114    /// Create a new Mem address constraint object with default configuration.
115    pub fn new_mem(size: u64) -> Self {
116        ResourceConstraint::MemAddress {
117            range: None,
118            align: 0x1000,
119            size,
120        }
121    }
122
123    /// Create a new Mem address constraint object.
124    pub fn mem_with_constraints(size: u64, range: Option<(u64, u64)>, align: u64) -> Self {
125        ResourceConstraint::MemAddress { range, align, size }
126    }
127
128    /// Create a new legacy IRQ constraint object.
129    ///
130    /// Allocating the pre-allocated legacy Irq `irq` if specified.
131    pub fn new_legacy_irq(irq: Option<u32>) -> Self {
132        ResourceConstraint::LegacyIrq { irq }
133    }
134
135    /// Create a new PCI MSI IRQ constraint object.
136    pub fn new_pci_msi_irq(size: u32) -> Self {
137        ResourceConstraint::PciMsiIrq { size }
138    }
139
140    /// Create a new PCI MSIX IRQ constraint object.
141    pub fn new_pci_msix_irq(size: u32) -> Self {
142        ResourceConstraint::PciMsixIrq { size }
143    }
144
145    /// Create a new Generic IRQ constraint object.
146    pub fn new_generic_irq(size: u32) -> Self {
147        ResourceConstraint::GenericIrq { size }
148    }
149
150    /// Create a new KVM memory slot constraint object.
151    ///
152    /// Allocating kvm memory slots starting from the index `slot` if specified.
153    pub fn new_kvm_mem_slot(size: u32, slot: Option<u32>) -> Self {
154        ResourceConstraint::KvmMemSlot { slot, size }
155    }
156}
157
158/// Type of Message Singaled Interrupt
159#[derive(Copy, Clone, Debug, Eq, PartialEq)]
160pub enum MsiIrqType {
161    /// PCI MSI IRQ numbers.
162    PciMsi,
163    /// PCI MSIx IRQ numbers.
164    PciMsix,
165    /// Generic MSI IRQ numbers.
166    GenericMsi,
167}
168
169/// Enumeration for device resources.
170#[derive(Clone, Debug, Eq, PartialEq)]
171pub enum Resource {
172    /// IO Port resource range.
173    PioAddressRange {
174        /// Pio resource base
175        base: u16,
176        /// Pio resource size
177        size: u16,
178    },
179    /// Memory Mapped IO resource range.
180    MmioAddressRange {
181        /// Mmio resource base
182        base: u64,
183        /// Mmio resource size
184        size: u64,
185    },
186    /// Guest Mem resource range.
187    MemAddressRange {
188        /// Mem resource base
189        base: u64,
190        /// Mem resource size
191        size: u64,
192    },
193    /// Legacy IRQ number.
194    LegacyIrq(u32),
195    /// Message Signaled Interrupt
196    MsiIrq {
197        /// Msi irq type
198        ty: MsiIrqType,
199        /// Msi irq base
200        base: u32,
201        /// Msi irq size
202        size: u32,
203    },
204    /// Network Interface Card MAC address.
205    MacAddresss(String),
206    /// KVM memslot index.
207    KvmMemSlot(u32),
208}
209
210/// Newtype to store a set of device resources.
211#[derive(Clone, Debug, Default, Eq, PartialEq)]
212pub struct DeviceResources(Vec<Resource>);
213
214impl DeviceResources {
215    /// Create a container object to store device resources.
216    pub fn new() -> Self {
217        DeviceResources(Vec::new())
218    }
219
220    /// Append a device resource to the container object.
221    pub fn append(&mut self, entry: Resource) {
222        self.0.push(entry);
223    }
224
225    /// Get the IO port address resources.
226    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    /// Get the Memory Mapped IO address resources.
237    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    /// Get the Guest Memory address resources.
248    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    /// Get the first legacy interrupt number(IRQ).
259    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    /// Get information about the first PCI MSI interrupt resource.
269    pub fn get_pci_msi_irqs(&self) -> Option<(u32, u32)> {
270        self.get_msi_irqs(MsiIrqType::PciMsi)
271    }
272
273    /// Get information about the first PCI MSIx interrupt resource.
274    pub fn get_pci_msix_irqs(&self) -> Option<(u32, u32)> {
275        self.get_msi_irqs(MsiIrqType::PciMsix)
276    }
277
278    /// Get information about the first Generic MSI interrupt resource.
279    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    /// Get the KVM memory slots to map memory into the guest.
300    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    /// Get the first resource information for NIC MAC address.
311    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    /// Get immutable reference to all the resources.
321    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        // None case.
469        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        // None case.
482        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        // None case.
495        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        // None case.
508        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        // None case.
518        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}