/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #ifndef __FENNIX_KERNEL_APIC_H__ #define __FENNIX_KERNEL_APIC_H__ #include #include #include namespace APIC { enum APICRegisters { // source from: https://github.com/pdoane/osdev/blob/master/intr/local_apic.c APIC_ID = 0x20, // Local APIC ID APIC_VER = 0x30, // Local APIC Version APIC_TPR = 0x80, // Task Priority APIC_APR = 0x90, // Arbitration Priority APIC_PPR = 0xA0, // Processor Priority APIC_EOI = 0xB0, // EOI APIC_RRD = 0xC0, // Remote Read APIC_LDR = 0xD0, // Logical Destination APIC_DFR = 0xE0, // Destination Format APIC_SVR = 0xF0, // Spurious Interrupt Vector APIC_ISR = 0x100, // In-Service (8 registers) APIC_TMR = 0x180, // Trigger Mode (8 registers) APIC_IRR = 0x200, // Interrupt Request (8 registers) APIC_ESR = 0x280, // Error Status APIC_ICRLO = 0x300, // Interrupt Command APIC_ICRHI = 0x310, // Interrupt Command [63:32] APIC_TIMER = 0x320, // LVT Timer APIC_THERMAL = 0x330, // LVT Thermal Sensor APIC_PERF = 0x340, // LVT Performance Counter APIC_LINT0 = 0x350, // LVT LINT0 APIC_LINT1 = 0x360, // LVT LINT1 APIC_ERROR = 0x370, // LVT Error APIC_TICR = 0x380, // Initial Count (for Timer) APIC_TCCR = 0x390, // Current Count (for Timer) APIC_TDCR = 0x3E0, // Divide Configuration (for Timer) }; enum IOAPICRegisters { GetIOAPICVersion = 0x1 }; enum IOAPICFlags { ActiveHighLow = 2, EdgeLevel = 8 }; enum APICDeliveryMode { Fixed = 0b000, LowestPriority = 0b001, /* Reserved */ SMI = 0b010, APIC_DELIVERY_MODE_RESERVED0 = 0b011, /* Reserved */ NMI = 0b100, INIT = 0b101, Startup = 0b110, ExtINT = 0b111 /* Reserved */ }; enum APICDestinationMode { Physical = 0b0, Logical = 0b1 }; enum APICDeliveryStatus { Idle = 0b0, SendPending = 0b1 }; enum APICLevel { DeAssert = 0b0, Assert = 0b1 }; enum APICTriggerMode { Edge = 0b0, Level = 0b1 }; enum APICDestinationShorthand { NoShorthand = 0b00, Self = 0b01, AllIncludingSelf = 0b10, AllExcludingSelf = 0b11 }; enum LVTTimerDivide { DivideBy2 = 0b000, DivideBy4 = 0b001, DivideBy8 = 0b010, DivideBy16 = 0b011, DivideBy32 = 0b100, DivideBy64 = 0b101, DivideBy128 = 0b110, DivideBy1 = 0b111 }; enum LVTTimerMask { Unmasked = 0b0, Masked = 0b1 }; enum LVTTimerMode { OneShot = 0b00, Periodic = 0b01, TSCDeadline = 0b10 }; typedef union { struct { /** @brief Interrupt Vector */ uint64_t Vector : 8; /** @brief Reserved */ uint64_t Reserved0 : 4; /** * @brief Delivery Status * * 0: Idle * 1: Send Pending */ uint64_t DeliveryStatus : 1; /** @brief Reserved */ uint64_t Reserved1 : 3; /** * @brief Mask * * 0: Not masked * 1: Masked */ uint64_t Mask : 1; /** @brief Timer Mode * * 0: One-shot * 1: Periodic * 2: TSC-Deadline */ uint64_t TimerMode : 1; /** @brief Reserved */ uint64_t Reserved2 : 14; }; uint64_t raw; } __packed LVTTimer; typedef union { struct { /** @brief Spurious Vector */ uint64_t Vector : 8; /** @brief Enable or disable APIC software */ uint64_t Software : 1; /** @brief Focus Processor Checking */ uint64_t FocusProcessorChecking : 1; /** @brief Reserved */ uint64_t Reserved : 2; /** @brief Disable EOI Broadcast */ uint64_t DisableEOIBroadcast : 1; /** @brief Reserved */ uint64_t Reserved1 : 19; }; uint64_t raw; } __packed Spurious; typedef union { struct { /** @brief Interrupt Vector */ uint64_t Vector : 8; /** @brief Delivery Mode */ uint64_t DeliveryMode : 3; /** @brief Destination Mode * * 0: Physical * 1: Logical */ uint64_t DestinationMode : 1; /** @brief Delivery Status * * @note Reserved when in x2APIC mode */ uint64_t DeliveryStatus : 1; /** @brief Reserved */ uint64_t Reserved0 : 1; /** @brief Level * * 0: Deassert * 1: Assert */ uint64_t Level : 1; /** @brief Trigger Mode * * 0: Edge * 1: Level */ uint64_t TriggerMode : 1; /** @brief Reserved */ uint64_t Reserved1 : 2; /** @brief Destination Shorthand * * 0: No shorthand * 1: Self * 2: All including self * 3: All excluding self */ uint64_t DestinationShorthand : 2; /** @brief Reserved */ uint64_t Reserved2 : 12; }; uint64_t raw; } __packed InterruptCommandRegisterLow; typedef union { struct { /** @brief Reserved */ uint64_t Reserved0 : 24; /** @brief Destination */ uint64_t Destination : 8; }; uint64_t raw; } __packed InterruptCommandRegisterHigh; typedef union { struct { /** @brief Interrupt Vector */ uint64_t Vector : 8; /** @brief Delivery Mode */ uint64_t DeliveryMode : 3; /** @brief Destination Mode * * 0: Physical * 1: Logical */ uint64_t DestinationMode : 1; /** @brief Delivery Status */ uint64_t DeliveryStatus : 1; /** @brief Interrupt Input Pin Polarity * * 0: Active High * 1: Active Low */ uint64_t Polarity : 1; /** @brief Remote IRR */ uint64_t RemoteIRR : 1; /** @brief Trigger Mode * * 0: Edge * 1: Level */ uint64_t TriggerMode : 1; /** @brief Mask */ uint64_t Mask : 1; /** @brief Reserved */ uint64_t Reserved0 : 15; /** @brief Reserved */ uint64_t Reserved1 : 24; /** @brief Destination */ uint64_t DestinationID : 8; }; struct { uint64_t Low; uint64_t High; } split; uint64_t raw; } __packed IOAPICRedirectEntry; typedef union { struct { uint64_t Version : 8; uint64_t Reserved : 8; uint64_t MaximumRedirectionEntry : 8; uint64_t Reserved2 : 8; }; uint64_t raw; } __packed IOAPICVersion; class APIC { private: bool x2APICSupported = false; uint64_t APICBaseAddress = 0; public: decltype(x2APICSupported) &x2APIC = x2APICSupported; uint32_t Read(uint32_t Register); void Write(uint32_t Register, uint32_t Value); void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value); uint32_t IORead(uint64_t Base, uint32_t Register); void EOI(); void RedirectIRQs(int CPU = 0); void WaitForIPI(); void IPI(uint8_t CPU, InterruptCommandRegisterLow icr); void SendInitIPI(uint8_t CPU); void SendStartupIPI(uint8_t CPU, uint64_t StartupAddress); uint32_t IOGetMaxRedirect(uint32_t APICID); void RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status); void RedirectIRQ(int CPU, uint16_t IRQ, int Status); APIC(int Core); ~APIC(); }; class Timer : public Interrupts::Handler { private: APIC *lapic; uint64_t Ticks = 0; void OnInterruptReceived(CPU::TrapFrame *Frame); public: uint64_t GetTicks() { return Ticks; } void OneShot(uint32_t Vector, uint64_t Miliseconds); Timer(APIC *apic); ~Timer(); }; } #endif // !__FENNIX_KERNEL_APIC_H__