Files
jsix_import/src/kernel/apic.h
Justin C. Miller f0025dbc47 [kernel] Schedule threads on other CPUs
Now that the other CPUs have been brought up, add support for scheduling
tasks on them. The scheduler now maintains separate ready/blocked lists
per CPU, and CPUs will attempt to balance load via periodic work
stealing.

Other changes as a result of this:
- The device manager no longer creates a local APIC object, but instead
  just gathers relevant info from the APCI tables. Each CPU creates its
  own local APIC object. This also spurred the APIC timer calibration to
  become a static value, as all APICs are assumed to be symmetrical.
- Fixed a bug where the scheduler was popping the current task off of
  its ready list, however the current task is never on the ready list
  (except the idle task was first set up as both current and ready).
  This was causing the lists to get into bad states. Now a task can only
  ever be current or in a ready or blocked list.
- Got rid of the unused static process::s_processes list of all
  processes, instead of trying to synchronize it via locks.
- Added spinlocks for synchronization to the scheduler and logger
  objects.
2021-02-15 12:56:22 -08:00

147 lines
4.2 KiB
C++

#pragma once
/// \file apic.h
/// Classes to control both local and I/O APICs.
#include <stdint.h>
#include "kutil/enum_bitfields.h"
enum class isr : uint8_t;
/// Base class for other APIC types
class apic
{
public:
/// Constructor
/// \arg base Physical base address of the APIC's MMIO registers
apic(uintptr_t base);
protected:
uint32_t *m_base;
};
enum class ipi : uint32_t
{
// Delivery modes
fixed = 0x0000,
smi = 0x0200,
nmi = 0x0400,
init = 0x0500,
startup = 0x0600,
// Flags
deassert = 0x0000,
assert = 0x4000,
edge = 0x0000, ///< edge-triggered
level = 0x8000, ///< level-triggered
};
IS_BITFIELD(ipi);
/// Controller for processor-local APICs
class lapic :
public apic
{
public:
/// Constructor
/// \arg base Physicl base address of the APIC's MMIO registers
lapic(uintptr_t base);
/// Get the local APIC's ID
uint8_t get_id();
/// Send an inter-processor interrupt.
/// \arg mode The sending mode
/// \arg vector The interrupt vector
/// \arg dest The APIC ID of the destination
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
/// Send an inter-processor broadcast interrupt to all other CPUs
/// \arg mode The sending mode
/// \arg self If true, include this CPU in the broadcast
/// \arg vector The interrupt vector
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
/// Wait for an IPI to finish sending. This is done automatically
/// before sending another IPI with send_ipi().
void ipi_wait();
/// Enable interrupts for the LAPIC timer.
/// \arg vector Interrupt vector the timer should use
/// \arg repeat If false, this timer is one-off, otherwise repeating
void enable_timer(isr vector, bool repeat = true);
/// Reset the timer countdown.
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
/// \returns The interval in us that was remaining before reset
uint32_t reset_timer(uint64_t interval);
/// Stop the timer.
/// \returns The interval in us 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 clock
void calibrate_timer();
private:
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
void set_divisor(uint8_t divisor);
void set_repeat(bool repeat);
uint32_t m_divisor;
static uint64_t s_ticks_per_us;
};
/// Controller for I/O APICs
class ioapic :
public apic
{
public:
/// Constructor
/// \arg base Physical base address of the APIC's MMIO registers
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
ioapic(uintptr_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;
};