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