WIP x86-smp
add uspace mmu support for x86-64 trampoline x86-64 cpus to long mode and into the kernel aspace
This commit is contained in:
@@ -22,10 +22,59 @@
|
||||
#include <platform/pc.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
#define LOCAL_TRACE 1
|
||||
|
||||
static bool lapic_present = false;
|
||||
static uint8_t *lapic_mmio;
|
||||
static volatile uint32_t *lapic_mmio;
|
||||
|
||||
// local apic registers
|
||||
enum lapic_regs {
|
||||
LAPIC_ID = 0x20,
|
||||
LAPIC_VERSION = 0x30,
|
||||
LAPIC_TPR = 0x80,
|
||||
LAPIC_APR = 0x90,
|
||||
LAPIC_PPR = 0xa0,
|
||||
LAPIC_EOI = 0xb0,
|
||||
LAPIC_RRD = 0xc0,
|
||||
LAPIC_LDR = 0xd0,
|
||||
LAPIC_DFR = 0xe0,
|
||||
LAPIC_SVR = 0xf0,
|
||||
LAPIC_ISR0 = 0x100,
|
||||
|
||||
LAPIC_TMR0 = 0x180,
|
||||
|
||||
LAPIC_IRR0 = 0x200,
|
||||
|
||||
LAPIC_ESR = 0x280,
|
||||
|
||||
LAPIC_CMCI = 0x2f0,
|
||||
LAPIC_ICRLO = 0x300,
|
||||
LAPIC_ICRHI = 0x310,
|
||||
LAPIC_TIMER = 0x320,
|
||||
LAPIC_THERMAL = 0x330,
|
||||
LAPIC_PERF = 0x340,
|
||||
LAPIC_LINT0 = 0x350,
|
||||
LAPIC_LINT1 = 0x360,
|
||||
LAPIC_ERROR = 0x370,
|
||||
LAPIC_TICR = 0x380,
|
||||
LAPIC_TCCR = 0x390,
|
||||
LAPIC_DIV = 0x3e0,
|
||||
|
||||
// Extended features
|
||||
LAPIC_EXT_FEATURES = 0x400,
|
||||
LAPIC_EXT_CONTROL = 0x410,
|
||||
LAPIC_EXT_SEOI = 0x420,
|
||||
LAPIC_EXT_IER0 = 0x480,
|
||||
LAPIC_EXT_LVT0 = 0x500,
|
||||
};
|
||||
|
||||
static uint32_t lapic_read(enum lapic_regs reg) {
|
||||
return mmio_read32(lapic_mmio + reg / 4);
|
||||
}
|
||||
|
||||
static void lapic_write(enum lapic_regs reg, uint32_t val) {
|
||||
mmio_write32(lapic_mmio + reg / 4, val);
|
||||
}
|
||||
|
||||
void lapic_init(void) {
|
||||
// discover the presence of the local apic and map it
|
||||
@@ -43,9 +92,14 @@ void lapic_init_postvm(uint level) {
|
||||
|
||||
// IA32_APIC_BASE_MSR
|
||||
uint64_t apic_base = read_msr(0x1b);
|
||||
LTRACEF("apic base %#llx\n", apic_base);
|
||||
LTRACEF("raw apic base msr %#llx\n", apic_base);
|
||||
|
||||
// TODO: assert that it's enabled
|
||||
// make sure it's enabled
|
||||
if ((apic_base & 0x800) == 0) {
|
||||
dprintf(INFO, "X86: enabling lapic\n");
|
||||
apic_base |= 0x800;
|
||||
write_msr(0x1b, apic_base);
|
||||
}
|
||||
|
||||
apic_base &= ~0xfff;
|
||||
dprintf(INFO, "X86: lapic physical address %#llx\n", apic_base);
|
||||
@@ -54,6 +108,20 @@ void lapic_init_postvm(uint level) {
|
||||
status_t err = vmm_alloc_physical(vmm_get_kernel_aspace(), "lapic", PAGE_SIZE, (void **)&lapic_mmio, 0,
|
||||
apic_base & ~0xfff, /* vmm_flags */ 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
|
||||
ASSERT(err == NO_ERROR);
|
||||
|
||||
// Read the local apic id and version and features
|
||||
uint32_t id = lapic_read(LAPIC_ID);
|
||||
uint32_t version = lapic_read(LAPIC_VERSION);
|
||||
bool eas = version & (1u<<31);
|
||||
uint32_t max_lvt = (version >> 16) & 0xff;
|
||||
version &= 0xff;
|
||||
dprintf(INFO, "X86: local apic id %#x version %#x\n", id, version);
|
||||
dprintf(INFO, "X86: local apic max lvt entries %u\n", max_lvt);
|
||||
if (eas) {
|
||||
dprintf(INFO, "X86: local apic EAS features %#x\n", lapic_read(LAPIC_EXT_FEATURES));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
LK_INIT_HOOK(lapic, lapic_init_postvm, LK_INIT_LEVEL_VM);
|
||||
@@ -61,7 +129,28 @@ LK_INIT_HOOK(lapic, lapic_init_postvm, LK_INIT_LEVEL_VM);
|
||||
void lapic_eoi(unsigned int vector) {
|
||||
LTRACEF("vector %#x\n", vector);
|
||||
if (lapic_present) {
|
||||
*REG32(lapic_mmio + 0xb0) = 1;
|
||||
lapic_write(LAPIC_EOI, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void lapic_send_init_ipi(uint32_t apic_id, bool level) {
|
||||
if (lapic_present) {
|
||||
lapic_write(LAPIC_ICRHI, apic_id << 24);
|
||||
lapic_write(LAPIC_ICRLO, (5u << 8) | (level ? (1u << 14) : 0));
|
||||
}
|
||||
}
|
||||
|
||||
void lapic_send_startup_ipi(uint32_t apic_id, uint32_t startup_vector) {
|
||||
if (lapic_present) {
|
||||
lapic_write(LAPIC_ICRHI, apic_id << 24);
|
||||
lapic_write(LAPIC_ICRLO, (6u << 8) | (startup_vector >> 12));
|
||||
}
|
||||
}
|
||||
|
||||
void lapic_send_ipi(uint32_t apic_id, uint32_t vector) {
|
||||
if (lapic_present) {
|
||||
lapic_write(LAPIC_ICRHI, apic_id << 24);
|
||||
// XXX add correct flag bits
|
||||
lapic_write(LAPIC_ICRLO, vector);
|
||||
}
|
||||
}
|
||||
167
platform/pc/mp-boot.S
Normal file
167
platform/pc/mp-boot.S
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <lk/asm.h>
|
||||
#include <arch/x86/descriptor.h>
|
||||
|
||||
#define LOAD_ADDRESS 0x4000
|
||||
#define MSR_EFER 0xc0000080
|
||||
#define EFER_LME 0x00000100
|
||||
|
||||
#define ARGS_ADDRESS (LOAD_ADDRESS + 0x1000)
|
||||
#define ARGS_CR3 (ARGS_ADDRESS + 0x00)
|
||||
#define ARGS_STACK (ARGS_ADDRESS + 0x08)
|
||||
|
||||
.text
|
||||
.code16
|
||||
// secondary cpu boot entry point and switch to protected mode
|
||||
// enters with the following state:
|
||||
// real mode, CS 0x0400, PC 0 (physical address 0x4000)
|
||||
FUNCTION(mp_boot_start)
|
||||
// jump over the temp GDT below and switch to a flat memory segment (0)
|
||||
ljmp $0, $(LOAD_ADDRESS + 0x28)
|
||||
|
||||
.org 0x8
|
||||
.Lgdt:
|
||||
// temporary GDT to get us into protected mode
|
||||
// stuff the GDTR in the first entry
|
||||
.short (8*4)
|
||||
.int (LOAD_ADDRESS + 0x8) // address of .Lgdt
|
||||
.short 0
|
||||
|
||||
// 0x8 code flat 32bit
|
||||
.short 0xffff /* limit 15:00 */
|
||||
.short 0x0000 /* base 15:00 */
|
||||
.byte 0x00 /* base 23:16 */
|
||||
.byte 0b10011010 /* P(1) DPL(00) S(1) 1 C(0) R(1) A(0) */
|
||||
.byte 0b11001111 /* G(1) D(1) 0 0 limit 19:16 */
|
||||
.byte 0x0 /* base 31:24 */
|
||||
|
||||
// 0x10 data flat 32bit
|
||||
.short 0xffff /* limit 15:00 */
|
||||
.short 0x0000 /* base 15:00 */
|
||||
.byte 0x00 /* base 23:16 */
|
||||
.byte 0b10010010 /* P(1) DPL(00) S(1) 0 E(0) W(1) A(0) */
|
||||
.byte 0b11001111 /* G(1) B(1) 0 0 limit 19:16 */
|
||||
.byte 0x0 /* base 31:24 */
|
||||
|
||||
// 0x18 code 64bit
|
||||
.short 0xffff /* limit 15:00 */
|
||||
.short 0x0000 /* base 15:00 */
|
||||
.byte 0x00 /* base 23:16 */
|
||||
.byte 0b10011010 /* P(1) DPL(00) S(1) 1 C(0) R(1) A(0) */
|
||||
.byte 0b10101111 /* G(1) D(0) L(1) AVL(0) limit 19:16 */
|
||||
.byte 0x0 /* base 31:24 */
|
||||
|
||||
.org 0x28 // 0x08 + 0x20
|
||||
// load the above GDT
|
||||
lgdt (LOAD_ADDRESS + 0x08)
|
||||
|
||||
// switch to protected mode
|
||||
movl %cr0, %eax
|
||||
orl $1, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
// jump to 32bit mode
|
||||
ljmpl $0x8, $(LOAD_ADDRESS + 0x40)
|
||||
.org 0x40
|
||||
.code32
|
||||
.Lprot:
|
||||
// we're now in 32bit mode, set up the 32bit data segment registers
|
||||
mov $0x10, %ax
|
||||
mov %ax, %ss
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
#if ARCH_X86_64
|
||||
// set up 64bit paging
|
||||
// set PAE bit in CR4
|
||||
mov %cr4, %eax
|
||||
or $(1<<5), %eax
|
||||
mov %eax, %cr4
|
||||
|
||||
// Enable Long mode
|
||||
movl $MSR_EFER ,%ecx
|
||||
rdmsr
|
||||
orl $EFER_LME,%eax
|
||||
wrmsr
|
||||
|
||||
// load trampoline page table
|
||||
movl (ARGS_CR3), %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
// enable paging, now we're in 32bit compatibility mode
|
||||
mov %cr0, %eax
|
||||
btsl $(31), %eax
|
||||
mov %eax, %cr0
|
||||
|
||||
movl $(LOAD_ADDRESS + 0x800), %esp
|
||||
|
||||
// Use a far jump to get into 64bit mode
|
||||
pushl $0x18
|
||||
pushl $(LOAD_ADDRESS + 0x90)
|
||||
lret
|
||||
|
||||
.org 0x90
|
||||
.code64
|
||||
farjump64:
|
||||
/* branch to our high address */
|
||||
movq (.Lhigh_addr), %rax
|
||||
jmp *%rax
|
||||
.Lhigh_addr:
|
||||
.quad mp_boot_start_high
|
||||
|
||||
#else // ARCH_X86_32
|
||||
// set up 32bit paging
|
||||
|
||||
// set PSE bit in CR4
|
||||
mov %cr4, %eax
|
||||
or $(1<<4), %eax
|
||||
mov %eax, %cr4
|
||||
|
||||
// XXX load trampoline page table
|
||||
|
||||
// get into high address
|
||||
|
||||
// set up stack pointer
|
||||
|
||||
// call into C
|
||||
cld
|
||||
jmp .
|
||||
#endif
|
||||
|
||||
DATA(mp_boot_end)
|
||||
END_FUNCTION(mp_boot_start)
|
||||
|
||||
FUNCTION(mp_boot_start_high)
|
||||
#if ARCH_X86_64
|
||||
// set up stack pointer
|
||||
mov $(ARGS_STACK), %rsp
|
||||
|
||||
// load the real GDT
|
||||
lgdt _gdtr
|
||||
|
||||
push $CODE_64_SELECTOR
|
||||
lea .Lnext(%rip), %rax
|
||||
push %rax
|
||||
lretq
|
||||
|
||||
.Lnext:
|
||||
|
||||
// zero out the segment registers
|
||||
xor %ax, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
#else // ARCH_X86_32
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// set up stack pointer
|
||||
|
||||
// call into C
|
||||
cld
|
||||
jmp .
|
||||
END_FUNCTION(mp_boot_start_high)
|
||||
@@ -10,16 +10,52 @@
|
||||
|
||||
#include <lk/main.h>
|
||||
#include <lib/acpi_lite.h>
|
||||
#include <string.h>
|
||||
#include <lk/trace.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#if WITH_SMP
|
||||
|
||||
#define TRAMPOLINE_ADDRESS 0x4000
|
||||
|
||||
#define LOCAL_TRACE 1
|
||||
|
||||
static void start_cpu(uint cpu_num, uint32_t apic_id) {
|
||||
extern void mp_boot_start(void);
|
||||
extern void mp_boot_end(void);
|
||||
|
||||
struct bootstrap_args {
|
||||
uintptr_t trampoline_cr3;
|
||||
};
|
||||
|
||||
static void start_cpu(uint cpu_num, uint32_t apic_id, struct bootstrap_args *args) {
|
||||
LTRACEF("cpu_num %u, apic_id %u\n", cpu_num, apic_id);
|
||||
|
||||
// XXX do work here
|
||||
|
||||
arch_disable_ints();
|
||||
|
||||
// start x86 secondary cpu
|
||||
|
||||
// send INIT IPI
|
||||
lapic_send_init_ipi(apic_id, true);
|
||||
thread_sleep(10);
|
||||
|
||||
// deassert INIT
|
||||
lapic_send_init_ipi(apic_id, false);
|
||||
thread_sleep(10);
|
||||
|
||||
lapic_send_startup_ipi(apic_id, TRAMPOLINE_ADDRESS);
|
||||
|
||||
// wait 200us
|
||||
thread_sleep(1);
|
||||
|
||||
// send SIPI again
|
||||
lapic_send_startup_ipi(apic_id, TRAMPOLINE_ADDRESS);
|
||||
|
||||
// wait 10ms
|
||||
thread_sleep(10);
|
||||
|
||||
for (;;);
|
||||
}
|
||||
|
||||
struct detected_cpus {
|
||||
@@ -51,16 +87,48 @@ void platform_start_secondary_cpus(void) {
|
||||
// TODO: deal with cpu topology
|
||||
|
||||
// start up the secondary cpus
|
||||
if (cpus.num_detected > 1) {
|
||||
dprintf(INFO, "PC: detected %u cpus\n", cpus.num_detected);
|
||||
|
||||
lk_init_secondary_cpus(cpus.num_detected - 1);
|
||||
|
||||
for (uint i = 1; i < cpus.num_detected; i++) {
|
||||
dprintf(INFO, "PC: starting cpu %u\n", cpus.apic_ids[i]);
|
||||
start_cpu(i, cpus.apic_ids[i]);
|
||||
}
|
||||
if (cpus.num_detected < 2) {
|
||||
dprintf(INFO, "PC: no secondary cpus detected\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a new aspace to build an identity map in
|
||||
vmm_aspace_t *aspace;
|
||||
status_t err = vmm_create_aspace(&aspace, "identity map", 0);
|
||||
if (err < 0) {
|
||||
panic("failed to create identity map aspace\n");
|
||||
}
|
||||
|
||||
// set up an identity map for the trampoline code
|
||||
|
||||
void *ptr = (void *)TRAMPOLINE_ADDRESS;
|
||||
err = vmm_alloc_physical(aspace, "trampoline", 0x10000, &ptr, 0,
|
||||
TRAMPOLINE_ADDRESS, VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_CACHED);
|
||||
if (err < 0) {
|
||||
panic("failed to allocate trampoline memory\n");
|
||||
}
|
||||
|
||||
vmm_aspace_t *old_aspace = vmm_set_active_aspace(aspace);
|
||||
|
||||
// set up bootstrap code page at TRAMPOLINE_ADDRESS for secondary cpu
|
||||
memcpy(ptr, mp_boot_start, mp_boot_end - mp_boot_start);
|
||||
|
||||
// next page has args in it
|
||||
struct bootstrap_args *args = (struct bootstrap_args *)((uintptr_t)ptr + 0x1000);
|
||||
args->trampoline_cr3 = aspace->arch_aspace.cr3_phys;
|
||||
|
||||
dprintf(INFO, "PC: detected %u cpus\n", cpus.num_detected);
|
||||
|
||||
lk_init_secondary_cpus(cpus.num_detected - 1);
|
||||
|
||||
for (uint i = 1; i < cpus.num_detected; i++) {
|
||||
dprintf(INFO, "PC: starting cpu %u\n", cpus.apic_ids[i]);
|
||||
start_cpu(i, cpus.apic_ids[i], args);
|
||||
}
|
||||
|
||||
// XXX restore old aspace
|
||||
vmm_set_active_aspace(old_aspace);
|
||||
// XXX free aspace when done
|
||||
}
|
||||
|
||||
#endif // WITH_SMP
|
||||
@@ -25,6 +25,9 @@ void pic_mask_interrupts(void);
|
||||
// local apic
|
||||
void lapic_init(void);
|
||||
void lapic_eoi(unsigned int vector);
|
||||
void lapic_send_init_ipi(uint32_t apic_id, bool level);
|
||||
void lapic_send_startup_ipi(uint32_t apic_id, uint32_t startup_vector);
|
||||
void lapic_send_ipi(uint32_t apic_id, uint32_t vector);
|
||||
|
||||
// secondary cpus
|
||||
void platform_start_secondary_cpus(void);
|
||||
|
||||
@@ -24,6 +24,7 @@ MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/keyboard.c \
|
||||
$(LOCAL_DIR)/lapic.c \
|
||||
$(LOCAL_DIR)/mp.c \
|
||||
$(LOCAL_DIR)/mp-boot.S \
|
||||
$(LOCAL_DIR)/pic.c \
|
||||
$(LOCAL_DIR)/platform.c \
|
||||
$(LOCAL_DIR)/timer.c \
|
||||
|
||||
Reference in New Issue
Block a user