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