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