vm_topology\processor/
aarch64.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! ARM64-specific topology definitions.
5
6use super::ArchTopology;
7use super::InvalidTopology;
8use super::ProcessorTopology;
9use super::TopologyBuilder;
10use super::VpIndex;
11use super::VpInfo;
12use super::VpTopologyInfo;
13use aarch64defs::MpidrEl1;
14
15/// ARM64-specific topology information.
16#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
17#[derive(Debug, Copy, Clone)]
18#[non_exhaustive]
19pub struct Aarch64Topology {
20    platform: Aarch64PlatformConfig,
21}
22
23impl ArchTopology for Aarch64Topology {
24    type ArchVpInfo = Aarch64VpInfo;
25    type BuilderState = Aarch64TopologyBuilderState;
26
27    fn vp_topology(_topology: &ProcessorTopology<Self>, info: &Self::ArchVpInfo) -> VpTopologyInfo {
28        VpTopologyInfo {
29            socket: info.mpidr.aff2().into(),
30            core: info.mpidr.aff1().into(),
31            thread: info.mpidr.aff0().into(),
32        }
33    }
34}
35
36/// Aarch64-specific [`TopologyBuilder`] state.
37pub struct Aarch64TopologyBuilderState {
38    platform: Aarch64PlatformConfig,
39}
40
41/// ARM64 platform interrupt and GIC configuration.
42///
43/// Groups GIC base addresses, MSI frame info, and platform interrupt
44/// assignments (PMU, virtual timer) into a single struct so that the
45/// topology builder takes one value instead of several positional `u32`s.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
48pub struct Aarch64PlatformConfig {
49    /// GIC distributor base address.
50    #[cfg_attr(feature = "inspect", inspect(hex))]
51    pub gic_distributor_base: u64,
52    /// GIC redistributors base address.
53    #[cfg_attr(feature = "inspect", inspect(hex))]
54    pub gic_redistributors_base: u64,
55    /// GIC v2m MSI frame, if MSIs via v2m are supported.
56    pub gic_v2m: Option<GicV2mInfo>,
57    /// Performance Monitor Unit GSIV (GIC INTID). `None` if not available.
58    pub pmu_gsiv: Option<u32>,
59    /// Virtual timer PPI (GIC INTID, e.g. 20 for PPI 4).
60    pub virt_timer_ppi: u32,
61}
62
63/// GIC v2m MSI frame parameters.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
66pub struct GicV2mInfo {
67    /// Physical base address of the v2m MSI frame.
68    #[cfg_attr(feature = "inspect", inspect(hex))]
69    pub frame_base: u64,
70    /// First GIC interrupt ID in the SPI range owned by this frame.
71    pub spi_base: u32,
72    /// Number of SPIs owned by this frame.
73    pub spi_count: u32,
74}
75
76/// ARM64 specific VP info.
77#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
78#[derive(Debug, Copy, Clone)]
79pub struct Aarch64VpInfo {
80    /// The base info.
81    #[cfg_attr(feature = "inspect", inspect(flatten))]
82    pub base: VpInfo,
83    /// The MPIDR_EL1 value of the processor.
84    #[cfg_attr(feature = "inspect", inspect(hex, with = "|&x| u64::from(x)"))]
85    pub mpidr: MpidrEl1,
86    /// GIC Redistributor Address
87    #[cfg_attr(feature = "inspect", inspect(hex))]
88    pub gicr: u64,
89    /// Performance Interrupt GSIV (PMU)
90    #[cfg_attr(feature = "inspect", inspect(hex))]
91    pub pmu_gsiv: Option<u32>,
92}
93
94impl AsRef<VpInfo> for Aarch64VpInfo {
95    fn as_ref(&self) -> &VpInfo {
96        &self.base
97    }
98}
99
100impl TopologyBuilder<Aarch64Topology> {
101    /// Returns a builder for creating an aarch64 processor topology.
102    pub fn new_aarch64(platform: Aarch64PlatformConfig) -> Self {
103        Self {
104            vps_per_socket: 1,
105            smt_enabled: false,
106            arch: Aarch64TopologyBuilderState { platform },
107        }
108    }
109
110    /// Builds a processor topology with `proc_count` processors.
111    pub fn build(
112        &self,
113        proc_count: u32,
114    ) -> Result<ProcessorTopology<Aarch64Topology>, InvalidTopology> {
115        if proc_count >= 256 {
116            return Err(InvalidTopology::TooManyVps {
117                requested: proc_count,
118                max: u8::MAX.into(),
119            });
120        }
121        if !(16..32).contains(&self.arch.platform.virt_timer_ppi) {
122            return Err(InvalidTopology::InvalidPpiIntid(
123                self.arch.platform.virt_timer_ppi,
124            ));
125        }
126        if let Some(gsiv) = self.arch.platform.pmu_gsiv {
127            if !(16..32).contains(&gsiv) {
128                return Err(InvalidTopology::InvalidPpiIntid(gsiv));
129            }
130        }
131        let mpidrs = (0..proc_count).map(|vp_index| {
132            // TODO: construct mpidr appropriately for the specified
133            // topology.
134            let uni_proc = proc_count == 1;
135            let mut aff = (0..4).map(|i| (vp_index >> (8 * i)) as u8);
136            MpidrEl1::new()
137                .with_res1_31(true)
138                .with_u(uni_proc)
139                .with_aff0(aff.next().unwrap())
140                .with_aff1(aff.next().unwrap())
141                .with_aff2(aff.next().unwrap())
142                .with_aff3(aff.next().unwrap())
143        });
144        self.build_with_vp_info(mpidrs.enumerate().map(|(id, mpidr)| Aarch64VpInfo {
145            base: VpInfo {
146                vp_index: VpIndex::new(id as u32),
147                vnode: 0,
148            },
149            mpidr,
150            gicr: self.arch.platform.gic_redistributors_base
151                + id as u64 * aarch64defs::GIC_REDISTRIBUTOR_SIZE,
152            pmu_gsiv: self.arch.platform.pmu_gsiv,
153        }))
154    }
155
156    /// Builds a processor topology with processors with the specified information.
157    pub fn build_with_vp_info(
158        &self,
159        vps: impl IntoIterator<Item = Aarch64VpInfo>,
160    ) -> Result<ProcessorTopology<Aarch64Topology>, InvalidTopology> {
161        let vps = Vec::from_iter(vps);
162        let mut smt_enabled = false;
163        for (i, vp) in vps.iter().enumerate() {
164            if i != vp.base.vp_index.index() as usize {
165                return Err(InvalidTopology::InvalidVpIndices);
166            }
167
168            if vp.mpidr.mt() {
169                smt_enabled = true;
170            }
171        }
172
173        Ok(ProcessorTopology {
174            vps,
175            smt_enabled,
176            vps_per_socket: self.vps_per_socket,
177            arch: Aarch64Topology {
178                platform: self.arch.platform,
179            },
180        })
181    }
182}
183
184impl ProcessorTopology<Aarch64Topology> {
185    /// Returns the GIC distributor base
186    pub fn gic_distributor_base(&self) -> u64 {
187        self.arch.platform.gic_distributor_base
188    }
189
190    /// Returns the GIC redistributors base
191    pub fn gic_redistributors_base(&self) -> u64 {
192        self.arch.platform.gic_redistributors_base
193    }
194
195    /// Returns the PMU GSIV
196    pub fn pmu_gsiv(&self) -> Option<u32> {
197        self.arch.platform.pmu_gsiv
198    }
199
200    /// Returns the GIC v2m MSI frame info, if present.
201    pub fn gic_v2m(&self) -> Option<GicV2mInfo> {
202        self.arch.platform.gic_v2m
203    }
204
205    /// Returns the virtual timer PPI (GIC INTID).
206    pub fn virt_timer_ppi(&self) -> u32 {
207        self.arch.platform.virt_timer_ppi
208    }
209}