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:
parent
123087e235
commit
8a258edfd6
5 changed files with 108 additions and 48 deletions
|
@ -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!
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue