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 <object/interrupt_event_dispatcher.h>
8 
9 #include <kernel/auto_lock.h>
10 #include <dev/interrupt.h>
11 #include <zircon/rights.h>
12 #include <fbl/alloc_checker.h>
13 #include <fbl/auto_lock.h>
14 #include <fbl/mutex.h>
15 #include <platform.h>
16 
Create(fbl::RefPtr<Dispatcher> * dispatcher,zx_rights_t * rights,uint32_t vector,uint32_t options)17 zx_status_t InterruptEventDispatcher::Create(fbl::RefPtr<Dispatcher>* dispatcher,
18                                              zx_rights_t* rights,
19                                              uint32_t vector,
20                                              uint32_t options) {
21 
22     if (options & ZX_INTERRUPT_VIRTUAL)
23         return ZX_ERR_INVALID_ARGS;
24 
25     // Attempt to construct the dispatcher.
26     fbl::AllocChecker ac;
27     InterruptEventDispatcher* disp = new (&ac) InterruptEventDispatcher(vector);
28     if (!ac.check())
29         return ZX_ERR_NO_MEMORY;
30 
31     // Hold a ref while we check to see if someone else owns this vector or not.
32     // If things go wrong, this ref will be released and the IED will get
33     // cleaned up automatically.
34     auto disp_ref = fbl::AdoptRef<Dispatcher>(disp);
35 
36     Guard<fbl::Mutex> guard{disp->get_lock()};
37 
38     uint32_t interrupt_flags = 0;
39 
40     if (options & ~(ZX_INTERRUPT_REMAP_IRQ | ZX_INTERRUPT_MODE_MASK))
41         return ZX_ERR_INVALID_ARGS;
42 
43     // Remap the vector if we have been asked to do so.
44     if (options & ZX_INTERRUPT_REMAP_IRQ)
45         vector = remap_interrupt(vector);
46 
47     if (!is_valid_interrupt(vector, 0))
48         return ZX_ERR_INVALID_ARGS;
49 
50     bool default_mode = false;
51     enum interrupt_trigger_mode tm = IRQ_TRIGGER_MODE_EDGE;
52     enum interrupt_polarity pol = IRQ_POLARITY_ACTIVE_LOW;
53     switch (options & ZX_INTERRUPT_MODE_MASK) {
54     case ZX_INTERRUPT_MODE_DEFAULT:
55         default_mode = true;
56         break;
57     case ZX_INTERRUPT_MODE_EDGE_LOW:
58         tm = IRQ_TRIGGER_MODE_EDGE;
59         pol = IRQ_POLARITY_ACTIVE_LOW;
60         break;
61     case ZX_INTERRUPT_MODE_EDGE_HIGH:
62         tm = IRQ_TRIGGER_MODE_EDGE;
63         pol = IRQ_POLARITY_ACTIVE_HIGH;
64         break;
65     case ZX_INTERRUPT_MODE_LEVEL_LOW:
66         tm = IRQ_TRIGGER_MODE_LEVEL;
67         pol = IRQ_POLARITY_ACTIVE_LOW;
68         interrupt_flags = INTERRUPT_UNMASK_PREWAIT | INTERRUPT_MASK_POSTWAIT;
69         break;
70     case ZX_INTERRUPT_MODE_LEVEL_HIGH:
71         tm = IRQ_TRIGGER_MODE_LEVEL;
72         pol = IRQ_POLARITY_ACTIVE_HIGH;
73         interrupt_flags = INTERRUPT_UNMASK_PREWAIT | INTERRUPT_MASK_POSTWAIT;
74         break;
75     default:
76         return ZX_ERR_INVALID_ARGS;
77     }
78 
79     if (!default_mode) {
80         zx_status_t status = configure_interrupt(vector, tm, pol);
81         if (status != ZX_OK)
82             return status;
83     }
84 
85     disp->set_flags(interrupt_flags);
86 
87     // Register the interrupt
88     zx_status_t status = disp->RegisterInterruptHandler();
89     if (status != ZX_OK)
90         return status;
91 
92     unmask_interrupt(vector);
93 
94     // Transfer control of the new dispatcher to the creator and we are done.
95     *rights = default_rights();
96     *dispatcher = ktl::move(disp_ref);
97 
98     return ZX_OK;
99 }
100 
BindVcpu(fbl::RefPtr<VcpuDispatcher> vcpu_dispatcher)101 zx_status_t InterruptEventDispatcher::BindVcpu(fbl::RefPtr<VcpuDispatcher> vcpu_dispatcher) {
102     Guard<SpinLock, IrqSave> guard{&spinlock_};
103     if (state() == InterruptState::DESTROYED) {
104         return ZX_ERR_CANCELED;
105     } else if (state() == InterruptState::WAITING) {
106         return ZX_ERR_BAD_STATE;
107     } else if (HasPort()) {
108         return ZX_ERR_ALREADY_BOUND;
109     }
110 
111     for (const auto& vcpu : vcpus_) {
112         if (vcpu == vcpu_dispatcher) {
113             return ZX_OK;
114         } else if (vcpu->guest() != vcpu_dispatcher->guest()) {
115             return ZX_ERR_INVALID_ARGS;
116         }
117     }
118 
119     fbl::AllocChecker ac;
120     vcpus_.push_back(ktl::move(vcpu_dispatcher), &ac);
121     if (!ac.check()) {
122         return ZX_ERR_NO_MEMORY;
123     }
124     if (vcpus_.size() == 1) {
125         MaskInterrupt();
126         UnregisterInterruptHandler();
127         zx_status_t status = register_int_handler(vector_, VcpuIrqHandler, this);
128         if (status != ZX_OK) {
129             return status;
130         }
131         UnmaskInterrupt();
132     }
133     return ZX_OK;
134 }
135 
IrqHandler(void * ctx)136 interrupt_eoi InterruptEventDispatcher::IrqHandler(void* ctx) {
137     InterruptEventDispatcher* self = reinterpret_cast<InterruptEventDispatcher*>(ctx);
138 
139     if (self->get_flags() & INTERRUPT_MASK_POSTWAIT)
140         mask_interrupt(self->vector_);
141 
142     self->InterruptHandler();
143     return IRQ_EOI_DEACTIVATE;
144 }
145 
VcpuIrqHandler(void * ctx)146 interrupt_eoi InterruptEventDispatcher::VcpuIrqHandler(void* ctx) {
147     InterruptEventDispatcher* self = reinterpret_cast<InterruptEventDispatcher*>(ctx);
148     self->VcpuInterruptHandler();
149     // Skip the EOI to allow the guest to deactivate the interrupt.
150     return IRQ_EOI_PRIORITY_DROP;
151 }
152 
VcpuInterruptHandler()153 void InterruptEventDispatcher::VcpuInterruptHandler() {
154     Guard<SpinLock, IrqSave> guard{&spinlock_};
155     cpu_mask_t mask = 0;
156     for (const auto& vcpu : vcpus_) {
157         mask |= vcpu->PhysicalInterrupt(vector_);
158     }
159     if (mask != 0) {
160         mp_interrupt(MP_IPI_TARGET_MASK, mask);
161     }
162 }
163 
MaskInterrupt()164 void InterruptEventDispatcher::MaskInterrupt() {
165     mask_interrupt(vector_);
166 }
167 
UnmaskInterrupt()168 void InterruptEventDispatcher::UnmaskInterrupt() {
169     unmask_interrupt(vector_);
170 }
171 
RegisterInterruptHandler()172 zx_status_t InterruptEventDispatcher::RegisterInterruptHandler() {
173     return register_int_handler(vector_, IrqHandler, this);
174 }
175 
UnregisterInterruptHandler()176 void InterruptEventDispatcher::UnregisterInterruptHandler() {
177     register_int_handler(vector_, nullptr, nullptr);
178 }
179 
HasVcpu() const180 bool InterruptEventDispatcher::HasVcpu() const {
181     return !vcpus_.is_empty();
182 }
183