Skip to content

Commit

Permalink
x86: More APIC handling
Browse files Browse the repository at this point in the history
  • Loading branch information
marv7000 committed Sep 17, 2024
1 parent 1a0997a commit da21bf8
Show file tree
Hide file tree
Showing 19 changed files with 329 additions and 54 deletions.
3 changes: 3 additions & 0 deletions include/menix/drv/acpi/hpet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void hpet_init();
63 changes: 63 additions & 0 deletions include/menix/drv/acpi/madt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include <menix/common.h>
#include <menix/util/list.h>

// MADT entry types
typedef struct
{
u8 type;
u8 length;
u8 acpi_id;
u8 lapic_id;
u32 flags;
} ATTR(packed) MadtLApic;

typedef struct
{
u8 type;
u8 length;
u8 ioapic_id;
u8 reserved;
u32 ioapic_addr;
u32 gsi_base;
} ATTR(packed) MadtIoApic;

typedef struct
{
u8 type;
u8 length;
u8 bus_source;
u8 irq_source;
u32 gsi;
u16 flags;
} ATTR(packed) MadtIso;

typedef struct
{
u8 type;
u8 length;
u8 acpi_id;
u16 flags;
u8 lint;
} ATTR(packed) MadtNmi;

typedef struct
{
u8 type;
u8 length;
u16 reserved;
u64 lapic_addr;
} ATTR(packed) MadtLApicAddr;

typedef List(MadtLApic*) MadtLApicList;
typedef List(MadtIoApic*) MadtIoApicList;
typedef List(MadtIso*) MadtIsoList;
typedef List(MadtNmi*) MadtNmiList;

extern MadtLApicList madt_lapic_list;
extern MadtIoApicList madt_ioapic_list;
extern MadtIsoList madt_iso_list;
extern MadtNmiList madt_nmi_list;

void madt_init();
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <menix/common.h>

// Do PCI configuration using ACPI "MCFG". This is the preferred way.
void pci_init_acpi();
void mcfg_init();

// Reads 8 bits from a PCI device using the ACPI MCFG table info.
u8 mcfg_read8(u16 seg, u8 bus, u8 slot, u8 func, u16 offset);
Expand Down
2 changes: 1 addition & 1 deletion include/menix/drv/acpi/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ typedef struct
AcpiDescHeader header;
u32 lapic_addr;
u32 flags;

u8 entries[];
} ATTR(packed) AcpiMadt;

typedef struct
Expand Down
16 changes: 8 additions & 8 deletions include/menix/io/mmio.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ typedef volatile u64 mmio64;

// Macros for reading and writing data from and to memory mapped addresses.

#define read8(addr) (*((mmio8*)(addr)))
#define write8(addr, value) (*((mmio8*)(addr)) = (u8)(value))
#define read16(addr) (*((mmio16*)(addr)))
#define write16(addr, value) (*((mmio16*)(addr)) = (u16)(value))
#define read32(addr) (*((mmio32*)(addr)))
#define write32(addr, value) (*((mmio32*)(addr)) = (u32)(value))
#define mmio_read8(addr) (*((mmio8*)(addr)))
#define mmio_write8(addr, value) (*((mmio8*)(addr)) = (u8)(value))
#define mmio_read16(addr) (*((mmio16*)(addr)))
#define mmio_write16(addr, value) (*((mmio16*)(addr)) = (u16)(value))
#define mmio_read32(addr) (*((mmio32*)(addr)))
#define mmio_write32(addr, value) (*((mmio32*)(addr)) = (u32)(value))
#if CONFIG_bits >= 64
#define read64(addr) (*((mmio64*)(addr)))
#define write64(addr, value) (*((mmio64*)(addr)) = (u64)(value))
#define mmio_read64(addr) (*((mmio64*)(addr)))
#define mmio_write64(addr, value) (*((mmio64*)(addr)) = (u64)(value))
#endif
3 changes: 0 additions & 3 deletions include/menix/system/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,3 @@ void arch_dump_registers(CpuRegisters* regs);

// Gets processor metadata.
Cpu* arch_current_cpu();

// Jump to user mode and set the instruction pointer to `ip`.
extern void arch_return_to_user(VirtAddr ip);
4 changes: 2 additions & 2 deletions kernel/arch/x86/include/apic.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void apic_send_ipi(u32 id, u32 flags);
void apic_redirect_irq(u32 irq, u8 interrupt);

// Initializes the LAPIC
void lapic_init();
void lapic_init(usize id);

// Reads data from a LAPIC register.
u32 lapic_read(u32 register);
Expand All @@ -29,6 +29,6 @@ u32 lapic_read(u32 register);
void lapic_write(u32 register, u32 value);

// Returns the ID of the processor-local APIC.
u8 lapic_get_id();
usize lapic_get_id();

void timer_handler(CpuRegisters* regs);
180 changes: 172 additions & 8 deletions kernel/arch/x86/system/apic.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// x86 advanced programmable interrupt controller

#include <menix/common.h>
#include <menix/drv/acpi/madt.h>
#include <menix/memory/pm.h>
#include <menix/system/arch.h>
#include <menix/thread/scheduler.h>
Expand All @@ -27,23 +28,185 @@ void apic_init()
arch_x86_write8(PIC2_DATA_PORT, 0x02); // ICW3: Connect to master PIC at IRQ2.
arch_x86_write8(PIC2_DATA_PORT, 0x01); // ICW4: Set the PIC to operate in 8086/88 mode.
arch_x86_write8(PIC2_DATA_PORT, 0xFF); // Mask all interrupts.
}

void apic_send_eoi()
{
// TODO
apic_redirect_irq(0, 48);
}

static u32 ioapic_read(PhysAddr ioapic_address, usize reg)
{
write16(pm_get_phys_base() + ioapic_address, reg & 0xFF);
return read16(pm_get_phys_base() + ioapic_address + 16);
mmio_write16(pm_get_phys_base() + ioapic_address, reg & 0xFF);
return mmio_read16(pm_get_phys_base() + ioapic_address + 16);
}

static void ioapic_write(PhysAddr ioapic_address, usize reg, u32 data)
{
write16(pm_get_phys_base() + ioapic_address, reg & 0xFF);
write16(pm_get_phys_base() + ioapic_address + 16, data);
mmio_write16(pm_get_phys_base() + ioapic_address, reg & 0xFF);
mmio_write16(pm_get_phys_base() + ioapic_address + 16, data);
}

static MadtIoApic* get_ioapic_by_gsi(i32 gsi)
{
for (usize i = 0; i < madt_ioapic_list.length; i++)
{
MadtIoApic* ioapic = madt_ioapic_list.items[i];
if (ioapic->gsi_base <= gsi &&
ioapic->gsi_base + ((ioapic_read(ioapic->ioapic_addr, 1) & 0xFF0000) >> 16) > gsi)
return ioapic;
}

return NULL;
}

static void ioapic_redirect_gsi(u32 gsi, u8 vec, u16 flags)
{
// Get I/O APIC address of the GSI
usize io_apic = get_ioapic_by_gsi(gsi)->ioapic_addr;

u32 low_index = 0x10 + (gsi - get_ioapic_by_gsi(gsi)->gsi_base) * 2;
u32 high_index = low_index + 1;

u32 high = ioapic_read(io_apic, high_index);

// Set APIC ID
high &= ~0xFF000000;
high |= ioapic_read(io_apic, 0) << 24;
ioapic_write(io_apic, high_index, high);

u32 low = ioapic_read(io_apic, low_index);

low &= ~(1 << 16);
low &= ~(1 << 11);
low &= ~0x700;
low &= ~0xFF;
low |= vec;

if (flags & 2)
low |= 1 << 13;

if (flags & 8)
low |= 1 << 15;

ioapic_write(io_apic, low_index, low);
}

static inline u32 reg_to_x2apic(u32 reg)
{
return ((reg == 0x310) ? 0x30 : (reg >> 4)) + 0x800;
}

void apic_redirect_irq(u32 irq, u8 interrupt)
{
for (u32 i = 0; i < madt_iso_list.length; i++)
{
if (madt_iso_list.items[i]->irq_source == irq)
{
ioapic_redirect_gsi(madt_iso_list.items[i]->gsi, interrupt, madt_iso_list.items[i]->flags);
return;
}
}
ioapic_redirect_gsi(irq, interrupt, 0);
}

u32 lapic_read(u32 reg)
{
if (has_x2apic)
return asm_rdmsr(reg_to_x2apic(reg));

return mmio_read16(pm_get_phys_base() + lapic_addr + reg);
}

void lapic_write(u32 reg, u32 value)
{
if (has_x2apic)
asm_wrmsr(reg_to_x2apic(reg), value);
else
mmio_write16(pm_get_phys_base() + lapic_addr + reg, value);
}

static void lapic_set_nmi(u8 vec, u8 current_processor_id, u8 processor_id, u16 flags, u8 lint)
{
// A value of 0xFF means all the processors
if (processor_id != 0xFF)
{
if (current_processor_id != processor_id)
return;
}

// Set to raise in vector number "vec" and set NMI flag
u32 nmi = 0x400 | vec;

// Set to active low if needed
if (flags & 2)
nmi |= 1 << 13;

// Set to level triggered if needed
if (flags & 8)
nmi |= 1 << 15;

// Use the proper LINT register
if (lint == 0)
lapic_write(0x350, nmi);
else if (lint == 1)
lapic_write(0x360, nmi);
}

void lapic_init(usize cpu_id)
{
u64 apic_msr = asm_rdmsr(0x1B);
// Set APIC enable flag
apic_msr |= 1 << 11;
u32 a = 0, b = 0, c = 0, d = 0;
asm_cpuid(1, 0, a, b, c, d);
if (c & CPUID_1C_X2APIC)
{
has_x2apic = true;
// Set X2APIC flag
apic_msr |= 1 << 10;
}

asm_wrmsr(0x1B, apic_msr);

// Initialize local APIC
lapic_write(0x80, 0);
lapic_write(0xF0, lapic_read(0xF0) | 0x100);
if (!has_x2apic)
{
lapic_write(0xE0, 0xF0000000);
lapic_write(0xD0, lapic_read(0x20));
}

// Set NMIs according to the MADT
for (int i = 0; i < madt_nmi_list.length; i++)
{
MadtNmi* nmi = madt_nmi_list.items[i];
lapic_set_nmi(2, cpu_id, nmi->acpi_id, nmi->flags, nmi->lint);
}

// Set up APIC timer

// Tell APIC timer to divide by 16
lapic_write(0x3E0, 3);
// Set timer init counter to -1
lapic_write(0x380, 0xFFFFFFFF);

timer_sleep(10);

// Stop the APIC timer
lapic_write(0x320, 0x10000);

// How much the APIC timer ticked in 10ms
tick_in_10ms = 0xFFFFFFFF - lapic_read(0x390);

// Start timer as periodic on IRQ 0
lapic_write(0x320, 32 | 0x20000);
// With divider 16
lapic_write(0x3E0, 3);
lapic_write(0x380, tick_in_10ms / 10);
}

void apic_send_eoi()
{
lapic_write(0xB0, 0);
}

void timer_handler(CpuRegisters* regs)
Expand All @@ -53,5 +216,6 @@ void timer_handler(CpuRegisters* regs)
// TODO
scheduler_reschedule(regs);

apic_send_eoi();
asm_interrupt_enable();
}
11 changes: 7 additions & 4 deletions kernel/arch/x86/system/arch.c
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// x86 platform initialization

#include <menix/drv/acpi/acpi.h>
#include <menix/drv/pci/pci.h>
#include <menix/drv/pci/pci_acpi.h>
#include <menix/fs/vfs.h>
#include <menix/memory/alloc.h>
#include <menix/system/arch.h>
#include <menix/thread/spin.h>
#include <menix/util/log.h>

#include <apic.h>
#include <gdt.h>
#include <idt.h>
#include <interrupts.h>
#include <serial.h>

#include "menix/drv/acpi/acpi.h"

static BootInfo* boot_info;
static SpinLock cpu_lock = spin_new();

Expand Down Expand Up @@ -135,14 +134,18 @@ void arch_init_cpu(Cpu* cpu, Cpu* boot)
void arch_early_init(BootInfo* info)
{
asm_interrupt_disable();

gdt_init();
idt_init();
serial_init();

boot_info = info;
}

void arch_init(BootInfo* info)
{
acpi_init(info->acpi_rsdp);

asm_interrupt_enable();
}

Expand Down
2 changes: 1 addition & 1 deletion kernel/arch/x86/thread/scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <menix/thread/spin.h>
#include <menix/thread/thread.h>

#include "apic.h"
#include <apic.h>

SpinLock rope_lock = spin_new();
SpinLock wakeup_lock = spin_new();
Expand Down
Loading

0 comments on commit da21bf8

Please sign in to comment.