|
@@ -34,7 +34,9 @@
|
|
#define APIC_ENABLED (1 << 8)
|
|
#define APIC_ENABLED (1 << 8)
|
|
|
|
|
|
#define APIC_BASE_MSR 0x1b
|
|
#define APIC_BASE_MSR 0x1b
|
|
|
|
+#define APIC_REGS_MSR_BASE 0x800
|
|
|
|
|
|
|
|
+#define APIC_REG_ID 0x20
|
|
#define APIC_REG_EOI 0xb0
|
|
#define APIC_REG_EOI 0xb0
|
|
#define APIC_REG_LD 0xd0
|
|
#define APIC_REG_LD 0xd0
|
|
#define APIC_REG_DF 0xe0
|
|
#define APIC_REG_DF 0xe0
|
|
@@ -145,27 +147,39 @@ PhysicalAddress APIC::get_base()
|
|
void APIC::set_base(const PhysicalAddress& base)
|
|
void APIC::set_base(const PhysicalAddress& base)
|
|
{
|
|
{
|
|
MSR msr(APIC_BASE_MSR);
|
|
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)
|
|
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)
|
|
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());
|
|
return *reinterpret_cast<volatile u32*>(m_apic_base->vaddr().offset(offset).as_ptr());
|
|
}
|
|
}
|
|
|
|
|
|
void APIC::set_lvt(u32 offset, u8 interrupt)
|
|
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)
|
|
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()
|
|
void APIC::wait_for_pending_icr()
|
|
@@ -177,8 +191,13 @@ void APIC::wait_for_pending_icr()
|
|
|
|
|
|
void APIC::write_icr(const ICRReg& 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
|
|
#define APIC_LVT_TIMER_ONESHOT 0
|
|
@@ -224,15 +243,19 @@ UNMAP_AFTER_INIT bool APIC::init_bsp()
|
|
CPUID id(1);
|
|
CPUID id(1);
|
|
if ((id.edx() & (1 << 9)) == 0)
|
|
if ((id.edx() & (1 << 9)) == 0)
|
|
return false;
|
|
return false;
|
|
|
|
+ if (id.ecx() & (1 << 21))
|
|
|
|
+ m_is_x2 = true;
|
|
|
|
|
|
PhysicalAddress apic_base = get_base();
|
|
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);
|
|
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();
|
|
auto rsdp = ACPI::StaticParsing::find_rsdp();
|
|
@@ -254,10 +277,17 @@ UNMAP_AFTER_INIT bool APIC::init_bsp()
|
|
size_t entry_length = madt_entry->length;
|
|
size_t entry_length = madt_entry->length;
|
|
if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::LocalAPIC) {
|
|
if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::LocalAPIC) {
|
|
auto* plapic_entry = (const ACPI::Structures::MADTEntries::ProcessorLocalAPIC*)madt_entry;
|
|
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++;
|
|
m_processor_cnt++;
|
|
if ((plapic_entry->flags & 0x1) != 0)
|
|
if ((plapic_entry->flags & 0x1) != 0)
|
|
m_processor_enabled_cnt++;
|
|
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());
|
|
madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress(madt_entry).offset(entry_length).get());
|
|
entries_length -= entry_length;
|
|
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);
|
|
dbgln_if(APIC_DEBUG, "APIC: Starting {} AP(s)", aps_to_enable);
|
|
|
|
|
|
// INIT
|
|
// 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);
|
|
IO::delay(10 * 1000);
|
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
for (int i = 0; i < 2; i++) {
|
|
// SIPI
|
|
// 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);
|
|
IO::delay(200);
|
|
}
|
|
}
|
|
@@ -405,17 +435,28 @@ UNMAP_AFTER_INIT void APIC::boot_aps()
|
|
|
|
|
|
UNMAP_AFTER_INIT void APIC::enable(u32 cpu)
|
|
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);
|
|
|
|
|
|
- // 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
|
|
|
|
|
|
+ // We need to enable x2 mode on each core independently
|
|
|
|
+ set_base(get_base());
|
|
|
|
|
|
- // read it back to make sure it's actually set
|
|
|
|
- auto apic_id = read_register(APIC_REG_LD) >> 24;
|
|
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dbgln_if(APIC_DEBUG, "CPU #{} apic id: {}", cpu, apic_id);
|
|
Processor::current().info().set_apic_id(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);
|
|
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) {
|
|
if (cpu == 0) {
|
|
SpuriousInterruptHandler::initialize(IRQ_APIC_SPURIOUS);
|
|
SpuriousInterruptHandler::initialize(IRQ_APIC_SPURIOUS);
|
|
|
|
|
|
- // set error interrupt vector
|
|
|
|
- set_lvt(APIC_REG_LVT_ERR, IRQ_APIC_ERR);
|
|
|
|
APICErrInterruptHandler::initialize(IRQ_APIC_ERR);
|
|
APICErrInterruptHandler::initialize(IRQ_APIC_ERR);
|
|
|
|
|
|
// register IPI interrupt vector
|
|
// register IPI interrupt vector
|
|
APICIPIInterruptHandler::initialize(IRQ_APIC_IPI);
|
|
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 spurious interrupt vector
|
|
set_siv(APIC_REG_SIV, IRQ_APIC_SPURIOUS);
|
|
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_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_THERMAL, APIC_LVT(0, 0) | APIC_LVT_MASKED);
|
|
write_register(APIC_REG_LVT_PERFORMANCE_COUNTER, 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());
|
|
dbgln_if(APIC_SMP_DEBUG, "SMP: Broadcast IPI from CPU #{}", Processor::current_id());
|
|
wait_for_pending_icr();
|
|
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)
|
|
void APIC::send_ipi(u32 cpu)
|
|
{
|
|
{
|
|
dbgln_if(APIC_SMP_DEBUG, "SMP: Send IPI from CPU #{} to CPU #{}", Processor::current_id(), cpu);
|
|
dbgln_if(APIC_SMP_DEBUG, "SMP: Send IPI from CPU #{} to CPU #{}", Processor::current_id(), cpu);
|
|
VERIFY(cpu != Processor::current_id());
|
|
VERIFY(cpu != Processor::current_id());
|
|
- VERIFY(cpu < 8);
|
|
|
|
|
|
+ VERIFY(cpu < Processor::count());
|
|
wait_for_pending_icr();
|
|
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)
|
|
UNMAP_AFTER_INIT APICTimer* APIC::initialize_timers(HardwareTimerBase& calibration_timer)
|
|
{
|
|
{
|
|
- if (!m_apic_base)
|
|
|
|
|
|
+ if (!m_apic_base && !m_is_x2)
|
|
return nullptr;
|
|
return nullptr;
|
|
|
|
|
|
// We should only initialize and calibrate the APIC timer once on the BSP!
|
|
// We should only initialize and calibrate the APIC timer once on the BSP!
|