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::fmt; 8 use std::fs::{File, OpenOptions}; 9 use std::io; 10 use std::mem::size_of; 11 12 use vfio_sys::*; 13 14 #[derive(Debug)] 15 pub enum DirectIrqError { 16 Open(io::Error), 17 Enable, 18 } 19 20 impl fmt::Display for DirectIrqError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result21 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 match self { 23 DirectIrqError::Open(e) => write!(f, "failed to open /dev/plat-irq-forward: {}", e), 24 DirectIrqError::Enable => write!(f, "failed to enable direct irq"), 25 } 26 } 27 } 28 29 pub struct DirectIrq { 30 dev: File, 31 trigger: Event, 32 resample: Option<Event>, 33 } 34 35 impl DirectIrq { 36 /// Create DirectIrq object to access hardware triggered interrupts. new(trigger: Event, resample: Option<Event>) -> Result<Self, DirectIrqError>37 pub fn new(trigger: Event, resample: Option<Event>) -> Result<Self, DirectIrqError> { 38 let dev = OpenOptions::new() 39 .read(true) 40 .write(true) 41 .open("/dev/plat-irq-forward") 42 .map_err(DirectIrqError::Open)?; 43 Ok(DirectIrq { 44 dev, 45 trigger, 46 resample, 47 }) 48 } 49 50 /// Enable hardware triggered interrupt handling. 51 /// 52 /// Note: this feature is not part of VFIO, but provides 53 /// missing IRQ forwarding functionality. 54 /// 55 /// # Arguments 56 /// 57 /// * `irq_num` - host interrupt number (GSI). 58 /// irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError>59 pub fn irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError> { 60 if let Some(resample) = &self.resample { 61 self.plat_irq_ioctl( 62 irq_num, 63 PLAT_IRQ_FORWARD_SET_LEVEL_TRIGGER_EVENTFD, 64 self.trigger.as_raw_descriptor(), 65 )?; 66 self.plat_irq_ioctl( 67 irq_num, 68 PLAT_IRQ_FORWARD_SET_LEVEL_UNMASK_EVENTFD, 69 resample.as_raw_descriptor(), 70 )?; 71 } else { 72 self.plat_irq_ioctl( 73 irq_num, 74 PLAT_IRQ_FORWARD_SET_EDGE_TRIGGER, 75 self.trigger.as_raw_descriptor(), 76 )?; 77 }; 78 79 Ok(()) 80 } 81 plat_irq_ioctl( &self, irq_num: u32, action: u32, fd: RawDescriptor, ) -> Result<(), DirectIrqError>82 fn plat_irq_ioctl( 83 &self, 84 irq_num: u32, 85 action: u32, 86 fd: RawDescriptor, 87 ) -> Result<(), DirectIrqError> { 88 let count = 1; 89 let u32_size = size_of::<u32>(); 90 let mut irq_set = vec_with_array_field::<plat_irq_forward_set, u32>(count); 91 irq_set[0].argsz = (size_of::<plat_irq_forward_set>() + count * u32_size) as u32; 92 irq_set[0].action_flags = action; 93 irq_set[0].count = count as u32; 94 irq_set[0].irq_number_host = irq_num; 95 // Safe as we are the owner of irq_set and allocation provides enough space for 96 // eventfd array. 97 let data = unsafe { irq_set[0].eventfd.as_mut_slice(count * u32_size) }; 98 let (left, _right) = data.split_at_mut(u32_size); 99 left.copy_from_slice(&fd.to_ne_bytes()[..]); 100 101 // Safe as we are the owner of plat_irq_forward and irq_set which are valid value 102 let ret = unsafe { ioctl_with_ref(self, PLAT_IRQ_FORWARD_SET(), &irq_set[0]) }; 103 if ret < 0 { 104 Err(DirectIrqError::Enable) 105 } else { 106 Ok(()) 107 } 108 } 109 } 110 111 impl AsRawDescriptor for DirectIrq { as_raw_descriptor(&self) -> i32112 fn as_raw_descriptor(&self) -> i32 { 113 self.dev.as_raw_descriptor() 114 } 115 } 116