Files
jsix/src/kernel/apic.h
Justin C. Miller 5e6769036c APIC timer calibration
Now the APIC timer is calibrated against the PIT, and the interval for
timer_enable takes a number of microseconds instead of raw ticks and a
divisor.
2018-09-16 18:56:01 -07:00

110 lines
3.2 KiB
C++

#pragma once
/// \file apic.h
/// Classes to control both local and I/O APICs.
#include <stdint.h>
enum class isr : uint8_t;
/// Base class for other APIC types
class apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
apic(uint32_t *base);
protected:
uint32_t *m_base;
};
/// Controller for processor-local APICs
class lapic :
public apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg spurious Vector of the spurious interrupt handler
lapic(uint32_t *base, isr spurious);
/// Enable interrupts for the LAPIC timer.
/// \arg vector Interrupt vector the timer should use
/// \arg interval The timer interval, in microseconds
/// \arg repeat If false, this timer is one-off, otherwise repeating
/// \returns The count of ticks the timer is set for
uint32_t enable_timer(isr vector, uint64_t interval, bool repeat = true);
/// Reset the timer countdown.
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
/// \returns The count of ticks that were remaining before reset
uint32_t reset_timer(uint32_t count);
/// Stop the timer.
/// \returns The count of ticks remaining before an interrupt was to happen
inline uint32_t stop_timer() { return reset_timer(0); }
/// Enable interrupts for the LAPIC LINT0 pin.
/// \arg num Local interrupt number (0 or 1)
/// \arg vector Interrupt vector LINT0 should use
/// \arg nmi Whether this interrupt is NMI delivery mode
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
void enable(); ///< Enable servicing of interrupts
void disable(); ///< Disable (temporarily) servicing of interrupts
/// Calibrate the timer speed against the PIT
void calibrate_timer();
private:
uint32_t enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat);
uint32_t m_ticks_per_us;
};
/// Controller for I/O APICs
class ioapic :
public apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
ioapic(uint32_t *base, uint32_t base_gsi);
uint32_t get_base_gsi() const { return m_base_gsi; }
uint32_t get_num_gsi() const { return m_num_gsi; }
/// Set a redirection entry.
/// TODO: pick CPU
/// \arg source Source interrupt number
/// \arg vector Interrupt vector that should be used
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
/// \arg masked Whether the iterrupt should be suppressed
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
/// \arg irq The IOAPIC-local irq number
/// \arg masked Whether to suppress this interrupt
void mask(uint8_t irq, bool masked);
/// Mask all interrupts on this IOAPIC.
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
/// Unmask all interrupts on this IOAPIC.
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
void dump_redirs() const;
private:
uint32_t m_base_gsi;
uint32_t m_num_gsi;
uint8_t m_id;
uint8_t m_version;
};