| // Copyright 2019 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 | 
 |  | 
 | #if ARCH_X86 | 
 | #include <arch/x86/apic.h> | 
 | #endif | 
 | #include <lib/unittest/unittest.h> | 
 | #include <platform.h> | 
 |  | 
 | #include <ktl/atomic.h> | 
 | #include <object/interrupt_dispatcher.h> | 
 | #include <object/interrupt_event_dispatcher.h> | 
 | #include <object/port_dispatcher.h> | 
 |  | 
 | namespace { | 
 |  | 
 | // Tests that if an irq handler fires at the same time as an interrupt dispatcher is destroyed | 
 | // the system does not deadlock. | 
 | static bool TestConcurrentIntEventDispatcherTeardown() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   // Generating the interrupt events for this test is necessarily arch specific and is only | 
 |   // implemented for x86 here. | 
 | #if ARCH_X86 | 
 |   KernelHandle<InterruptDispatcher> interrupt; | 
 |   zx_rights_t rights; | 
 |  | 
 |   uint32_t gsi; | 
 |   constexpr uint32_t gsi_search_max = 24; | 
 |   for (gsi = 0; gsi < gsi_search_max; gsi++) { | 
 |     zx_status_t status = | 
 |         InterruptEventDispatcher::Create(&interrupt, &rights, gsi, ZX_INTERRUPT_MODE_DEFAULT); | 
 |     if (status == ZX_OK) { | 
 |       break; | 
 |     } | 
 |   } | 
 |   ASSERT_NE(gsi, gsi_search_max, "Failed to find free global interrupt"); | 
 |  | 
 |   // Find the local vector, put it in the low byte of our shared state. | 
 |   ktl::atomic<uint16_t> state = apic_io_fetch_irq_vector(gsi); | 
 |  | 
 |   // Spin up a thread to generate the interrupt. As IPIs cannot be masked this causes the | 
 |   // associated InterruptDispatcher handler to constantly get invoked, which is what we want. | 
 |   Thread* int_thread = Thread::Create( | 
 |       "int", | 
 |       [](void* arg) -> int { | 
 |         ktl::atomic<uint16_t>* state = static_cast<ktl::atomic<uint16_t>*>(arg); | 
 |         uint8_t vec = state->load(ktl::memory_order_seq_cst) & 0xffu; | 
 |         // Loop until anything is set in the high byte of the state. | 
 |         while ((state->load(ktl::memory_order_seq_cst) & 0xff00) == 0) { | 
 |           apic_send_self_ipi(vec, DELIVERY_MODE_FIXED); | 
 |           Thread::Current::Yield(); | 
 |         } | 
 |         return -1; | 
 |       }, | 
 |       &state, DEFAULT_PRIORITY); | 
 |   int_thread->Resume(); | 
 |  | 
 |   // Remove the interrupt and if we don't deadlock and keep executing then all is well. | 
 |   interrupt.reset(); | 
 |  | 
 |   // Signal the thread by setting bits in the high byte. | 
 |   state.fetch_or(0xff00, ktl::memory_order_seq_cst); | 
 |   // Shutdown the test. | 
 |   zx_status_t status = int_thread->Join(nullptr, current_time() + ZX_SEC(5)); | 
 |   EXPECT_EQ(status, ZX_OK); | 
 | #endif | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | UNITTEST_START_TESTCASE(interrupt_event_dispatcher_tests) | 
 | UNITTEST("ConcurrentIntEventDispatcherTeardown", TestConcurrentIntEventDispatcherTeardown) | 
 | UNITTEST_END_TESTCASE(interrupt_event_dispatcher_tests, "interrupt_event_dispatcher_tests", | 
 |                       "InterruptEventDispatcher tests") |