Newer
Older
minerva / Kernel / Arch / aarch64 / Interrupts / GIC.cpp
@minerva minerva on 13 Jul 8 KB Initial commit
/*
 * Copyright (c) 2024, Sönke Holz <sholz8530@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Arch/aarch64/InterruptManagement.h>
#include <Kernel/Arch/aarch64/Interrupts/GIC.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>
#include <Kernel/Firmware/DeviceTree/Driver.h>
#include <Kernel/Firmware/DeviceTree/Management.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>

namespace Kernel {

// 4.1.2 Distributor register map
struct GIC::DistributorRegisters {
    enum class ControlBits : u32 {
        Enable = 1 << 0,
    };

    static constexpr size_t INTERRUPT_CONTROLLER_TYPE_IT_LINES_NUMBER_OFFSET = 0;
    static constexpr u32 INTERRUPT_CONTROLLER_TYPE_IT_LINES_NUMBER_MASK = (1 << 5) - 1;

    u32 control;                    // GICD_CTLR
    u32 interrupt_controller_type;  // GICR_TYPER
    u32 implementer_identification; // GICD_IIDR
    u32 reserved0[5];
    u32 implementation_defined0[8];
    u32 reserved1[16];
    u32 interrupt_group[32];         // GICD_IGROUPn
    u32 interrupt_set_enable[32];    // GICD_ISENABLERn
    u32 interrupt_clear_enable[32];  // GICD_ICENABLERn
    u32 interrupt_set_pending[32];   // GICD_ISPENDRn
    u32 interrupt_clear_pending[32]; // GICD_ICPENDRn
    u32 set_active[32];              // GICD_ISACTIVERn
    u32 clear_active[32];            // GICD_ICACTIVERn
    u32 interrupt_priority[255];     // GICD_IPRIORITYRn
    u32 reserved2;
    u32 interrupt_processor_targets[255]; // GICD_ITARGETSRn
    u32 reserved3;
    u32 interrupt_configuration[64]; // GICD_ICFGRn
    u32 reserved4[64];
    u32 non_secure_access_control[64]; // GICD_NSACRn
    u32 software_generated_interrupt;  // GICD_SGIR
    u32 reserved5[3];
    u32 software_generated_interrupt_clear_pending[4]; // GICD_CPENDSGIRn
    u32 software_generated_interrupt_set_pending[4];   // GICD_SPENDSGIRn
    u32 reserved6[40];
    u32 implementation_defined1[12];
};
static_assert(AssertSize<GIC::DistributorRegisters, 0x1000>());

AK_ENUM_BITWISE_OPERATORS(GIC::DistributorRegisters::ControlBits);

// 4.1.3 CPU interface register map
struct GIC::CPUInterfaceRegisters {
    enum class ControlBits : u32 {
        Enable = 1 << 0,
    };

    static constexpr size_t IDENTIFICATION_ARCHITECTURE_VERSION_OFFSET = 16;
    static constexpr u32 IDENTIFICATION_ARCHITECTURE_VERSION_MASK = (1 << 4) - 1;

    u32 control;                                    // GICC_CTLR
    u32 interrupt_priority_mask;                    // GICC_PMR, only the 8 bottom bits are valid
    u32 binary_point;                               // GICC_BPR
    u32 interrupt_acknowledge;                      // GICC_IAR
    u32 end_of_interrupt;                           // GICC_EOIR
    u32 running_priority;                           // GICC_RPR, only the 8 bottom bits are valid
    u32 highest_priority_pending_interrupt;         // GICC_HPPIR
    u32 aliased_binary_point;                       // GICC_ABPR
    u32 aliased_interrupt_acknowledge;              // GICC_AIAR
    u32 aliased_end_of_interrupt;                   // GICC_AEOIR
    u32 aliased_highest_priority_pending_interrupt; // GICC_AHPPIR
    u32 reserved0[5];
    u32 implementation_defined0[36];
    u32 active_priorities[4];            // GICC_APRn
    u32 non_secure_active_priorities[4]; // GICC_NSAPRn
    u32 reserved1[3];
    u32 identification; // GICC_IIDR
    u32 reserved2[960];
    u32 deactivate_interrupt; // GICC_DIR
};
static_assert(AssertSize<GIC::CPUInterfaceRegisters, 0x1004>());

UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<GIC>> GIC::try_to_initialize(DeviceTree::Device::Resource distributor_registers_resource, DeviceTree::Device::Resource cpu_interface_registers_resource)
{
    if (distributor_registers_resource.size < sizeof(DistributorRegisters))
        return EINVAL;

    if (cpu_interface_registers_resource.size < sizeof(CPUInterfaceRegisters))
        return EINVAL;

    auto distributor_registers = TRY(Memory::map_typed_writable<DistributorRegisters volatile>(distributor_registers_resource.paddr));
    auto cpu_interface_registers = TRY(Memory::map_typed_writable<CPUInterfaceRegisters volatile>(cpu_interface_registers_resource.paddr));

    auto gic = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) GIC(move(distributor_registers), move(cpu_interface_registers))));
    TRY(gic->initialize());

    return gic;
}

void GIC::enable(GenericInterruptHandler const& handler)
{
    // FIXME: Set the trigger mode in DistributorRegisters::interrupt_configuration (GICD_ICFGRn) to level-triggered or edge-triggered.

    auto interrupt_number = handler.interrupt_number();
    m_distributor_registers->interrupt_set_enable[interrupt_number / 32] = 1 << (interrupt_number % 32);
}

void GIC::disable(GenericInterruptHandler const& handler)
{
    auto interrupt_number = handler.interrupt_number();
    m_distributor_registers->interrupt_clear_enable[interrupt_number / 32] = 1 << (interrupt_number % 32);
}

void GIC::eoi(GenericInterruptHandler const& handler)
{
    auto interrupt_number = handler.interrupt_number();
    m_cpu_interface_registers->end_of_interrupt = interrupt_number;
}

Optional<size_t> GIC::pending_interrupt() const
{
    auto interrupt_number = m_cpu_interface_registers->interrupt_acknowledge;

    // 1023 means no pending interrupt.
    if (interrupt_number == 1023)
        return {};

    return interrupt_number;
}

UNMAP_AFTER_INIT GIC::GIC(Memory::TypedMapping<DistributorRegisters volatile> distributor_registers, Memory::TypedMapping<CPUInterfaceRegisters volatile> cpu_interface_registers)
    : m_distributor_registers(move(distributor_registers))
    , m_cpu_interface_registers(move(cpu_interface_registers))
{
}

UNMAP_AFTER_INIT ErrorOr<void> GIC::initialize()
{
    auto gic_architecture_version = (m_cpu_interface_registers->identification >> CPUInterfaceRegisters::IDENTIFICATION_ARCHITECTURE_VERSION_OFFSET) & CPUInterfaceRegisters::IDENTIFICATION_ARCHITECTURE_VERSION_MASK;
    if (gic_architecture_version != 2)
        return ENOTSUP; // We currently only support GICv2.

    // Disable forwarding of interrupts to the CPU interfaces during initialization.
    m_distributor_registers->control &= ~to_underlying(DistributorRegisters::ControlBits::Enable);

    auto it_lines_number = (m_distributor_registers->interrupt_controller_type >> DistributorRegisters::INTERRUPT_CONTROLLER_TYPE_IT_LINES_NUMBER_OFFSET) & DistributorRegisters::INTERRUPT_CONTROLLER_TYPE_IT_LINES_NUMBER_MASK;

    // 4.3.2 Interrupt Controller Type Register, GICD_TYPER:
    // "If ITLinesNumber=N, the maximum number of interrupts is 32(N+1)."
    // "The ITLinesNumber field only indicates the maximum number of SPIs that the GIC might support. This value determines the number of implemented interrupt registers [...]"
    u32 const max_number_of_interrupts = 32 * (it_lines_number + 1);

    // Disable all interrupts and mark them as non-pending.
    for (size_t i = 0; i < max_number_of_interrupts / 32; i++) {
        m_distributor_registers->interrupt_clear_enable[i] = 0xffff'ffff;
        m_distributor_registers->interrupt_clear_pending[i] = 0xffff'ffff;
        m_distributor_registers->clear_active[i] = 0xffff'ffff;
    }

    // Initialize the priority of all interrupts to 0 (the highest priority) and configure them to target all processors.
    for (size_t i = 0; i < max_number_of_interrupts / 4; i++) {
        m_distributor_registers->interrupt_priority[i] = 0;
        m_distributor_registers->interrupt_processor_targets[i] = static_cast<u32>(explode_byte(0xff));
    }

    // FIXME: We need to configure the CPU interface for each processor once we support SMP.

    // Set the interrupt priority threshold to the max value, so accept any interrupt with a priority below 0xff.
    m_cpu_interface_registers->interrupt_priority_mask = 0xff;

    // Enable the distributor and CPU interface.
    m_cpu_interface_registers->control |= to_underlying(CPUInterfaceRegisters::ControlBits::Enable);
    m_distributor_registers->control |= to_underlying(DistributorRegisters::ControlBits::Enable);

    return {};
}

static constinit Array const compatibles_array = {
    "arm,gic-400"sv,
    "arm,cortex-a15-gic"sv,
};

EARLY_DEVICETREE_DRIVER(GICDriver, compatibles_array);

// https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
ErrorOr<void> GICDriver::probe(DeviceTree::Device const& device, StringView) const
{
    auto distributor_registers_resource = TRY(device.get_resource(0));
    auto cpu_interface_registers_resource = TRY(device.get_resource(1));

    DeviceTree::DeviceRecipe<NonnullLockRefPtr<IRQController>> recipe {
        name(),
        device.node_name(),
        [distributor_registers_resource, cpu_interface_registers_resource] {
            return GIC::try_to_initialize(distributor_registers_resource, cpu_interface_registers_resource);
        },
    };

    InterruptManagement::add_recipe(move(recipe));

    return {};
}

}