xref: /cloud-hypervisor/hypervisor/src/kvm/aarch64/gic/dist_regs.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use kvm_ioctls::DeviceFd;
5 
6 use crate::arch::aarch64::gic::{Error, Result};
7 use crate::device::HypervisorDeviceError;
8 use crate::kvm::kvm_bindings::{
9     kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
10 };
11 
12 /*
13  Distributor registers as detailed at page 456 from
14  https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_architecture_specification.pdf.
15  Address offsets are relative to the Distributor base address defined
16 by the system memory map. Unless otherwise stated in the register description,
17 all GIC registers are 32-bits wide.
18  */
19 const GICD_CTLR: u32 = 0x0;
20 const GICD_STATUSR: u32 = 0x0010;
21 const GICD_IGROUPR: u32 = 0x0080;
22 const GICD_ISENABLER: u32 = 0x0100;
23 const GICD_ICENABLER: u32 = 0x0180;
24 const GICD_ISPENDR: u32 = 0x0200;
25 const GICD_ICPENDR: u32 = 0x0280;
26 const GICD_ISACTIVER: u32 = 0x0300;
27 const GICD_ICACTIVER: u32 = 0x0380;
28 const GICD_IPRIORITYR: u32 = 0x0400;
29 const GICD_ICFGR: u32 = 0x0C00;
30 const GICD_IROUTER: u32 = 0x6000;
31 
32 /// This is how we represent the registers of the vgic's distributor.
33 /// Some of the distributor register )(i.e GICD_STATUSR) are simple
34 /// registers (i.e they are associated to a 32 bit value).
35 /// However, there are other registers that have variable lengths since
36 /// they dedicate some of the 32 bits to some specific interrupt. So, their length
37 /// depends on the number of interrupts (i.e the ones that are represented as GICD_REG<n>)
38 /// in the documentation mentioned above.
39 struct DistReg {
40     /// Offset from distributor address.
41     base: u32,
42     /// Bits per interrupt.
43     /// Relevant for registers that DO share IRQs.
44     bpi: u8,
45     /// Length of the register.
46     /// Relevant for registers that DO NOT share IRQs.
47     length: u16,
48 }
49 
50 // All or at least the registers we are interested in are 32 bit, so
51 // we use a constant for size(u32).
52 const REG_SIZE: u8 = 4;
53 
54 // Creates a vgic distributor register.
55 macro_rules! VGIC_DIST_REG {
56     ($base:expr, $bpi:expr, $length:expr) => {
57         DistReg {
58             base: $base,
59             bpi: $bpi,
60             length: $length,
61         }
62     };
63 }
64 
65 // List with relevant distributor registers that we will be restoring.
66 // Order is taken from qemu.
67 static VGIC_DIST_REGS: &[DistReg] = &[
68     VGIC_DIST_REG!(GICD_STATUSR, 0, 4),
69     VGIC_DIST_REG!(GICD_ICENABLER, 1, 0),
70     VGIC_DIST_REG!(GICD_ISENABLER, 1, 0),
71     VGIC_DIST_REG!(GICD_IGROUPR, 1, 0),
72     VGIC_DIST_REG!(GICD_IROUTER, 64, 0),
73     VGIC_DIST_REG!(GICD_ICFGR, 2, 0),
74     VGIC_DIST_REG!(GICD_ICPENDR, 1, 0),
75     VGIC_DIST_REG!(GICD_ISPENDR, 1, 0),
76     VGIC_DIST_REG!(GICD_ICACTIVER, 1, 0),
77     VGIC_DIST_REG!(GICD_ISACTIVER, 1, 0),
78     VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
79 ];
80 
81 fn dist_attr_set(gic: &DeviceFd, offset: u32, val: u32) -> Result<()> {
82     let gic_dist_attr = kvm_device_attr {
83         group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
84         attr: offset as u64,
85         addr: &val as *const u32 as u64,
86         flags: 0,
87     };
88 
89     gic.set_device_attr(&gic_dist_attr).map_err(|e| {
90         Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
91     })?;
92 
93     Ok(())
94 }
95 
96 fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
97     let mut val = 0;
98 
99     let mut gic_dist_attr = kvm_device_attr {
100         group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
101         attr: offset as u64,
102         addr: &mut val as *mut u32 as u64,
103         flags: 0,
104     };
105 
106     // SAFETY: gic_dist_attr.addr is safe to write to.
107     unsafe { gic.get_device_attr(&mut gic_dist_attr) }.map_err(|e| {
108         Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
109     })?;
110 
111     Ok(val)
112 }
113 
114 /// Get the distributor control register.
115 pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
116     dist_attr_get(gic, GICD_CTLR)
117 }
118 
119 /// Set the distributor control register.
120 pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
121     dist_attr_set(gic, GICD_CTLR, val)
122 }
123 
124 fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
125     let mut num_irq = 0;
126 
127     let mut nr_irqs_attr = kvm_device_attr {
128         group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
129         attr: 0,
130         addr: &mut num_irq as *mut u32 as u64,
131         flags: 0,
132     };
133     // SAFETY: nr_irqs_attr.addr is safe to write to.
134     unsafe { gic.get_device_attr(&mut nr_irqs_attr) }.map_err(|e| {
135         Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
136     })?;
137     Ok(num_irq)
138 }
139 
140 fn compute_reg_len(gic: &DeviceFd, reg: &DistReg, base: u32) -> Result<u32> {
141     // FIXME:
142     // Redefine some GIC constants to avoid the dependency on `layout` crate.
143     // This is temporary solution, will be fixed in future refactoring.
144     const LAYOUT_IRQ_BASE: u32 = 32;
145 
146     let mut end = base;
147     let num_irq = get_interrupts_num(gic)?;
148     if reg.length > 0 {
149         // This is the single type register (i.e one that is not DIST_X<n>) and for which
150         // the bpi is 0.
151         // Look in the kernel for REGISTER_DESC_WITH_LENGTH.
152         end = base + reg.length as u32;
153     }
154     if reg.bpi > 0 {
155         // This is the type of register that takes into account the number of interrupts
156         // that the model has. It is also the type of register where
157         // a register relates to multiple interrupts.
158         end = base + (reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) / 8);
159         if reg.bpi as u32 * (num_irq - LAYOUT_IRQ_BASE) % 8 > 0 {
160             end += REG_SIZE as u32;
161         }
162     }
163     Ok(end)
164 }
165 
166 /// Set distributor registers of the GIC.
167 pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
168     let mut idx = 0;
169 
170     for dreg in VGIC_DIST_REGS {
171         let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
172         let end = compute_reg_len(gic, dreg, base)?;
173 
174         while base < end {
175             dist_attr_set(gic, base, state[idx])?;
176             idx += 1;
177             base += REG_SIZE as u32;
178         }
179     }
180     Ok(())
181 }
182 /// Get distributor registers of the GIC.
183 pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
184     let mut state = Vec::new();
185 
186     for dreg in VGIC_DIST_REGS {
187         let mut base = dreg.base + REG_SIZE as u32 * dreg.bpi as u32;
188         let end = compute_reg_len(gic, dreg, base)?;
189 
190         while base < end {
191             state.push(dist_attr_get(gic, base)?);
192             base += REG_SIZE as u32;
193         }
194     }
195     Ok(state)
196 }
197