| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // Copyright (c) 2015 Intel Corporation |
| // |
| // 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 |
| |
| #include <assert.h> |
| #include <debug.h> |
| #include <lib/acpi_lite/apic.h> |
| #include <lib/acpi_lite/structures.h> |
| #include <sys/types.h> |
| #include <trace.h> |
| #include <zircon/types.h> |
| |
| #include <arch/regs.h> |
| #include <arch/x86.h> |
| #include <arch/x86/feature.h> |
| #include <arch/x86/platform_access.h> |
| #include <arch/x86/pv.h> |
| #include <dev/interrupt.h> |
| #include <fbl/algorithm.h> |
| #include <kernel/stats.h> |
| #include <kernel/thread.h> |
| #include <ktl/optional.h> |
| #include <lk/init.h> |
| #include <object/resource_dispatcher.h> |
| #include <platform/pc.h> |
| #include <platform/pc/acpi.h> |
| #include <platform/pic.h> |
| |
| #include "interrupt_manager.h" |
| #include "platform_p.h" |
| |
| #include <ktl/enforce.h> |
| |
| namespace { |
| |
| // Values from ioapic to cache for calls to interrupt_get_base_vector / interrupt_get_max_vector |
| ktl::optional<GsiRange> x64_gsis; |
| |
| // Interface passed to InterruptManager to construct the real system interrupt |
| // manager. |
| class IoApic { |
| public: |
| static bool IsValidInterrupt(unsigned int vector, uint32_t flags) { |
| return is_valid_interrupt(vector, flags); |
| } |
| static uint8_t FetchIrqVector(unsigned int vector) { return apic_io_fetch_irq_vector(vector); } |
| static void ConfigureIrqVector(uint32_t global_irq, uint8_t x86_vector) { |
| apic_io_configure_irq_vector(global_irq, x86_vector); |
| } |
| static void ConfigureIrq(uint32_t global_irq, enum interrupt_trigger_mode trig_mode, |
| enum interrupt_polarity polarity, |
| enum apic_interrupt_delivery_mode del_mode, bool mask, |
| enum apic_interrupt_dst_mode dst_mode, uint8_t dst, uint8_t vector) { |
| apic_io_configure_irq(global_irq, trig_mode, polarity, del_mode, mask, dst_mode, dst, vector); |
| } |
| static void MaskIrq(uint32_t global_irq, bool mask) { apic_io_mask_irq(global_irq, mask); } |
| static zx_status_t FetchIrqConfig(uint32_t global_irq, enum interrupt_trigger_mode* trig_mode, |
| enum interrupt_polarity* polarity) { |
| return apic_io_fetch_irq_config(global_irq, trig_mode, polarity); |
| } |
| }; |
| |
| // Singleton for managing interrupts. This is fully initialized in |
| // platform_init_apic(). |
| InterruptManager<IoApic> kInterruptManager; |
| |
| // Convert an ACPI entry into the format required by the platform's APIC code. |
| io_apic_isa_override ParseIsaOverride(const acpi_lite::AcpiMadtIntSourceOverrideEntry* record) { |
| // 0 means ISA, ISOs are only ever for ISA IRQs |
| if (record->bus != 0) { |
| panic("Invalid bus for IO APIC interrupt override.\n"); |
| } |
| |
| // "Conforms" below means conforms to the bus spec: edge triggered and active high. |
| const uint32_t flags = record->flags; |
| const interrupt_polarity polarity = [flags]() { |
| uint32_t polarity = flags & ACPI_MADT_FLAG_POLARITY_MASK; |
| switch (polarity) { |
| case ACPI_MADT_FLAG_POLARITY_CONFORMS: |
| case ACPI_MADT_FLAG_POLARITY_HIGH: |
| return IRQ_POLARITY_ACTIVE_HIGH; |
| case ACPI_MADT_FLAG_POLARITY_LOW: |
| return IRQ_POLARITY_ACTIVE_LOW; |
| default: |
| panic("Unknown IRQ polarity in override: %u\n", polarity); |
| } |
| }(); |
| |
| const interrupt_trigger_mode trigger_mode = [flags]() { |
| uint32_t trigger = flags & ACPI_MADT_FLAG_TRIGGER_MASK; |
| switch (trigger) { |
| case ACPI_MADT_FLAG_TRIGGER_CONFORMS: |
| case ACPI_MADT_FLAG_TRIGGER_EDGE: |
| return IRQ_TRIGGER_MODE_EDGE; |
| break; |
| case ACPI_MADT_FLAG_TRIGGER_LEVEL: |
| return IRQ_TRIGGER_MODE_LEVEL; |
| default: |
| panic("Unknown IRQ trigger in override: %u\n", trigger); |
| } |
| }(); |
| |
| return io_apic_isa_override{ |
| .isa_irq = record->source, |
| .remapped = true, |
| .tm = trigger_mode, |
| .pol = polarity, |
| .global_irq = record->global_sys_interrupt, |
| }; |
| } |
| |
| } // namespace |
| |
| static void platform_init_apic(uint level) { |
| pic_map(PIC1_BASE, PIC2_BASE); |
| pic_disable(); |
| |
| acpi_lite::AcpiParser& parser = GlobalAcpiLiteParser(); |
| |
| // Enumerate the IO APICs |
| fbl::Vector<io_apic_descriptor> descriptors; |
| zx_status_t status = acpi_lite::EnumerateIoApics( |
| parser, [&descriptors](const acpi_lite::AcpiMadtIoApicEntry& descriptor) { |
| fbl::AllocChecker ac; |
| descriptors.push_back( |
| io_apic_descriptor{ |
| .apic_id = descriptor.io_apic_id, |
| .global_irq_base = descriptor.global_system_interrupt_base, |
| .paddr = descriptor.io_apic_address, |
| }, |
| &ac); |
| return ac.check() ? ZX_OK : ZX_ERR_NO_MEMORY; |
| }); |
| if (status != ZX_OK) { |
| panic("Could not get IO APIC details: %d\n", status); |
| } |
| |
| // Enumerate interrupt source overrides. |
| fbl::Vector<io_apic_isa_override> overrides; |
| status = acpi_lite::EnumerateIoApicIsaOverrides( |
| parser, [&overrides](const acpi_lite::AcpiMadtIntSourceOverrideEntry& isa_override) { |
| fbl::AllocChecker ac; |
| overrides.push_back(ParseIsaOverride(&isa_override), &ac); |
| return ac.check() ? ZX_OK : ZX_ERR_NO_MEMORY; |
| }); |
| if (status != ZX_OK) { |
| panic("Could not get interrupt source overrides: %d\n", status); |
| } |
| |
| apic_vm_init(); |
| apic_local_init(); |
| apic_io_init(descriptors.data(), descriptors.size(), overrides.data(), overrides.size()); |
| x64_gsis = apic_io_get_gsi_range(); |
| |
| ASSERT(arch_ints_disabled()); |
| |
| // Initialize the delivery modes/targets for the ISA interrupts |
| uint8_t bsp_apic_id = apic_bsp_id(); |
| for (uint8_t irq = 0; irq < 8; ++irq) { |
| // Explicitly skip mapping the PIC2 interrupt, since it is actually |
| // just used internally on the PICs for daisy chaining. QEMU remaps |
| // ISA IRQ 0 to global IRQ 2, but does not remap ISA IRQ 2 off of |
| // global IRQ 2, so skipping this mapping also prevents a collision |
| // with the PIT IRQ. |
| if (irq != ISA_IRQ_PIC2) { |
| apic_io_configure_isa_irq(irq, DELIVERY_MODE_FIXED, IO_APIC_IRQ_MASK, DST_MODE_PHYSICAL, |
| bsp_apic_id, 0); |
| } |
| apic_io_configure_isa_irq(static_cast<uint8_t>(irq + 8), DELIVERY_MODE_FIXED, IO_APIC_IRQ_MASK, |
| DST_MODE_PHYSICAL, bsp_apic_id, 0); |
| } |
| |
| status = kInterruptManager.Init(); |
| ResourceDispatcher::InitializeAllocator(ZX_RSRC_KIND_IRQ, interrupt_get_base_vector(), |
| interrupt_get_max_vector()); |
| ASSERT(status == ZX_OK); |
| } |
| LK_INIT_HOOK(apic, &platform_init_apic, LK_INIT_LEVEL_VM + 2) |
| |
| zx_status_t mask_interrupt(unsigned int vector) { return kInterruptManager.MaskInterrupt(vector); } |
| |
| zx_status_t unmask_interrupt(unsigned int vector) { |
| return kInterruptManager.UnmaskInterrupt(vector); |
| } |
| |
| zx_status_t configure_interrupt(unsigned int vector, enum interrupt_trigger_mode tm, |
| enum interrupt_polarity pol) { |
| return kInterruptManager.ConfigureInterrupt(vector, tm, pol); |
| } |
| |
| zx_status_t get_interrupt_config(unsigned int vector, enum interrupt_trigger_mode* tm, |
| enum interrupt_polarity* pol) { |
| return kInterruptManager.GetInterruptConfig(vector, tm, pol); |
| } |
| |
| void platform_irq(iframe_t* frame) { |
| CPU_STATS_INC(interrupts); |
| // get the current vector |
| uint64_t x86_vector = frame->vector; |
| DEBUG_ASSERT(x86_vector >= X86_INT_PLATFORM_BASE && x86_vector <= X86_INT_PLATFORM_MAX); |
| |
| // deliver the interrupt |
| kInterruptManager.InvokeX86Vector(static_cast<uint8_t>(x86_vector)); |
| |
| // NOTE: On x86, we always deactivate the interrupt. |
| apic_issue_eoi(); |
| } |
| |
| zx_status_t register_int_handler(unsigned int vector, int_handler handler, void* arg) { |
| return kInterruptManager.RegisterInterruptHandler(vector, handler, arg); |
| } |
| |
| zx_status_t register_permanent_int_handler(unsigned int vector, int_handler handler, void* arg) { |
| return kInterruptManager.RegisterInterruptHandler(vector, handler, arg, true /* Permanent */); |
| } |
| |
| // On x64 these methods return the base and max Global System Interrupts as |
| // defined by ACPI and described by MADT tables. |
| // ACPI Spec 6.1, section 5.2.12 & section 5.2.13. |
| uint32_t interrupt_get_base_vector(void) { |
| ZX_DEBUG_ASSERT(x64_gsis.has_value()); |
| return x64_gsis.value().start; |
| } |
| uint32_t interrupt_get_max_vector(void) { |
| ZX_DEBUG_ASSERT(x64_gsis.has_value()); |
| return x64_gsis.value().end; |
| } |
| |
| bool is_valid_interrupt(unsigned int vector, uint32_t flags) { |
| return apic_io_is_valid_irq(vector); |
| } |
| |
| unsigned int remap_interrupt(unsigned int vector) { |
| if (vector > NUM_ISA_IRQS) { |
| return vector; |
| } |
| return apic_io_isa_to_global(static_cast<uint8_t>(vector)); |
| } |
| |
| void shutdown_interrupts(void) { pic_disable(); } |
| |
| void shutdown_interrupts_curr_cpu(void) { |
| if (x86_hypervisor_has_pv_eoi()) { |
| MsrAccess msr; |
| PvEoi::get()->Disable(&msr); |
| } |
| |
| // TODO(maniscalco): Walk interrupt redirection entries and make sure nothing targets this CPU. |
| } |
| |
| // Intel 64 socs support the IOAPIC and Local APIC which support MSI by default. |
| // See 10.1, 10.4, and 10.11 of Intel® 64 and IA-32 Architectures Software Developer’s |
| // Manual 3A |
| bool msi_is_supported(void) { return true; } |
| bool msi_supports_masking() { return false; } |
| // Since we do not support masking on x64 it is an error to call |msi_mask_unmask|. |
| void msi_mask_unmask(const msi_block_t* block, uint msi_id, bool mask) { PANIC_UNIMPLEMENTED; } |
| |
| zx_status_t msi_alloc_block(uint requested_irqs, bool can_target_64bit, bool is_msix, |
| msi_block_t* out_block) { |
| return kInterruptManager.MsiAllocBlock(requested_irqs, can_target_64bit, is_msix, out_block); |
| } |
| |
| void msi_free_block(msi_block_t* block) { return kInterruptManager.MsiFreeBlock(block); } |
| |
| void msi_register_handler(const msi_block_t* block, uint msi_id, int_handler handler, void* ctx) { |
| return kInterruptManager.MsiRegisterHandler(block, msi_id, handler, ctx); |
| } |