Kernel: Rewrite APIC and PIC more OOP friendly

x86_64 port is slowly coming together
This commit is contained in:
Bananymous
2023-01-23 20:13:57 +02:00
parent b50bb03ed4
commit ba53582f23
13 changed files with 575 additions and 572 deletions

365
kernel/kernel/APIC.cpp Normal file
View File

@@ -0,0 +1,365 @@
#include <BAN/ScopeGuard.h>
#include <kernel/APIC.h>
#include <kernel/CPUID.h>
#include <kernel/IDT.h>
#include <kernel/IO.h>
#include <kernel/MMU.h>
#include <kernel/Serial.h>
#include <string.h>
#define LAPIC_EIO_REG 0xB0
#define LAPIC_SIV_REG 0xF0
#define LAPIC_IS_REG 0x100
#define IOAPIC_MAX_REDIRS 0x01
#define IOAPIC_REDIRS 0x10
#define PIC1 0x20
#define PIC2 0xA0
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define ICW1_ICW4 0x01
#define ICW1_INIT 0x10
#define ICW4_8086 0x01
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
struct RSDPDescriptor
{
char signature[8];
uint8_t checksum;
char OEMID[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__ ((packed));
struct RSDPDescriptor20
{
RSDPDescriptor first_part;
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed));
struct ACPISDTHeader
{
char signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
char OEMID[6];
char OEM_table_id[8];
uint32_t OEM_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed));
struct RSDT
{
ACPISDTHeader header;
uint32_t sdt_pointer[0];
};
struct XSDT
{
ACPISDTHeader header;
uint64_t sdt_pointer[0];
};
struct MADT
{
ACPISDTHeader header;
uint32_t local_apic;
uint32_t flags;
} __attribute__((packed));
struct MADTEntry
{
uint8_t type;
uint8_t length;
union
{
struct
{
uint8_t acpi_processor_id;
uint8_t apic_id;
uint32_t flags;
} __attribute__((packed)) entry0;
struct
{
uint8_t ioapic_id;
uint8_t reserved;
uint32_t ioapic_address;
uint32_t gsi_base;
} __attribute__((packed)) entry1;
struct
{
uint8_t bus_source;
uint8_t irq_source;
uint32_t gsi;
uint16_t flags;
} __attribute__((packed)) entry2;
struct
{
uint16_t reserved;
uint64_t address;
} __attribute__((packed)) entry5;
};
} __attribute__((packed));
union RedirectionEntry
{
struct
{
uint64_t vector : 8;
uint64_t delivery_mode : 3;
uint64_t destination_mode : 1;
uint64_t delivery_status : 1;
uint64_t pin_polarity : 1;
uint64_t remote_irr : 1;
uint64_t trigger_mode : 1;
uint64_t mask : 1;
uint64_t reserved : 39;
uint64_t destination : 8;
};
struct
{
uint32_t lo_dword;
uint32_t hi_dword;
};
};
static bool IsRSDP(const RSDPDescriptor* rsdp)
{
if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0)
return false;
{
uint8_t checksum = 0;
for (uint32_t i = 0; i < sizeof(RSDPDescriptor); i++)
checksum += ((uint8_t*)rsdp)[i];
if (checksum != 0)
return false;
}
if (rsdp->revision == 2)
{
RSDPDescriptor20* rsdp20 = (RSDPDescriptor20*)rsdp;
uint8_t checksum = 0;
for (uint32_t i = 0; i < sizeof(RSDPDescriptor20); i++)
checksum += ((uint8_t*)rsdp20)[i];
if (checksum != 0)
return false;
}
return true;
}
static RSDPDescriptor* LocateRSDP()
{
// Look in main BIOS area below 1 MB
for (uintptr_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16)
if (IsRSDP((RSDPDescriptor*)addr))
return (RSDPDescriptor*)addr;
return nullptr;
}
static bool IsValidACPISDTHeader(ACPISDTHeader* header)
{
uint8_t sum = 0;
for (uint32_t i = 0; i < header->length; i++)
sum += ((uint8_t*)header)[i];
return sum == 0;
}
static MADT* LocateMADT(const RSDPDescriptor* rsdp)
{
uintptr_t root_addr = 0;
uint32_t entry_count = 0;
if (rsdp->revision == 2)
{
const XSDT* root = (const XSDT*)((RSDPDescriptor20*)rsdp)->xsdt_address;
MMU::Get().AllocatePage((uintptr_t)root);
entry_count = (root->header.length - sizeof(root->header)) / sizeof(*root->sdt_pointer);
root_addr = (uintptr_t)root;
dprintln("XSDT");
}
else
{
const RSDT* root = (const RSDT*)(uintptr_t)rsdp->rsdt_address;
MMU::Get().AllocatePage((uintptr_t)root);
entry_count = (root->header.length - sizeof(root->header)) / sizeof(*root->sdt_pointer);
root_addr = (uintptr_t)root;
dprintln("RSDT");
}
BAN::ScopeGuard guard([root_addr]() { MMU::Get().UnAllocatePage(root_addr); });
for (uint32_t i = 0; i < entry_count; i++)
{
ACPISDTHeader* header = nullptr;
if (rsdp->revision == 2)
header = (ACPISDTHeader*)((const XSDT*)root_addr)->sdt_pointer[i];
else
header = (ACPISDTHeader*)(uintptr_t)((const RSDT*)root_addr)->sdt_pointer[i];
if (memcmp(header->signature, "APIC", 4) == 0 && IsValidACPISDTHeader(header))
return (MADT*)header;
}
return nullptr;
}
APIC* APIC::Create()
{
uint32_t ecx, edx;
CPUID::GetFeatures(ecx, edx);
if (!(edx & CPUID::Features::EDX_APIC))
{
dprintln("Local APIC is not available");
return nullptr;
}
RSDPDescriptor* rsdp = LocateRSDP();
if (rsdp == nullptr)
{
dprintln("Could not locate RSDP");
return nullptr;
}
MADT* madt = LocateMADT(rsdp);
if (madt == nullptr)
{
dprintln("Could not find MADT in RSDP");
return nullptr;
}
MMU::Get().AllocatePage((uintptr_t)madt);
APIC* apic = new APIC;
apic->m_local_apic = madt->local_apic;
for (uint32_t i = 0x00; i <= 0xFF; i++)
apic->m_irq_overrides[i] = i;
uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT);
while (madt_entry_addr < (uintptr_t)madt + madt->header.length)
{
MADTEntry* entry = (MADTEntry*)madt_entry_addr;
switch (entry->type)
{
case 0:
Processor processor;
processor.processor_id = entry->entry0.acpi_processor_id;
processor.apic_id = entry->entry0.apic_id;
processor.flags = entry->entry0.flags & 0x03;
MUST(apic->m_processors.PushBack(processor));
break;
case 1:
IOAPIC ioapic;
ioapic.id = entry->entry1.ioapic_id;
ioapic.address = entry->entry1.ioapic_address;
ioapic.gsi_base = entry->entry1.gsi_base;
ioapic.max_redirs = 0;
MUST(apic->m_io_apics.PushBack(ioapic));
break;
case 2:
apic->m_irq_overrides[entry->entry2.irq_source] = entry->entry2.gsi;
break;
case 5:
apic->m_local_apic = entry->entry5.address;
break;
default:
dprintln("Unhandled madt entry, type {}", entry->type);
break;
}
madt_entry_addr += entry->length;
}
MMU::Get().UnAllocatePage((uintptr_t)madt);
if (apic->m_local_apic == 0 || apic->m_io_apics.Empty())
{
dprintln("MADT did not provide necessary information");
delete apic;
return nullptr;
}
MMU::Get().AllocatePage(apic->m_local_apic);
for (auto& io_apic : apic->m_io_apics)
{
MMU::Get().AllocatePage(io_apic.address);
io_apic.max_redirs = io_apic.Read(IOAPIC_MAX_REDIRS);
}
// Mask all interrupts
uint32_t sivr = apic->ReadFromLocalAPIC(LAPIC_SIV_REG);
apic->WriteToLocalAPIC(LAPIC_SIV_REG, sivr | 0x1FF);
return apic;
}
uint32_t APIC::ReadFromLocalAPIC(ptrdiff_t offset)
{
return *(uint32_t*)(m_local_apic + offset);
}
void APIC::WriteToLocalAPIC(ptrdiff_t offset, uint32_t data)
{
*(uint32_t*)(m_local_apic + offset) = data;
}
uint32_t APIC::IOAPIC::Read(uint8_t offset)
{
volatile uint32_t* ioapic = (volatile uint32_t*)address;
ioapic[0] = offset;
return ioapic[4];
}
void APIC::IOAPIC::Write(uint8_t offset, uint32_t data)
{
volatile uint32_t* ioapic = (volatile uint32_t*)address;
ioapic[0] = offset;
ioapic[4] = data;
}
void APIC::EOI(uint8_t)
{
WriteToLocalAPIC(LAPIC_EIO_REG, 0);
}
void APIC::EnableIrq(uint8_t irq)
{
uint32_t gsi = m_irq_overrides[irq];
IOAPIC* ioapic = nullptr;
for (IOAPIC& io : m_io_apics)
{
if (io.gsi_base <= gsi && gsi <= io.gsi_base + io.max_redirs)
{
ioapic = &io;
break;
}
}
ASSERT(ioapic);
RedirectionEntry redir;
redir.lo_dword = ioapic->Read(IOAPIC_REDIRS + gsi * 2);
redir.hi_dword = ioapic->Read(IOAPIC_REDIRS + gsi * 2 + 1);
redir.vector = IRQ_VECTOR_BASE + irq;
redir.mask = 0;
redir.destination = m_processors.Front().apic_id;
ioapic->Write(IOAPIC_REDIRS + gsi * 2, redir.lo_dword);
ioapic->Write(IOAPIC_REDIRS + gsi * 2 + 1, redir.hi_dword);
}
void APIC::GetISR(uint32_t out[8])
{
for (uint32_t i = 0; i < 8; i++)
out[i] = ReadFromLocalAPIC(LAPIC_IS_REG + i * 0x10);
}

View File

@@ -1,7 +1,7 @@
#include <BAN/Queue.h>
#include <kernel/APIC.h>
#include <kernel/IDT.h>
#include <kernel/Input.h>
#include <kernel/InterruptController.h>
#include <kernel/IO.h>
#include <kernel/kprint.h>
#include <kernel/PIT.h>
@@ -540,7 +540,7 @@ namespace Input
{
// Register callback and IRQ
IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler);
APIC::EnableIRQ(KEYBOARD_IRQ);
InterruptController::Get().EnableIrq(KEYBOARD_IRQ);
i8042_controller_command(I8042_ENABLE_FIRST_PORT);
MUST(s_command_queue.Push({
@@ -570,7 +570,7 @@ namespace Input
{
// Register callback and IRQ
IDT::register_irq_handler(MOUSE_IRQ, mouse_irq_handler);
APIC::EnableIRQ(MOUSE_IRQ);
InterruptController::Get().EnableIrq(MOUSE_IRQ);
i8042_controller_command(I8042_ENABLE_SECOND_PORT);
MUST(s_command_queue.Push({

View File

@@ -0,0 +1,27 @@
#include <BAN/Errors.h>
#include <kernel/InterruptController.h>
#include <kernel/APIC.h>
#include <kernel/PIC.h>
static InterruptController* s_instance = nullptr;
InterruptController& InterruptController::Get()
{
ASSERT(s_instance);
return *s_instance;
}
void InterruptController::Initialize(bool force_pic)
{
ASSERT(s_instance == nullptr);
PIC::MaskAll();
PIC::Remap();
if (!force_pic)
s_instance = APIC::Create();
if (s_instance)
return;
dprintln("Using PIC instead of APIC");
s_instance = PIC::Create();
}

View File

@@ -2,6 +2,8 @@
#include <kernel/IDT.h>
#include <kernel/IO.h>
#include <string.h>
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
@@ -23,92 +25,86 @@
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
namespace PIC
PIC* PIC::Create()
{
MaskAll();
Remap();
return new PIC;
}
void Remap()
void PIC::Remap()
{
uint8_t a1 = IO::inb(PIC1_DATA);
uint8_t a2 = IO::inb(PIC2_DATA);
// Start the initialization sequence (in cascade mode)
IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
IO::io_wait();
IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
IO::io_wait();
// ICW2
IO::outb(PIC1_DATA, IRQ_VECTOR_BASE);
IO::io_wait();
IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08);
IO::io_wait();
// ICW3
IO::outb(PIC1_DATA, 4);
IO::io_wait();
IO::outb(PIC2_DATA, 2);
IO::io_wait();
// ICW4
IO::outb(PIC1_DATA, ICW4_8086);
IO::io_wait();
IO::outb(PIC2_DATA, ICW4_8086);
IO::io_wait();
// Restore original masks
IO::outb(PIC1_DATA, a1);
IO::outb(PIC2_DATA, a2);
}
void PIC::MaskAll()
{
IO::outb(PIC1_DATA, 0xFF);
IO::outb(PIC2_DATA, 0xFF);
}
void PIC::EOI(uint8_t irq)
{
if (irq >= 8)
IO::outb(PIC2_COMMAND, PIC_EOI);
IO::outb(PIC1_COMMAND, PIC_EOI);
}
void PIC::EnableIrq(uint8_t irq)
{
uint16_t port;
uint8_t value;
if(irq < 8)
{
uint8_t a1 = IO::inb(PIC1_DATA);
uint8_t a2 = IO::inb(PIC2_DATA);
// Start the initialization sequence (in cascade mode)
IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
IO::io_wait();
IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
IO::io_wait();
// ICW2
IO::outb(PIC1_DATA, IRQ_VECTOR_BASE);
IO::io_wait();
IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08);
IO::io_wait();
// ICW3
IO::outb(PIC1_DATA, 4);
IO::io_wait();
IO::outb(PIC2_DATA, 2);
IO::io_wait();
// ICW4
IO::outb(PIC1_DATA, ICW4_8086);
IO::io_wait();
IO::outb(PIC2_DATA, ICW4_8086);
IO::io_wait();
// Restore original masks
IO::outb(PIC1_DATA, a1);
IO::outb(PIC2_DATA, a2);
port = PIC1_DATA;
}
void MaskAll()
else
{
IO::outb(PIC1_DATA, 0xff);
IO::outb(PIC2_DATA, 0xff);
port = PIC2_DATA;
irq -= 8;
}
value = IO::inb(port) & ~(1 << irq);
IO::outb(port, value);
}
void EOI(uint8_t irq)
{
if (irq >= 8)
IO::outb(PIC2_COMMAND, PIC_EOI);
IO::outb(PIC1_COMMAND, PIC_EOI);
}
void Mask(uint8_t irq) {
uint16_t port;
uint8_t value;
if(irq < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
irq -= 8;
}
value = IO::inb(port) | (1 << irq);
IO::outb(port, value);
}
void Unmask(uint8_t irq) {
uint16_t port;
uint8_t value;
if(irq < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
irq -= 8;
}
value = IO::inb(port) & ~(1 << irq);
IO::outb(port, value);
}
uint16_t GetISR()
{
IO::outb(PIC1_COMMAND, 0x0b);
IO::outb(PIC2_COMMAND, 0x0b);
uint8_t isr0 = IO::inb(PIC1_COMMAND);
uint8_t isr1 = IO::inb(PIC2_COMMAND);
return (isr1 << 8) | isr0;
}
void PIC::GetISR(uint32_t out[8])
{
memset(out, 0, 8 * sizeof(uint32_t));
IO::outb(PIC1_COMMAND, 0x0b);
IO::outb(PIC2_COMMAND, 0x0b);
uint16_t isr0 = IO::inb(PIC1_COMMAND);
uint16_t isr1 = IO::inb(PIC2_COMMAND);
uintptr_t addr = (uintptr_t)out + IRQ_VECTOR_BASE / 8;
*(uint16_t*)addr = (isr1 << 8) | isr0;
}

View File

@@ -1,7 +1,7 @@
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/IO.h>
#include <kernel/kprint.h>
#include <kernel/APIC.h>
#define IRQ_TIMER 0
@@ -47,7 +47,7 @@ namespace PIT
IDT::register_irq_handler(IRQ_TIMER, clock_handle);
APIC::EnableIRQ(IRQ_TIMER);
InterruptController::Get().EnableIrq(IRQ_TIMER);
}
}

View File

@@ -1,15 +1,12 @@
#include <BAN/Memory.h>
#include <kernel/APIC.h>
#include <kernel/GDT.h>
#include <kernel/IDT.h>
#include <kernel/Input.h>
#include <kernel/InterruptController.h>
#include <kernel/kmalloc.h>
#include <kernel/kprint.h>
#include <kernel/MMU.h>
#include <kernel/multiboot.h>
#include <kernel/PIC.h>
#include <kernel/PIT.h>
#include <kernel/RTC.h>
#include <kernel/Serial.h>
#include <kernel/Shell.h>
#include <kernel/TTY.h>
@@ -81,8 +78,9 @@ extern "C" void kernel_main()
dprintln("VESA initialized");
TTY* tty1 = new TTY(terminal_driver);
APIC::Initialize(cmdline.force_pic);
dprintln("APIC initialized");
InterruptController::Initialize(cmdline.force_pic);
dprintln("Interrupt controller initialized");
PIT::initialize();
dprintln("PIT initialized");
if (!Input::initialize())