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
InterruptDispatcher()14 InterruptDispatcher::InterruptDispatcher()
15 : timestamp_(0), state_(InterruptState::IDLE) {
16 event_init(&event_, false, EVENT_FLAG_AUTOUNSIGNAL);
17 }
18
WaitForInterrupt(zx_time_t * out_timestamp)19 zx_status_t InterruptDispatcher::WaitForInterrupt(zx_time_t* out_timestamp) {
20 while (true) {
21 {
22 Guard<SpinLock, IrqSave> guard{&spinlock_};
23 if (port_dispatcher_ || HasVcpu()) {
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
SendPacketLocked(zx_time_t timestamp)64 bool 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
Trigger(zx_time_t timestamp)73 zx_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
InterruptHandler()107 void 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
Destroy()129 zx_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
Bind(fbl::RefPtr<PortDispatcher> port_dispatcher,uint64_t key)159 zx_status_t InterruptDispatcher::Bind(fbl::RefPtr<PortDispatcher> port_dispatcher, uint64_t key) {
160 Guard<SpinLock, IrqSave> guard{&spinlock_};
161 if (state_ == InterruptState::DESTROYED) {
162 return ZX_ERR_CANCELED;
163 } else if (state_ == InterruptState::WAITING) {
164 return ZX_ERR_BAD_STATE;
165 } else if (port_dispatcher_ || HasVcpu()) {
166 return ZX_ERR_ALREADY_BOUND;
167 }
168
169 port_dispatcher_ = ktl::move(port_dispatcher);
170 port_packet_.key = key;
171 return ZX_OK;
172 }
173
Ack()174 zx_status_t InterruptDispatcher::Ack() {
175 // Using AutoReschedDisable is necessary for correctness to prevent
176 // context-switching to the woken thread while holding spinlock_.
177 AutoReschedDisable resched_disable;
178 resched_disable.Disable();
179 Guard<SpinLock, IrqSave> guard{&spinlock_};
180 if (port_dispatcher_ == nullptr) {
181 return ZX_ERR_BAD_STATE;
182 }
183 if (state_ == InterruptState::DESTROYED) {
184 return ZX_ERR_CANCELED;
185 }
186 if (state_ == InterruptState::NEEDACK) {
187 if (flags_ & INTERRUPT_UNMASK_PREWAIT) {
188 UnmaskInterrupt();
189 }
190 if (timestamp_) {
191 if (!SendPacketLocked(timestamp_)) {
192 // We cannot queue another packet here.
193 // If we reach here it means that the
194 // interrupt packet has not been processed,
195 // another interrupt has occurred & then the
196 // interrupt was ACK'd
197 return ZX_ERR_BAD_STATE;
198 }
199 } else {
200 state_ = InterruptState::IDLE;
201 }
202 }
203 return ZX_OK;
204 }
205
on_zero_handles()206 void InterruptDispatcher::on_zero_handles() {
207 Destroy();
208 }
209