1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:28:11 +00:00

Kernel: Add x2APIC support

This allows addressing all cores on more modern processors. For now,
we still have a hardcoded limit of 64 due to s_processors being a
static array.
This commit is contained in:
Tom 2021-09-03 21:27:57 -06:00 committed by Andreas Kling
parent 123087e235
commit 8a258edfd6
5 changed files with 108 additions and 48 deletions

View file

@ -34,7 +34,9 @@
#define APIC_ENABLED (1 << 8)
#define APIC_BASE_MSR 0x1b
#define APIC_REGS_MSR_BASE 0x800
#define APIC_REG_ID 0x20
#define APIC_REG_EOI 0xb0
#define APIC_REG_LD 0xd0
#define APIC_REG_DF 0xe0
@ -145,27 +147,39 @@ PhysicalAddress APIC::get_base()
void APIC::set_base(const PhysicalAddress& base)
{
MSR msr(APIC_BASE_MSR);
msr.set(base.get() | 0x800);
u64 flags = 1 << 11;
if (m_is_x2)
flags |= 1 << 10;
msr.set(base.get() | flags);
}
void APIC::write_register(u32 offset, u32 value)
{
*reinterpret_cast<volatile u32*>(m_apic_base->vaddr().offset(offset).as_ptr()) = value;
if (m_is_x2) {
MSR msr(APIC_REGS_MSR_BASE + (offset >> 4));
msr.set(value);
} else {
*reinterpret_cast<volatile u32*>(m_apic_base->vaddr().offset(offset).as_ptr()) = value;
}
}
u32 APIC::read_register(u32 offset)
{
if (m_is_x2) {
MSR msr(APIC_REGS_MSR_BASE + (offset >> 4));
return (u32)msr.get();
}
return *reinterpret_cast<volatile u32*>(m_apic_base->vaddr().offset(offset).as_ptr());
}
void APIC::set_lvt(u32 offset, u8 interrupt)
{
write_register(offset, (read_register(offset) & 0xffffffff) | interrupt);
write_register(offset, read_register(offset) | interrupt);
}
void APIC::set_siv(u32 offset, u8 interrupt)
{
write_register(offset, (read_register(offset) & 0xffffffff) | interrupt | APIC_ENABLED);
write_register(offset, read_register(offset) | interrupt | APIC_ENABLED);
}
void APIC::wait_for_pending_icr()
@ -177,8 +191,13 @@ void APIC::wait_for_pending_icr()
void APIC::write_icr(const ICRReg& icr)
{
write_register(APIC_REG_ICR_HIGH, icr.high());
write_register(APIC_REG_ICR_LOW, icr.low());
if (m_is_x2) {
MSR msr(APIC_REGS_MSR_BASE + (APIC_REG_ICR_LOW >> 4));
msr.set(icr.x2_value());
} else {
write_register(APIC_REG_ICR_HIGH, icr.x_high());
write_register(APIC_REG_ICR_LOW, icr.x_low());
}
}
#define APIC_LVT_TIMER_ONESHOT 0
@ -224,15 +243,19 @@ UNMAP_AFTER_INIT bool APIC::init_bsp()
CPUID id(1);
if ((id.edx() & (1 << 9)) == 0)
return false;
if (id.ecx() & (1 << 21))
m_is_x2 = true;
PhysicalAddress apic_base = get_base();
dbgln_if(APIC_DEBUG, "Initializing APIC, base: {}", apic_base);
dbgln_if(APIC_DEBUG, "Initializing {}APIC, base: {}", m_is_x2 ? "x2" : "x", apic_base);
set_base(apic_base);
m_apic_base = MM.allocate_kernel_region(apic_base.page_base(), PAGE_SIZE, {}, Memory::Region::Access::ReadWrite);
if (!m_apic_base) {
dbgln("APIC: Failed to allocate memory for APIC base");
return false;
if (!m_is_x2) {
m_apic_base = MM.allocate_kernel_region(apic_base.page_base(), PAGE_SIZE, {}, Memory::Region::Access::ReadWrite);
if (!m_apic_base) {
dbgln("APIC: Failed to allocate memory for APIC base");
return false;
}
}
auto rsdp = ACPI::StaticParsing::find_rsdp();
@ -254,10 +277,17 @@ UNMAP_AFTER_INIT bool APIC::init_bsp()
size_t entry_length = madt_entry->length;
if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::LocalAPIC) {
auto* plapic_entry = (const ACPI::Structures::MADTEntries::ProcessorLocalAPIC*)madt_entry;
dbgln_if(APIC_DEBUG, "APIC: AP found @ MADT entry {}, processor ID: {}, APIC ID: {}, flags: {:#08x}", entry_index, plapic_entry->acpi_processor_id, plapic_entry->apic_id, plapic_entry->flags);
dbgln_if(APIC_DEBUG, "APIC: AP found @ MADT entry {}, processor ID: {}, xAPIC ID: {}, flags: {:#08x}", entry_index, plapic_entry->acpi_processor_id, plapic_entry->apic_id, plapic_entry->flags);
m_processor_cnt++;
if ((plapic_entry->flags & 0x1) != 0)
m_processor_enabled_cnt++;
} else if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::Local_x2APIC) {
// Only used for APID IDs >= 255
auto* plx2apic_entry = (const ACPI::Structures::MADTEntries::ProcessorLocalX2APIC*)madt_entry;
dbgln_if(APIC_DEBUG, "APIC: AP found @ MADT entry {}, processor ID: {}, x2APIC ID: {}, flags: {:#08x}", entry_index, plx2apic_entry->acpi_processor_id, plx2apic_entry->apic_id, plx2apic_entry->flags);
m_processor_cnt++;
if ((plx2apic_entry->flags & 0x1) != 0)
m_processor_enabled_cnt++;
}
madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress(madt_entry).offset(entry_length).get());
entries_length -= entry_length;
@ -357,13 +387,13 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps()
dbgln_if(APIC_DEBUG, "APIC: Starting {} AP(s)", aps_to_enable);
// INIT
write_icr(ICRReg(0, ICRReg::INIT, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf));
write_icr({ 0, 0, ICRReg::INIT, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf });
IO::delay(10 * 1000);
for (int i = 0; i < 2; i++) {
// SIPI
write_icr(ICRReg(0x08, ICRReg::StartUp, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf)); // start execution at P8000
write_icr({ 0x08, 0, ICRReg::StartUp, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf }); // start execution at P8000
IO::delay(200);
}
@ -405,17 +435,28 @@ UNMAP_AFTER_INIT void APIC::boot_aps()
UNMAP_AFTER_INIT void APIC::enable(u32 cpu)
{
if (cpu >= 8) {
// TODO: x2apic support?
PANIC("SMP support is currently limited to 8 CPUs!");
VERIFY(m_is_x2 || cpu < 8);
u32 apic_id;
if (m_is_x2) {
dbgln_if(APIC_DEBUG, "Enable x2APIC on CPU #{}", cpu);
// We need to enable x2 mode on each core independently
set_base(get_base());
apic_id = read_register(APIC_REG_ID);
} else {
dbgln_if(APIC_DEBUG, "Setting logical xAPIC ID for CPU #{}", cpu);
// Use the CPU# as logical apic id
VERIFY(cpu <= 8);
write_register(APIC_REG_LD, (read_register(APIC_REG_LD) & 0x00ffffff) | (cpu << 24));
// read it back to make sure it's actually set
apic_id = read_register(APIC_REG_LD) >> 24;
}
// Use the CPU# as logical apic id
VERIFY(cpu <= 0xff);
write_register(APIC_REG_LD, (read_register(APIC_REG_LD) & 0x00ffffff) | (cpu << 24)); // TODO: only if not in x2apic mode
// read it back to make sure it's actually set
auto apic_id = read_register(APIC_REG_LD) >> 24;
dbgln_if(APIC_DEBUG, "CPU #{} apic id: {}", cpu, apic_id);
Processor::current().info().set_apic_id(apic_id);
dbgln_if(APIC_DEBUG, "Enabling local APIC for CPU #{}, logical APIC ID: {}", cpu, apic_id);
@ -423,20 +464,23 @@ UNMAP_AFTER_INIT void APIC::enable(u32 cpu)
if (cpu == 0) {
SpuriousInterruptHandler::initialize(IRQ_APIC_SPURIOUS);
// set error interrupt vector
set_lvt(APIC_REG_LVT_ERR, IRQ_APIC_ERR);
APICErrInterruptHandler::initialize(IRQ_APIC_ERR);
// register IPI interrupt vector
APICIPIInterruptHandler::initialize(IRQ_APIC_IPI);
}
if (!m_is_x2) {
// local destination mode (flat mode), not supported in x2 mode
write_register(APIC_REG_DF, 0xf0000000);
}
// set error interrupt vector
set_lvt(APIC_REG_LVT_ERR, IRQ_APIC_ERR);
// set spurious interrupt vector
set_siv(APIC_REG_SIV, IRQ_APIC_SPURIOUS);
// local destination mode (flat mode)
write_register(APIC_REG_DF, 0xf0000000);
write_register(APIC_REG_LVT_TIMER, APIC_LVT(0, 0) | APIC_LVT_MASKED);
write_register(APIC_REG_LVT_THERMAL, APIC_LVT(0, 0) | APIC_LVT_MASKED);
write_register(APIC_REG_LVT_PERFORMANCE_COUNTER, APIC_LVT(0, 0) | APIC_LVT_MASKED);
@ -485,21 +529,21 @@ void APIC::broadcast_ipi()
{
dbgln_if(APIC_SMP_DEBUG, "SMP: Broadcast IPI from CPU #{}", Processor::current_id());
wait_for_pending_icr();
write_icr(ICRReg(IRQ_APIC_IPI + IRQ_VECTOR_BASE, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf));
write_icr({ IRQ_APIC_IPI + IRQ_VECTOR_BASE, 0xffffffff, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf });
}
void APIC::send_ipi(u32 cpu)
{
dbgln_if(APIC_SMP_DEBUG, "SMP: Send IPI from CPU #{} to CPU #{}", Processor::current_id(), cpu);
VERIFY(cpu != Processor::current_id());
VERIFY(cpu < 8);
VERIFY(cpu < Processor::count());
wait_for_pending_icr();
write_icr(ICRReg(IRQ_APIC_IPI + IRQ_VECTOR_BASE, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::NoShorthand, cpu));
write_icr({ IRQ_APIC_IPI + IRQ_VECTOR_BASE, m_is_x2 ? Processor::by_id(cpu).info().apic_id() : cpu, ICRReg::Fixed, m_is_x2 ? ICRReg::Physical : ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::NoShorthand });
}
UNMAP_AFTER_INIT APICTimer* APIC::initialize_timers(HardwareTimerBase& calibration_timer)
{
if (!m_apic_base)
if (!m_apic_base && !m_is_x2)
return nullptr;
// We should only initialize and calibrate the APIC timer once on the BSP!