[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_OSCMMEXCPT equ (1 << 10)
|
||||||
CR4_FSGSBASE equ (1 << 16)
|
CR4_FSGSBASE equ (1 << 16)
|
||||||
CR4_PCIDE equ (1 << 17)
|
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
|
CR4_VAL equ CR4_DE|CR4_PAE|CR4_MCE|CR4_PGE|CR4_OSFXSR|CR4_OSCMMEXCPT|CR4_FSGSBASE|CR4_PCIDE
|
||||||
|
|
||||||
EFER_MSR equ 0xC0000080
|
EFER_MSR equ 0xC0000080
|
||||||
@@ -69,7 +70,8 @@ align 4
|
|||||||
lidt [BASE + (.idtd - ap_startup)]
|
lidt [BASE + (.idtd - ap_startup)]
|
||||||
|
|
||||||
; Enter long mode
|
; Enter long mode
|
||||||
mov eax, CR4_VAL
|
mov eax, cr4
|
||||||
|
or eax, CR4_INIT
|
||||||
mov cr4, eax
|
mov cr4, eax
|
||||||
|
|
||||||
mov eax, [BASE + (.pml4 - ap_startup)]
|
mov eax, [BASE + (.pml4 - ap_startup)]
|
||||||
@@ -100,6 +102,8 @@ align 8
|
|||||||
mov gs, ax
|
mov gs, ax
|
||||||
mov ss, ax
|
mov ss, ax
|
||||||
|
|
||||||
|
mov eax, CR4_VAL
|
||||||
|
|
||||||
mov rdi, [BASE + (.cpu - ap_startup)]
|
mov rdi, [BASE + (.cpu - ap_startup)]
|
||||||
mov rax, [rdi + CPU_DATA.rsp0]
|
mov rax, [rdi + CPU_DATA.rsp0]
|
||||||
mov rsp, rax
|
mov rsp, rax
|
||||||
|
|||||||
@@ -70,16 +70,32 @@ lapic::get_id()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
// Wait until the APIC is ready to send
|
||||||
ipi_wait();
|
ipi_wait();
|
||||||
|
|
||||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
|
||||||
uint32_t command =
|
uint32_t command =
|
||||||
static_cast<uint32_t>(vector) |
|
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);
|
apic_write(m_base, lapic_icr_low, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
/// Classes to control both local and I/O APICs.
|
/// Classes to control both local and I/O APICs.
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "kutil/enum_bitfields.h"
|
||||||
|
|
||||||
enum class isr : uint8_t;
|
enum class isr : uint8_t;
|
||||||
|
|
||||||
@@ -18,6 +19,22 @@ protected:
|
|||||||
uint32_t *m_base;
|
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
|
/// Controller for processor-local APICs
|
||||||
class lapic :
|
class lapic :
|
||||||
@@ -32,19 +49,17 @@ public:
|
|||||||
/// Get the local APIC's ID
|
/// Get the local APIC's ID
|
||||||
uint8_t get_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.
|
/// Send an inter-processor interrupt.
|
||||||
/// \arg mode The sending mode
|
/// \arg mode The sending mode
|
||||||
/// \arg vector The interrupt vector
|
/// \arg vector The interrupt vector
|
||||||
/// \arg dest The APIC ID of the destination
|
/// \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
|
/// Wait for an IPI to finish sending. This is done automatically
|
||||||
/// before sending another IPI with send_ipi().
|
/// before sending another IPI with send_ipi().
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ start_aps(void *kpml4)
|
|||||||
size_t free_stack_count = 0;
|
size_t free_stack_count = 0;
|
||||||
uintptr_t stack_area_start = 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) {
|
for (uint8_t id : ids) {
|
||||||
if (id == apic.get_id()) continue;
|
if (id == apic.get_id()) continue;
|
||||||
|
|
||||||
@@ -287,10 +290,10 @@ start_aps(void *kpml4)
|
|||||||
// Kick it off!
|
// Kick it off!
|
||||||
size_t current_count = ap_startup_count;
|
size_t current_count = ap_startup_count;
|
||||||
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
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) {
|
for (unsigned i = 0; i < 20; ++i) {
|
||||||
if (ap_startup_count > current_count) break;
|
if (ap_startup_count > current_count) break;
|
||||||
clk.spinwait(20);
|
clk.spinwait(20);
|
||||||
@@ -301,7 +304,7 @@ start_aps(void *kpml4)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Send the second SIPI (intel recommends this)
|
// 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) {
|
for (unsigned i = 0; i < 100; ++i) {
|
||||||
if (ap_startup_count > current_count) break;
|
if (ap_startup_count > current_count) break;
|
||||||
clk.spinwait(100);
|
clk.spinwait(100);
|
||||||
|
|||||||
Reference in New Issue
Block a user