// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "lapic.h"
#include "platform.h"

extern void serial_write(const char *str);

// LAPIC is at physical 0xFEE00000. Access via HHDM.
static volatile uint32_t *lapic_base = 0;

// LAPIC register offsets (byte offsets, divided by 4 for uint32_t* indexing)
#define LAPIC_ID        (0x020 / 4)
#define LAPIC_EOI       (0x0B0 / 4)
#define LAPIC_SVR       (0x0F0 / 4)
#define LAPIC_ICR_LOW   (0x300 / 4)
#define LAPIC_ICR_HIGH  (0x310 / 4)

void lapic_enable(void) {
    if (!lapic_base) return;
    // Enable the LAPIC by setting the Spurious Interrupt Vector Register
    // Bit 8 = APIC Software Enable, vector = 0xFF (spurious)
    lapic_base[LAPIC_SVR] = 0x1FF;
}

void lapic_init(void) {
    extern uint64_t hhdm_offset;
    lapic_base = (volatile uint32_t *)(hhdm_offset + 0xFEE00000ULL);
    
    lapic_enable();
    serial_write("[LAPIC] Initialized at HHDM + 0xFEE00000\n");
}

void lapic_eoi(void) {
    if (lapic_base) {
        lapic_base[LAPIC_EOI] = 0;
    }
}

void lapic_send_ipi_all(void) {
    if (!lapic_base) return;
    
    // Send IPI to all excluding self
    // ICR format: 
    //   bits 7:0   = vector (IPI_SCHED_VECTOR = 0x41)
    //   bits 10:8  = delivery mode (000 = Fixed)
    //   bit 11     = destination mode (0 = Physical)
    //   bit 14     = level (1 = Assert)
    //   bits 19:18 = destination shorthand (11 = All Excluding Self)
    uint32_t icr_low = IPI_SCHED_VECTOR | (0b11 << 18) | (1 << 14);
    
    // Writing ICR_LOW triggers the IPI send
    lapic_base[LAPIC_ICR_LOW] = icr_low;
}
