[kernel] Fix SMP boot on KVM
KVM didn't like setting all the CR4 bits we wanted at once. I suspect that means real hardware won't either. Delay the setting of the rest of CR4 until after the CPU is in long mode - only set PAE and PGE from real mode.
This commit is contained in:
@@ -20,6 +20,7 @@ CR4_OSFXSR equ (1 << 9)
|
||||
CR4_OSCMMEXCPT equ (1 << 10)
|
||||
CR4_FSGSBASE equ (1 << 16)
|
||||
CR4_PCIDE equ (1 << 17)
|
||||
CR4_INIT equ CR4_PAE|CR4_PGE
|
||||
CR4_VAL equ CR4_DE|CR4_PAE|CR4_MCE|CR4_PGE|CR4_OSFXSR|CR4_OSCMMEXCPT|CR4_FSGSBASE|CR4_PCIDE
|
||||
|
||||
EFER_MSR equ 0xC0000080
|
||||
@@ -69,7 +70,8 @@ align 4
|
||||
lidt [BASE + (.idtd - ap_startup)]
|
||||
|
||||
; Enter long mode
|
||||
mov eax, CR4_VAL
|
||||
mov eax, cr4
|
||||
or eax, CR4_INIT
|
||||
mov cr4, eax
|
||||
|
||||
mov eax, [BASE + (.pml4 - ap_startup)]
|
||||
@@ -100,6 +102,8 @@ align 8
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
mov eax, CR4_VAL
|
||||
|
||||
mov rdi, [BASE + (.cpu - ap_startup)]
|
||||
mov rax, [rdi + CPU_DATA.rsp0]
|
||||
mov rsp, rax
|
||||
|
||||
@@ -70,16 +70,32 @@ lapic::get_id()
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi(ipi_mode mode, uint8_t vector, uint8_t dest)
|
||||
lapic::send_ipi(ipi mode, uint8_t vector, uint8_t dest)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode) << 8;
|
||||
static_cast<uint32_t>(mode);
|
||||
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi_broadcast(ipi mode, bool self, uint8_t vector)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode) |
|
||||
(self ? 0 : (1 << 18)) |
|
||||
(1 << 19);
|
||||
|
||||
apic_write(m_base, lapic_icr_high, 0);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/// Classes to control both local and I/O APICs.
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kutil/enum_bitfields.h"
|
||||
|
||||
enum class isr : uint8_t;
|
||||
|
||||
@@ -18,6 +19,22 @@ protected:
|
||||
uint32_t *m_base;
|
||||
};
|
||||
|
||||
enum class ipi : uint32_t
|
||||
{
|
||||
// Delivery modes
|
||||
fixed = 0x0000,
|
||||
smi = 0x0200,
|
||||
nmi = 0x0400,
|
||||
init = 0x0500,
|
||||
startup = 0x0600,
|
||||
|
||||
// Flags
|
||||
deassert = 0x0000,
|
||||
assert = 0x4000,
|
||||
edge = 0x0000, ///< edge-triggered
|
||||
level = 0x8000, ///< level-triggered
|
||||
};
|
||||
IS_BITFIELD(ipi);
|
||||
|
||||
/// Controller for processor-local APICs
|
||||
class lapic :
|
||||
@@ -32,19 +49,17 @@ public:
|
||||
/// Get the local APIC's ID
|
||||
uint8_t get_id();
|
||||
|
||||
enum class ipi_mode : uint8_t {
|
||||
fixed = 0,
|
||||
smi = 2,
|
||||
nmi = 4,
|
||||
init = 5,
|
||||
startup = 6,
|
||||
};
|
||||
|
||||
/// Send an inter-processor interrupt.
|
||||
/// \arg mode The sending mode
|
||||
/// \arg vector The interrupt vector
|
||||
/// \arg dest The APIC ID of the destination
|
||||
void send_ipi(ipi_mode mode, uint8_t vector, uint8_t dest);
|
||||
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
|
||||
|
||||
/// Send an inter-processor broadcast interrupt to all other CPUs
|
||||
/// \arg mode The sending mode
|
||||
/// \arg self If true, include this CPU in the broadcast
|
||||
/// \arg vector The interrupt vector
|
||||
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
|
||||
|
||||
/// Wait for an IPI to finish sending. This is done automatically
|
||||
/// before sending another IPI with send_ipi().
|
||||
|
||||
@@ -254,6 +254,9 @@ start_aps(void *kpml4)
|
||||
size_t free_stack_count = 0;
|
||||
uintptr_t stack_area_start = 0;
|
||||
|
||||
ipi mode = ipi::init | ipi::level | ipi::assert;
|
||||
apic.send_ipi_broadcast(mode, false, 0);
|
||||
|
||||
for (uint8_t id : ids) {
|
||||
if (id == apic.get_id()) continue;
|
||||
|
||||
@@ -287,10 +290,10 @@ start_aps(void *kpml4)
|
||||
// Kick it off!
|
||||
size_t current_count = ap_startup_count;
|
||||
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
||||
apic.send_ipi(lapic::ipi_mode::init, 0, id);
|
||||
clk.spinwait(1000);
|
||||
|
||||
apic.send_ipi(lapic::ipi_mode::startup, vector, id);
|
||||
ipi startup = ipi::startup | ipi::assert;
|
||||
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(20);
|
||||
@@ -301,7 +304,7 @@ start_aps(void *kpml4)
|
||||
continue;
|
||||
|
||||
// Send the second SIPI (intel recommends this)
|
||||
apic.send_ipi(lapic::ipi_mode::startup, vector, id);
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(100);
|
||||
|
||||
Reference in New Issue
Block a user