1 // Copyright 2021 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use base::{ioctl_with_ref, AsRawDescriptor, Event, RawDescriptor}; 6 use data_model::vec_with_array_field; 7 use std::fs::{File, OpenOptions}; 8 use std::io; 9 use std::mem::size_of; 10 11 use remain::sorted; 12 use thiserror::Error; 13 use vfio_sys::*; 14 15 use crate::{IrqEdgeEvent, IrqLevelEvent}; 16 17 #[sorted] 18 #[derive(Error, Debug)] 19 pub enum DirectIrqError { 20 #[error("failed to clone trigger event: {0}")] 21 CloneEvent(base::Error), 22 #[error("failed to clone resample event: {0}")] 23 CloneResampleEvent(base::Error), 24 #[error("failed to enable direct irq")] 25 Enable, 26 #[error("failed to enable gpe irq")] 27 EnableGpe, 28 #[error("failed to enable direct sci irq")] 29 EnableSci, 30 #[error("failed to enable wake irq")] 31 EnableWake, 32 #[error("failed to open /dev/plat-irq-forward: {0}")] 33 Open(io::Error), 34 } 35 36 pub struct DirectIrq { 37 dev: File, 38 trigger: Event, 39 resample: Option<Event>, 40 sci_irq_prepared: bool, 41 } 42 43 impl DirectIrq { new(trigger_evt: &Event, resample_evt: Option<&Event>) -> Result<Self, DirectIrqError>44 fn new(trigger_evt: &Event, resample_evt: Option<&Event>) -> Result<Self, DirectIrqError> { 45 let trigger = trigger_evt 46 .try_clone() 47 .map_err(DirectIrqError::CloneEvent)?; 48 let resample = if let Some(event) = resample_evt { 49 Some( 50 event 51 .try_clone() 52 .map_err(DirectIrqError::CloneResampleEvent)?, 53 ) 54 } else { 55 None 56 }; 57 let dev = OpenOptions::new() 58 .read(true) 59 .write(true) 60 .open("/dev/plat-irq-forward") 61 .map_err(DirectIrqError::Open)?; 62 Ok(DirectIrq { 63 dev, 64 trigger, 65 resample, 66 sci_irq_prepared: false, 67 }) 68 } 69 70 /// Create DirectIrq object to access hardware edge triggered interrupts. new_edge(irq_evt: &IrqEdgeEvent) -> Result<Self, DirectIrqError>71 pub fn new_edge(irq_evt: &IrqEdgeEvent) -> Result<Self, DirectIrqError> { 72 DirectIrq::new(irq_evt.get_trigger(), None) 73 } 74 75 /// Create DirectIrq object to access hardware level triggered interrupts. new_level(irq_evt: &IrqLevelEvent) -> Result<Self, DirectIrqError>76 pub fn new_level(irq_evt: &IrqLevelEvent) -> Result<Self, DirectIrqError> { 77 DirectIrq::new(irq_evt.get_trigger(), Some(irq_evt.get_resample())) 78 } 79 80 /// Enable hardware triggered interrupt handling. 81 /// 82 /// Note: this feature is not part of VFIO, but provides 83 /// missing IRQ forwarding functionality. 84 /// 85 /// # Arguments 86 /// 87 /// * `irq_num` - host interrupt number (GSI). 88 /// irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError>89 pub fn irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError> { 90 if let Some(resample) = &self.resample { 91 self.plat_irq_ioctl( 92 irq_num, 93 PLAT_IRQ_FORWARD_SET_LEVEL_TRIGGER_EVENTFD, 94 self.trigger.as_raw_descriptor(), 95 )?; 96 self.plat_irq_ioctl( 97 irq_num, 98 PLAT_IRQ_FORWARD_SET_LEVEL_UNMASK_EVENTFD, 99 resample.as_raw_descriptor(), 100 )?; 101 } else { 102 self.plat_irq_ioctl( 103 irq_num, 104 PLAT_IRQ_FORWARD_SET_EDGE_TRIGGER, 105 self.trigger.as_raw_descriptor(), 106 )?; 107 }; 108 109 Ok(()) 110 } 111 irq_wake_enable(&self, irq_num: u32) -> Result<(), DirectIrqError>112 pub fn irq_wake_enable(&self, irq_num: u32) -> Result<(), DirectIrqError> { 113 self.plat_irq_wake_ioctl(irq_num, PLAT_IRQ_WAKE_ENABLE) 114 } 115 116 /// Enable hardware triggered SCI interrupt handling for GPE. 117 /// 118 /// Note: sci_irq_prepare() itself does not enable SCI forwarding yet 119 /// but configures it so it can be enabled for selected GPEs using gpe_enable_forwarding(). sci_irq_prepare(&mut self) -> Result<(), DirectIrqError>120 pub fn sci_irq_prepare(&mut self) -> Result<(), DirectIrqError> { 121 if let Some(resample) = &self.resample { 122 self.plat_irq_ioctl( 123 0, 124 PLAT_IRQ_FORWARD_SET_LEVEL_SCI_FOR_GPE_TRIGGER_EVENTFD, 125 self.trigger.as_raw_descriptor(), 126 )?; 127 self.plat_irq_ioctl( 128 0, 129 PLAT_IRQ_FORWARD_SET_LEVEL_SCI_FOR_GPE_UNMASK_EVENTFD, 130 resample.as_raw_descriptor(), 131 )?; 132 } else { 133 return Err(DirectIrqError::EnableSci); 134 } 135 136 self.sci_irq_prepared = true; 137 138 Ok(()) 139 } 140 plat_irq_ioctl( &self, irq_num: u32, action: u32, fd: RawDescriptor, ) -> Result<(), DirectIrqError>141 fn plat_irq_ioctl( 142 &self, 143 irq_num: u32, 144 action: u32, 145 fd: RawDescriptor, 146 ) -> Result<(), DirectIrqError> { 147 let count = 1; 148 let u32_size = size_of::<u32>(); 149 let mut irq_set = vec_with_array_field::<plat_irq_forward_set, u32>(count); 150 irq_set[0].argsz = (size_of::<plat_irq_forward_set>() + count * u32_size) as u32; 151 irq_set[0].action_flags = action; 152 irq_set[0].count = count as u32; 153 irq_set[0].irq_number_host = irq_num; 154 // Safe as we are the owner of irq_set and allocation provides enough space for 155 // eventfd array. 156 let data = unsafe { irq_set[0].eventfd.as_mut_slice(count * u32_size) }; 157 let (left, _right) = data.split_at_mut(u32_size); 158 left.copy_from_slice(&fd.to_ne_bytes()[..]); 159 160 // Safe as we are the owner of plat_irq_forward and irq_set which are valid value 161 let ret = unsafe { ioctl_with_ref(self, PLAT_IRQ_FORWARD_SET(), &irq_set[0]) }; 162 if ret < 0 { 163 Err(DirectIrqError::Enable) 164 } else { 165 Ok(()) 166 } 167 } 168 plat_irq_wake_ioctl(&self, irq_num: u32, action: u32) -> Result<(), DirectIrqError>169 fn plat_irq_wake_ioctl(&self, irq_num: u32, action: u32) -> Result<(), DirectIrqError> { 170 let mut irq_wake_set = vec_with_array_field::<plat_irq_wake_set, u32>(0); 171 irq_wake_set[0].argsz = (size_of::<plat_irq_wake_set>()) as u32; 172 irq_wake_set[0].action_flags = action; 173 irq_wake_set[0].irq_number_host = irq_num; 174 175 // Safe as we are the owner of plat_irq_wake_set and irq_wake_set which are valid value 176 let ret = unsafe { ioctl_with_ref(self, PLAT_IRQ_WAKE_SET(), &irq_wake_set[0]) }; 177 if ret < 0 { 178 Err(DirectIrqError::EnableWake) 179 } else { 180 Ok(()) 181 } 182 } 183 184 /// Enable hardware triggered GPE handling via SCI interrupt forwarding. 185 /// Note: requires sci_irq_prepare() to be called beforehand. 186 /// 187 /// # Arguments 188 /// 189 /// * `gpe_num` - host GPE number. 190 /// gpe_enable_forwarding(&mut self, gpe_num: u32) -> Result<(), DirectIrqError>191 pub fn gpe_enable_forwarding(&mut self, gpe_num: u32) -> Result<(), DirectIrqError> { 192 if self.resample.is_none() || !self.sci_irq_prepared { 193 return Err(DirectIrqError::EnableGpe); 194 } 195 196 self.gpe_forward_ioctl(gpe_num, ACPI_GPE_FORWARD_SET_TRIGGER)?; 197 198 Ok(()) 199 } 200 gpe_forward_ioctl(&self, gpe_num: u32, action: u32) -> Result<(), DirectIrqError>201 fn gpe_forward_ioctl(&self, gpe_num: u32, action: u32) -> Result<(), DirectIrqError> { 202 let mut gpe_set = vec_with_array_field::<gpe_forward_set, u32>(0); 203 gpe_set[0].argsz = (size_of::<gpe_forward_set>()) as u32; 204 gpe_set[0].action_flags = action; 205 gpe_set[0].gpe_host_nr = gpe_num; 206 207 // Safe as we are the owner of plat_irq_forward and gpe_set which are valid value 208 let ret = unsafe { ioctl_with_ref(self, ACPI_GPE_FORWARD_SET(), &gpe_set[0]) }; 209 if ret < 0 { 210 Err(DirectIrqError::EnableGpe) 211 } else { 212 Ok(()) 213 } 214 } 215 } 216 217 impl AsRawDescriptor for DirectIrq { as_raw_descriptor(&self) -> i32218 fn as_raw_descriptor(&self) -> i32 { 219 self.dev.as_raw_descriptor() 220 } 221 } 222