| // Copyright 2016 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| use core::ptr::NonNull; | |
| use ksync::lock; | |
| use zx_status::Status; | |
| use zx_types::{ | |
| ZX_INTERRUPT_TIMESTAMP_MONO, ZX_INTERRUPT_VIRTUAL, ZX_INTERRUPT_WAKE_VECTOR, | |
| ZX_VIRTUAL_INTERRUPT_UNTRIGGERED, zx_rights_t, zx_time_t, | |
| }; | |
| use super::DispatcherOps; | |
| use super::handle::KernelHandle; | |
| use super::interrupt_dispatcher::{ | |
| INTERRUPT_TIMESTAMP_MONO, INTERRUPT_VIRTUAL, INTERRUPT_WAKE_VECTOR, InterruptDispatcher, | |
| InterruptKind, PostAckState, WakeVectorDiagnostics, | |
| }; | |
| use crate::counters::define_kcounter; | |
| define_kcounter!( | |
| DISPATCHER_VIRTUAL_INTERRUPT_CREATE_COUNT, | |
| "dispatcher.virtual_interrupt.create", | |
| Sum | |
| ); | |
| define_kcounter!( | |
| DISPATCHER_VIRTUAL_INTERRUPT_DESTROY_COUNT, | |
| "dispatcher.virtual_interrupt.destroy", | |
| Sum | |
| ); | |
| /// Virtual interrupt dispatcher variant triggered via `zx_interrupt_trigger`. | |
| #[repr(C)] | |
| pub struct VirtualInterruptDispatcher { | |
| disp: NonNull<InterruptDispatcher>, | |
| } | |
| impl Drop for VirtualInterruptDispatcher { | |
| fn drop(&mut self) { | |
| // SAFETY: `self.disp` points to the enclosing `InterruptDispatcher`, which is valid | |
| // during `~InterruptDispatcher`. | |
| let disp = unsafe { self.disp.as_ref() }; | |
| if disp.is_wake_vector() { | |
| disp.destroy_wake_event(); | |
| } | |
| DISPATCHER_VIRTUAL_INTERRUPT_DESTROY_COUNT.add(1); | |
| } | |
| } | |
| impl VirtualInterruptDispatcher { | |
| /// Creates a new virtual interrupt dispatcher with `options`. | |
| pub fn create( | |
| options: u32, | |
| ) -> Result<(KernelHandle<InterruptDispatcher>, zx_rights_t), Status> { | |
| if (options & ZX_INTERRUPT_VIRTUAL) == 0 { | |
| return Err(Status::INVALID_ARGS); | |
| } | |
| const PERMITTED_OPTIONS: u32 = | |
| ZX_INTERRUPT_VIRTUAL | ZX_INTERRUPT_TIMESTAMP_MONO | ZX_INTERRUPT_WAKE_VECTOR; | |
| if (options & !PERMITTED_OPTIONS) != 0 { | |
| return Err(Status::INVALID_ARGS); | |
| } | |
| let mut flags = INTERRUPT_VIRTUAL; | |
| if (options & ZX_INTERRUPT_TIMESTAMP_MONO) != 0 { | |
| flags |= INTERRUPT_TIMESTAMP_MONO; | |
| } | |
| if (options & ZX_INTERRUPT_WAKE_VECTOR) != 0 { | |
| flags |= INTERRUPT_WAKE_VECTOR; | |
| } | |
| // Attempt to construct the dispatcher. | |
| let disp = InterruptDispatcher::create_raw(flags, options, |disp| { | |
| Self::new(disp); | |
| }) | |
| .ok_or(Status::NO_MEMORY)?; | |
| let new_handle = KernelHandle::new(disp); | |
| // Transfer control of the new dispatcher to the creator and we are done. | |
| Ok((new_handle, InterruptDispatcher::default_rights())) | |
| } | |
| #[allow(clippy::new_ret_no_self)] | |
| fn new(disp: &InterruptDispatcher) { | |
| // SAFETY: Called once during `InterruptDispatcher::create_raw` construction. | |
| unsafe { | |
| disp.init_kind(InterruptKind::Virtual(Self { disp: NonNull::from(disp) })); | |
| } | |
| // Immediately after construction, virtual interrupt objects are always in the | |
| // untriggered state. Make sure that our initial signal state reflects this. | |
| disp.update_state(0, ZX_VIRTUAL_INTERRUPT_UNTRIGGERED); | |
| DISPATCHER_VIRTUAL_INTERRUPT_CREATE_COUNT.add(1); | |
| if disp.is_wake_vector() { | |
| disp.initialize_wake_event(); | |
| } | |
| } | |
| /// Populates wake vector diagnostics for this virtual interrupt. | |
| pub fn get_diagnostics(&self, diagnostics_out: &mut WakeVectorDiagnostics) { | |
| // SAFETY: `self.disp` points to the valid enclosing `InterruptDispatcher`. | |
| let disp = unsafe { self.disp.as_ref() }; | |
| diagnostics_out.enabled = disp.is_wake_vector(); | |
| diagnostics_out.koid = disp.get_koid(); | |
| diagnostics_out.print_extra(format_args!("VirtIRQ {}", disp.get_koid())); | |
| } | |
| /// Blocks until the virtual interrupt is triggered or destroyed, updating the | |
| /// `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal. | |
| pub fn wait_for_interrupt(&self, disp: &InterruptDispatcher) -> Result<zx_time_t, Status> { | |
| loop { | |
| // Hold the dispatcher lock during the first phase, and if we decide that we | |
| // need to block, be sure that our UNTRIGGERED signal has been asserted. | |
| { | |
| lock!(let guard = disp.state().lock_lock()); | |
| let opt_status = disp.begin_wait_for_interrupt(); | |
| if let Some(val) = opt_status { | |
| return val; | |
| } | |
| disp.update_state_locked(guard.token(), 0, ZX_VIRTUAL_INTERRUPT_UNTRIGGERED); | |
| } | |
| disp.do_wait_for_interrupt_block()?; | |
| } | |
| } | |
| /// Triggers the virtual interrupt at `timestamp`, clearing `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED`. | |
| pub fn trigger(&self, disp: &InterruptDispatcher, timestamp: zx_time_t) -> Result<(), Status> { | |
| lock!(let dispatcher_guard = disp.state().lock_lock()); | |
| let result = disp.state().trigger(disp, timestamp); | |
| // If the trigger operation succeeded, then we know that we must be in either | |
| // the triggered or need-ack state, and we should clear the UNTRIGGERED | |
| // signal. | |
| if result.is_ok() { | |
| disp.update_state_locked(dispatcher_guard.token(), ZX_VIRTUAL_INTERRUPT_UNTRIGGERED, 0); | |
| } | |
| result | |
| } | |
| /// Acknowledges the virtual interrupt and updates `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED`. | |
| pub fn ack(&self, disp: &InterruptDispatcher) -> Result<(), Status> { | |
| // Hold the dispatcher lock while we perform our lower level ack operation. | |
| // If this succeeds, make sure that the UNTRIGGERED signal is asserted. | |
| lock!(let guard = disp.state().lock_lock()); | |
| let result = disp.ack_internal(); | |
| // If something goes wrong with the ack, then our UNTRIGGERED signal state | |
| // should not have changed. | |
| let result = result?; | |
| // Things went well, we successfully acked the interrupt and should assert the | |
| // UNTRIGGERED signal. If, however, the interrupt immediately retriggered | |
| // (because it already had another pending signal), be sure to clear the | |
| // UNTRIGGERED signal after asserting it. | |
| disp.update_state_locked(guard.token(), 0, ZX_VIRTUAL_INTERRUPT_UNTRIGGERED); | |
| if result == PostAckState::Retriggered { | |
| disp.update_state_locked(guard.token(), ZX_VIRTUAL_INTERRUPT_UNTRIGGERED, 0); | |
| } else { | |
| debug_assert!(result == PostAckState::FullyAcked); | |
| } | |
| Ok(()) | |
| } | |
| /// Destroys the virtual interrupt and asserts `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED`. | |
| pub fn destroy(&self, disp: &InterruptDispatcher) -> Result<(), Status> { | |
| // An interrupt which has been explicitly destroyed (using a call to | |
| // zx_interrupt_destroy) can no longer be in the triggered state. Make sure | |
| // that we assert the virtual interrupt's UNTRIGGERED signal to be sure that | |
| // we don't accidentally end up with waiters stuck waiting on a destroyed | |
| // interrupt. | |
| lock!(let guard = disp.state().lock_lock()); | |
| let result = disp.state().destroy(disp); | |
| disp.update_state_locked(guard.token(), 0, ZX_VIRTUAL_INTERRUPT_UNTRIGGERED); | |
| result | |
| } | |
| } |