1// Copyright 2016 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <dev/interrupt.h> 8#include <object/interrupt_dispatcher.h> 9#include <object/port_dispatcher.h> 10#include <object/process_dispatcher.h> 11#include <platform.h> 12#include <zircon/syscalls/port.h> 13 14InterruptDispatcher::InterruptDispatcher() 15 : timestamp_(0), state_(InterruptState::IDLE) { 16 event_init(&event_, false, EVENT_FLAG_AUTOUNSIGNAL); 17} 18 19zx_status_t InterruptDispatcher::WaitForInterrupt(zx_time_t* out_timestamp) { 20 while (true) { 21 { 22 Guard<SpinLock, IrqSave> guard{&spinlock_}; 23 if (port_dispatcher_) { 24 return ZX_ERR_BAD_STATE; 25 } 26 switch (state_) { 27 case InterruptState::DESTROYED: 28 return ZX_ERR_CANCELED; 29 case InterruptState::TRIGGERED: 30 state_ = InterruptState::NEEDACK; 31 *out_timestamp = timestamp_; 32 timestamp_ = 0; 33 return event_unsignal(&event_); 34 case InterruptState::NEEDACK: 35 if (flags_ & INTERRUPT_UNMASK_PREWAIT) { 36 UnmaskInterrupt(); 37 } 38 break; 39 case InterruptState::IDLE: 40 break; 41 default: 42 return ZX_ERR_BAD_STATE; 43 } 44 state_ = InterruptState::WAITING; 45 } 46 47 { 48 ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::INTERRUPT); 49 zx_status_t status = event_wait_deadline(&event_, ZX_TIME_INFINITE, true); 50 if (status != ZX_OK) { 51 // The event_wait call was interrupted and we need to retry 52 // but before we retry we will set the interrupt state 53 // back to IDLE if we are still in the WAITING state 54 Guard<SpinLock, IrqSave> guard{&spinlock_}; 55 if (state_ == InterruptState::WAITING) { 56 state_ = InterruptState::IDLE; 57 } 58 return status; 59 } 60 } 61 } 62} 63 64bool InterruptDispatcher::SendPacketLocked(zx_time_t timestamp) { 65 bool status = port_dispatcher_->QueueInterruptPacket(&port_packet_, timestamp); 66 if (flags_ & INTERRUPT_MASK_POSTWAIT) { 67 MaskInterrupt(); 68 } 69 timestamp_ = 0; 70 return status; 71} 72 73zx_status_t InterruptDispatcher::Trigger(zx_time_t timestamp) { 74 75 if (!(flags_ & INTERRUPT_VIRTUAL)) 76 return ZX_ERR_BAD_STATE; 77 78 // Using AutoReschedDisable is necessary for correctness to prevent 79 // context-switching to the woken thread while holding spinlock_. 80 AutoReschedDisable resched_disable; 81 resched_disable.Disable(); 82 Guard<SpinLock, IrqSave> guard{&spinlock_}; 83 84 // only record timestamp if this is the first signal since we started waiting 85 if (!timestamp_) { 86 timestamp_ = timestamp; 87 } 88 if (state_ == InterruptState::DESTROYED) { 89 return ZX_ERR_CANCELED; 90 } 91 if (state_ == InterruptState::NEEDACK && port_dispatcher_) { 92 // Cannot trigger a interrupt without ACK 93 // only record timestamp if this is the first signal since we started waiting 94 return ZX_OK; 95 } 96 97 if (port_dispatcher_) { 98 SendPacketLocked(timestamp); 99 state_ = InterruptState::NEEDACK; 100 } else { 101 Signal(); 102 state_ = InterruptState::TRIGGERED; 103 } 104 return ZX_OK; 105} 106 107void InterruptDispatcher::InterruptHandler() { 108 Guard<SpinLock, IrqSave> guard{&spinlock_}; 109 110 // only record timestamp if this is the first IRQ since we started waiting 111 if (!timestamp_) { 112 timestamp_ = current_time(); 113 } 114 if (state_ == InterruptState::NEEDACK && port_dispatcher_) { 115 return; 116 } 117 if (port_dispatcher_) { 118 SendPacketLocked(timestamp_); 119 state_ = InterruptState::NEEDACK; 120 } else { 121 if (flags_ & INTERRUPT_MASK_POSTWAIT) { 122 MaskInterrupt(); 123 } 124 Signal(); 125 state_ = InterruptState::TRIGGERED; 126 } 127} 128 129zx_status_t InterruptDispatcher::Destroy() { 130 // Using AutoReschedDisable is necessary for correctness to prevent 131 // context-switching to the woken thread while holding spinlock_. 132 AutoReschedDisable resched_disable; 133 resched_disable.Disable(); 134 Guard<SpinLock, IrqSave> guard{&spinlock_}; 135 136 MaskInterrupt(); 137 UnregisterInterruptHandler(); 138 139 if (port_dispatcher_) { 140 bool packet_was_in_queue = port_dispatcher_->RemoveInterruptPacket(&port_packet_); 141 if ((state_ == InterruptState::NEEDACK) && 142 !packet_was_in_queue) { 143 state_ = InterruptState::DESTROYED; 144 return ZX_ERR_NOT_FOUND; 145 } 146 if ((state_ == InterruptState::IDLE) || 147 ((state_ == InterruptState::NEEDACK) && 148 packet_was_in_queue)) { 149 state_ = InterruptState::DESTROYED; 150 return ZX_OK; 151 } 152 } else { 153 state_ = InterruptState::DESTROYED; 154 Signal(); 155 } 156 return ZX_OK; 157} 158 159zx_status_t InterruptDispatcher::Bind(fbl::RefPtr<PortDispatcher> port_dispatcher, 160 fbl::RefPtr<InterruptDispatcher> interrupt, uint64_t key) { 161 Guard<SpinLock, IrqSave> guard{&spinlock_}; 162 if (state_ == InterruptState::DESTROYED) { 163 return ZX_ERR_CANCELED; 164 } 165 if (port_dispatcher_) { 166 return ZX_ERR_ALREADY_BOUND; 167 } 168 if (state_ == InterruptState::WAITING) { 169 return ZX_ERR_BAD_STATE; 170 } 171 172 port_dispatcher_ = fbl::move(port_dispatcher); 173 port_packet_.key = key; 174 return ZX_OK; 175} 176 177zx_status_t InterruptDispatcher::Ack() { 178 // Using AutoReschedDisable is necessary for correctness to prevent 179 // context-switching to the woken thread while holding spinlock_. 180 AutoReschedDisable resched_disable; 181 resched_disable.Disable(); 182 Guard<SpinLock, IrqSave> guard{&spinlock_}; 183 if (port_dispatcher_ == nullptr) { 184 return ZX_ERR_BAD_STATE; 185 } 186 if (state_ == InterruptState::DESTROYED) { 187 return ZX_ERR_CANCELED; 188 } 189 if (state_ == InterruptState::NEEDACK) { 190 if (flags_ & INTERRUPT_UNMASK_PREWAIT) { 191 UnmaskInterrupt(); 192 } 193 if (timestamp_) { 194 if (!SendPacketLocked(timestamp_)) { 195 // We cannot queue another packet here. 196 // If we reach here it means that the 197 // interrupt packet has not been processed, 198 // another interrupt has occurred & then the 199 // interrupt was ACK'd 200 return ZX_ERR_BAD_STATE; 201 } 202 } else { 203 state_ = InterruptState::IDLE; 204 } 205 } 206 return ZX_OK; 207} 208 209void InterruptDispatcher::on_zero_handles() { 210 Destroy(); 211} 212