• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The ChromiumOS Authors
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 std::fs::File;
6 use std::fs::OpenOptions;
7 use std::io;
8 use std::mem::size_of;
9 
10 use base::ioctl_with_ref;
11 use base::AsRawDescriptor;
12 use base::Event;
13 use base::RawDescriptor;
14 use data_model::vec_with_array_field;
15 use remain::sorted;
16 use thiserror::Error;
17 use vfio_sys::*;
18 
19 use crate::ACPIPMFixedEvent;
20 use crate::IrqEdgeEvent;
21 use crate::IrqLevelEvent;
22 
23 #[sorted]
24 #[derive(Error, Debug)]
25 pub enum DirectIrqError {
26     #[error("failed to clone trigger event: {0}")]
27     CloneEvent(base::Error),
28     #[error("failed to clone resample event: {0}")]
29     CloneResampleEvent(base::Error),
30     #[error("failed to enable direct irq")]
31     Enable,
32     #[error("failed to enable fixed event irq")]
33     EnableFixedEvent,
34     #[error("failed to enable gpe irq")]
35     EnableGpe,
36     #[error("failed to enable direct sci irq")]
37     EnableSci,
38     #[error("failed to open /dev/plat-irq-forward: {0}")]
39     Open(io::Error),
40 }
41 
42 pub struct DirectIrq {
43     dev: File,
44     trigger: Event,
45     resample: Option<Event>,
46     sci_irq_prepared: bool,
47 }
48 
49 impl DirectIrq {
new(trigger_evt: &Event, resample_evt: Option<&Event>) -> Result<Self, DirectIrqError>50     fn new(trigger_evt: &Event, resample_evt: Option<&Event>) -> Result<Self, DirectIrqError> {
51         let trigger = trigger_evt
52             .try_clone()
53             .map_err(DirectIrqError::CloneEvent)?;
54         let resample = if let Some(event) = resample_evt {
55             Some(
56                 event
57                     .try_clone()
58                     .map_err(DirectIrqError::CloneResampleEvent)?,
59             )
60         } else {
61             None
62         };
63         let dev = OpenOptions::new()
64             .read(true)
65             .write(true)
66             .open("/dev/plat-irq-forward")
67             .map_err(DirectIrqError::Open)?;
68         Ok(DirectIrq {
69             dev,
70             trigger,
71             resample,
72             sci_irq_prepared: false,
73         })
74     }
75 
76     /// Create DirectIrq object to access hardware edge triggered interrupts.
new_edge(irq_evt: &IrqEdgeEvent) -> Result<Self, DirectIrqError>77     pub fn new_edge(irq_evt: &IrqEdgeEvent) -> Result<Self, DirectIrqError> {
78         DirectIrq::new(irq_evt.get_trigger(), None)
79     }
80 
81     /// Create DirectIrq object to access hardware level triggered interrupts.
new_level(irq_evt: &IrqLevelEvent) -> Result<Self, DirectIrqError>82     pub fn new_level(irq_evt: &IrqLevelEvent) -> Result<Self, DirectIrqError> {
83         DirectIrq::new(irq_evt.get_trigger(), Some(irq_evt.get_resample()))
84     }
85 
86     /// Enable hardware triggered interrupt handling.
87     ///
88     /// Note: this feature is not part of VFIO, but provides
89     /// missing IRQ forwarding functionality.
90     ///
91     /// # Arguments
92     ///
93     /// * `irq_num` - host interrupt number (GSI).
94     ///
irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError>95     pub fn irq_enable(&self, irq_num: u32) -> Result<(), DirectIrqError> {
96         if let Some(resample) = &self.resample {
97             self.plat_irq_ioctl(
98                 irq_num,
99                 PLAT_IRQ_FORWARD_SET_LEVEL_TRIGGER_EVENTFD,
100                 self.trigger.as_raw_descriptor(),
101             )?;
102             self.plat_irq_ioctl(
103                 irq_num,
104                 PLAT_IRQ_FORWARD_SET_LEVEL_UNMASK_EVENTFD,
105                 resample.as_raw_descriptor(),
106             )?;
107         } else {
108             self.plat_irq_ioctl(
109                 irq_num,
110                 PLAT_IRQ_FORWARD_SET_EDGE_TRIGGER,
111                 self.trigger.as_raw_descriptor(),
112             )?;
113         };
114 
115         Ok(())
116     }
117 
118     /// Enable hardware triggered SCI interrupt handling for GPE or fixed events.
119     ///
120     /// Note: sci_irq_prepare() itself does not enable SCI forwarding yet
121     /// but configures it so it can be enabled for selected GPEs or fixed events
122     /// using gpe_enable_forwarding() or fixed_event_enable_forwarding().
sci_irq_prepare(&mut self) -> Result<(), DirectIrqError>123     pub fn sci_irq_prepare(&mut self) -> Result<(), DirectIrqError> {
124         if let Some(resample) = &self.resample {
125             self.plat_irq_ioctl(
126                 0,
127                 PLAT_IRQ_FORWARD_SET_LEVEL_ACPI_SCI_TRIGGER_EVENTFD,
128                 self.trigger.as_raw_descriptor(),
129             )?;
130             self.plat_irq_ioctl(
131                 0,
132                 PLAT_IRQ_FORWARD_SET_LEVEL_ACPI_SCI_UNMASK_EVENTFD,
133                 resample.as_raw_descriptor(),
134             )?;
135         } else {
136             return Err(DirectIrqError::EnableSci);
137         }
138 
139         self.sci_irq_prepared = true;
140 
141         Ok(())
142     }
143 
plat_irq_ioctl( &self, irq_num: u32, action: u32, fd: RawDescriptor, ) -> Result<(), DirectIrqError>144     fn plat_irq_ioctl(
145         &self,
146         irq_num: u32,
147         action: u32,
148         fd: RawDescriptor,
149     ) -> Result<(), DirectIrqError> {
150         let count = 1;
151         let u32_size = size_of::<u32>();
152         let mut irq_set = vec_with_array_field::<plat_irq_forward_set, u32>(count);
153         irq_set[0].argsz = (size_of::<plat_irq_forward_set>() + count * u32_size) as u32;
154         irq_set[0].action_flags = action;
155         irq_set[0].count = count as u32;
156         irq_set[0].irq_number_host = irq_num;
157         // Safe as we are the owner of irq_set and allocation provides enough space for
158         // eventfd array.
159         let data = unsafe { irq_set[0].eventfd.as_mut_slice(count * u32_size) };
160         let (left, _right) = data.split_at_mut(u32_size);
161         left.copy_from_slice(&fd.to_ne_bytes()[..]);
162 
163         // Safe as we are the owner of plat_irq_forward and irq_set which are valid value
164         let ret = unsafe { ioctl_with_ref(self, PLAT_IRQ_FORWARD_SET(), &irq_set[0]) };
165         if ret < 0 {
166             Err(DirectIrqError::Enable)
167         } else {
168             Ok(())
169         }
170     }
171 
172     /// Enable hardware triggered GPE handling via SCI interrupt forwarding.
173     /// Note: requires sci_irq_prepare() to be called beforehand.
174     ///
175     /// # Arguments
176     ///
177     /// * `gpe_num` - host GPE number.
178     ///
gpe_enable_forwarding(&mut self, gpe_num: u32) -> Result<(), DirectIrqError>179     pub fn gpe_enable_forwarding(&mut self, gpe_num: u32) -> Result<(), DirectIrqError> {
180         if self.resample.is_none() || !self.sci_irq_prepared {
181             return Err(DirectIrqError::EnableGpe);
182         }
183 
184         self.gpe_forward_ioctl(gpe_num)?;
185 
186         Ok(())
187     }
188 
gpe_forward_ioctl(&self, gpe_num: u32) -> Result<(), DirectIrqError>189     fn gpe_forward_ioctl(&self, gpe_num: u32) -> Result<(), DirectIrqError> {
190         let mut evt_set = vec_with_array_field::<acpi_evt_forward_set, u32>(0);
191         evt_set[0].argsz = (size_of::<acpi_evt_forward_set>()) as u32;
192         evt_set[0].action_flags = ACPI_EVT_FORWARD_SET_GPE_TRIGGER;
193         evt_set[0].__bindgen_anon_1.gpe_host_nr = gpe_num;
194 
195         // Safe as we are the owner of self and evt_set which are valid value
196         let ret = unsafe { ioctl_with_ref(self, ACPI_EVT_FORWARD_SET(), &evt_set[0]) };
197         if ret < 0 {
198             Err(DirectIrqError::EnableGpe)
199         } else {
200             Ok(())
201         }
202     }
203 
204     /// Enable hardware triggered ACPI fixed event handling via SCI interrupt forwarding.
205     /// Note: requires sci_irq_prepare() to be called beforehand.
206     ///
207     /// # Arguments
208     ///
209     /// * `event` - ACPI fixed event.
210     ///
fixed_event_enable_forwarding( &mut self, event: ACPIPMFixedEvent, ) -> Result<(), DirectIrqError>211     pub fn fixed_event_enable_forwarding(
212         &mut self,
213         event: ACPIPMFixedEvent,
214     ) -> Result<(), DirectIrqError> {
215         if self.resample.is_none() || !self.sci_irq_prepared {
216             return Err(DirectIrqError::EnableFixedEvent);
217         }
218 
219         self.fixed_event_forward_ioctl(
220             // Numeric values from ACPI_EVENT_xxx in include/acpi/actypes.h in kernel.
221             match event {
222                 ACPIPMFixedEvent::GlobalLock => 1,
223                 ACPIPMFixedEvent::PowerButton => 2,
224                 ACPIPMFixedEvent::SleepButton => 3,
225                 ACPIPMFixedEvent::RTC => 4,
226             },
227         )?;
228 
229         Ok(())
230     }
231 
fixed_event_forward_ioctl(&self, event_num: u32) -> Result<(), DirectIrqError>232     fn fixed_event_forward_ioctl(&self, event_num: u32) -> Result<(), DirectIrqError> {
233         let mut evt_set = vec_with_array_field::<acpi_evt_forward_set, u32>(0);
234         evt_set[0].argsz = (size_of::<acpi_evt_forward_set>()) as u32;
235         evt_set[0].action_flags = ACPI_EVT_FORWARD_SET_FIXED_EVENT_TRIGGER;
236         evt_set[0].__bindgen_anon_1.fixed_evt_nr = event_num;
237 
238         // Safe as we are the owner of self and evt_set which are valid value
239         let ret = unsafe { ioctl_with_ref(self, ACPI_EVT_FORWARD_SET(), &evt_set[0]) };
240         if ret < 0 {
241             Err(DirectIrqError::EnableFixedEvent)
242         } else {
243             Ok(())
244         }
245     }
246 }
247 
248 impl AsRawDescriptor for DirectIrq {
as_raw_descriptor(&self) -> i32249     fn as_raw_descriptor(&self) -> i32 {
250         self.dev.as_raw_descriptor()
251     }
252 }
253