• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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