xref: /cloud-hypervisor/hypervisor/src/kvm/aarch64/gic/redist_regs.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 // Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
2 // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 use kvm_ioctls::DeviceFd;
6 
7 use crate::arch::aarch64::gic::{Error, Result};
8 use crate::device::HypervisorDeviceError;
9 use crate::kvm::kvm_bindings::{
10     kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
11     KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
12     KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
13 };
14 use crate::kvm::{Register, VcpuKvmState};
15 use crate::CpuState;
16 
17 // Relevant redistributor registers that we want to save/restore.
18 const GICR_CTLR: u32 = 0x0000;
19 const GICR_STATUSR: u32 = 0x0010;
20 const GICR_WAKER: u32 = 0x0014;
21 const GICR_PROPBASER: u32 = 0x0070;
22 const GICR_PENDBASER: u32 = 0x0078;
23 
24 /* SGI and PPI Redistributor registers, offsets from RD_base */
25 /*
26  * Redistributor frame offsets from RD_base which is actually SZ_
27  */
28 const GICR_SGI_OFFSET: u32 = 0x0001_0000;
29 const GICR_IGROUPR0: u32 = GICR_SGI_OFFSET + 0x0080;
30 const GICR_ICENABLER0: u32 = GICR_SGI_OFFSET + 0x0180;
31 const GICR_ISENABLER0: u32 = GICR_SGI_OFFSET + 0x0100;
32 const GICR_ISPENDR0: u32 = GICR_SGI_OFFSET + 0x0200;
33 const GICR_ICPENDR0: u32 = GICR_SGI_OFFSET + 0x0280;
34 const GICR_ISACTIVER0: u32 = GICR_SGI_OFFSET + 0x0300;
35 const GICR_ICACTIVER0: u32 = GICR_SGI_OFFSET + 0x0380;
36 const GICR_IPRIORITYR0: u32 = GICR_SGI_OFFSET + 0x0400;
37 const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00;
38 
39 const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32;
40 const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64;
41 
42 const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64
43     | KVM_REG_SIZE_U64
44     | KVM_REG_ARM64_SYSREG as u64
45     | (((3_u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
46     | (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
47 
48 /// This is how we represent the registers of a distributor.
49 /// It is relevant their offset from the base address of the
50 /// distributor.
51 /// Each register has a different number
52 /// of bits_per_irq and is therefore variable length.
53 /// First 32 interrupts (0-32) are private to each CPU (SGIs and PPIs).
54 /// and so we save the first irq to identify between the type of the interrupt
55 /// that the respective register deals with.
56 struct RdistReg {
57     /// Offset from distributor address.
58     base: u32,
59     /// Length of the register.
60     length: u8,
61 }
62 
63 // All or at least the registers we are interested in are 32 bit, so
64 // we use a constant for size(u32).
65 const REG_SIZE: u8 = 4;
66 
67 // Creates a vgic redistributor register.
68 macro_rules! VGIC_RDIST_REG {
69     ($base:expr, $len:expr) => {
70         RdistReg {
71             base: $base,
72             length: $len,
73         }
74     };
75 }
76 
77 // List with relevant distributor registers that we will be restoring.
78 static VGIC_RDIST_REGS: &[RdistReg] = &[
79     VGIC_RDIST_REG!(GICR_STATUSR, 4),
80     VGIC_RDIST_REG!(GICR_WAKER, 4),
81     VGIC_RDIST_REG!(GICR_PROPBASER, 8),
82     VGIC_RDIST_REG!(GICR_PENDBASER, 8),
83     VGIC_RDIST_REG!(GICR_CTLR, 4),
84 ];
85 
86 // List with relevant distributor registers that we will be restoring.
87 static VGIC_SGI_REGS: &[RdistReg] = &[
88     VGIC_RDIST_REG!(GICR_IGROUPR0, 4),
89     VGIC_RDIST_REG!(GICR_ICENABLER0, 4),
90     VGIC_RDIST_REG!(GICR_ISENABLER0, 4),
91     VGIC_RDIST_REG!(GICR_ICFGR0, 8),
92     VGIC_RDIST_REG!(GICR_ICPENDR0, 4),
93     VGIC_RDIST_REG!(GICR_ISPENDR0, 4),
94     VGIC_RDIST_REG!(GICR_ICACTIVER0, 4),
95     VGIC_RDIST_REG!(GICR_ISACTIVER0, 4),
96     VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
97 ];
98 
99 fn redist_attr_set(gic: &DeviceFd, offset: u32, typer: u64, val: u32) -> Result<()> {
100     let gic_redist_attr = kvm_device_attr {
101         group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
102         attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
103         addr: &val as *const u32 as u64,
104         flags: 0,
105     };
106 
107     gic.set_device_attr(&gic_redist_attr).map_err(|e| {
108         Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
109     })?;
110 
111     Ok(())
112 }
113 
114 fn redist_attr_get(gic: &DeviceFd, offset: u32, typer: u64) -> Result<u32> {
115     let mut val = 0;
116 
117     let mut gic_redist_attr = kvm_device_attr {
118         group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
119         attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
120         addr: &mut val as *mut u32 as u64,
121         flags: 0,
122     };
123 
124     // SAFETY: gic_redist_attr.addr is safe to write to.
125     unsafe { gic.get_device_attr(&mut gic_redist_attr) }.map_err(|e| {
126         Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
127     })?;
128 
129     Ok(val)
130 }
131 
132 fn access_redists_aux(
133     gic: &DeviceFd,
134     gicr_typer: &[u64],
135     state: &mut Vec<u32>,
136     reg_list: &[RdistReg],
137     idx: &mut usize,
138     set: bool,
139 ) -> Result<()> {
140     for i in gicr_typer {
141         for rdreg in reg_list {
142             let mut base = rdreg.base;
143             let end = base + rdreg.length as u32;
144 
145             while base < end {
146                 if set {
147                     redist_attr_set(gic, base, *i, state[*idx])?;
148                     *idx += 1;
149                 } else {
150                     state.push(redist_attr_get(gic, base, *i)?);
151                 }
152                 base += REG_SIZE as u32;
153             }
154         }
155     }
156     Ok(())
157 }
158 
159 /// Get redistributor registers.
160 pub fn get_redist_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
161     let mut state = Vec::new();
162     let mut idx: usize = 0;
163     access_redists_aux(
164         gic,
165         gicr_typer,
166         &mut state,
167         VGIC_RDIST_REGS,
168         &mut idx,
169         false,
170     )?;
171 
172     access_redists_aux(gic, gicr_typer, &mut state, VGIC_SGI_REGS, &mut idx, false)?;
173     Ok(state)
174 }
175 
176 /// Set redistributor registers.
177 pub fn set_redist_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result<()> {
178     let mut idx: usize = 0;
179     let mut mut_state = state.to_owned();
180     access_redists_aux(
181         gic,
182         gicr_typer,
183         &mut mut_state,
184         VGIC_RDIST_REGS,
185         &mut idx,
186         true,
187     )?;
188     access_redists_aux(
189         gic,
190         gicr_typer,
191         &mut mut_state,
192         VGIC_SGI_REGS,
193         &mut idx,
194         true,
195     )
196 }
197 
198 pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
199     /* Pre-construct the GICR_TYPER:
200      * For our implementation:
201      *  Top 32 bits are the affinity value of the associated CPU
202      *  CommonLPIAff == 01 (redistributors with same Aff3 share LPI table)
203      *  Processor_Number == CPU index starting from 0
204      *  DPGS == 0 (GICR_CTLR.DPG* not supported)
205      *  Last == 1 if this is the last redistributor in a series of
206      *            contiguous redistributor pages
207      *  DirectLPI == 0 (direct injection of LPIs not supported)
208      *  VLPIS == 0 (virtual LPIs not supported)
209      *  PLPIS == 0 (physical LPIs not supported)
210      */
211     let mut gicr_typers: Vec<u64> = Vec::new();
212     for (index, state) in vcpu_states.iter().enumerate() {
213         let state: VcpuKvmState = state.clone().into();
214         let last = (index == vcpu_states.len() - 1) as u64;
215         // state.sys_regs is a big collection of system registers, including MIPDR_EL1
216         let mpidr: Vec<Register> = state
217             .sys_regs
218             .into_iter()
219             .filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
220             .collect();
221         //calculate affinity
222         let mut cpu_affid = mpidr[0].addr & 1095233437695;
223         cpu_affid = ((cpu_affid & 0xFF00000000) >> 8) | (cpu_affid & 0xFFFFFF);
224         gicr_typers.push((cpu_affid << 32) | (1 << 24) | (index as u64) << 8 | (last << 4));
225     }
226 
227     gicr_typers
228 }
229