[kernel] Start all other processors in the system
This very large commit is mainly focused on getting the APs started and to a state where they're waiting to have work scheduled. (Actually scheduling on them is for another commit.) To do this, a bunch of major changes were needed: - Moving a lot of the CPU initialization (including for the BSP) to init_cpu(). This includes setting up IST stacks, writing MSRs, and creating the cpu_data structure. For the APs, this also creates and installs the GDT and TSS, and installs the global IDT. - Creating the AP startup code, which tries to be as position independent as possible. It's copied from its location to 0x8000 for AP startup, and some of it is fixed at that address. The AP startup code jumps from real mode to long mode with paging in one swell foop. - Adding limited IPI capability to the lapic class. This will need to improve. - Renaming cpu/cpu.* to cpu/cpu_id.* because it was just annoying in GDB and really isn't anything but cpu_id anymore. - Moved all the GDT, TSS, and IDT code into their own files and made them classes instead of a mess of free functions. - Got rid of bsp_cpu_data everywhere. Now always call the new current_cpu() to get the current CPU's cpu_data. - Device manager keeps a list of APIC ids now. This should go somewhere else eventually, device_manager needs to be refactored away. - Moved some more things (notably the g_kernel_stacks vma) to the pre-constructor setup in memory_bootstrap. That whole file is in bad need of a refactor.
This commit is contained in:
@@ -6,22 +6,28 @@
|
||||
#include "kutil/assert.h"
|
||||
#include "apic.h"
|
||||
#include "block_device.h"
|
||||
#include "clock.h"
|
||||
#include "console.h"
|
||||
#include "cpu.h"
|
||||
#include "device_manager.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "interrupts.h"
|
||||
#include "io.h"
|
||||
#include "kernel_args.h"
|
||||
#include "kernel_memory.h"
|
||||
#include "log.h"
|
||||
#include "msr.h"
|
||||
#include "objects/channel.h"
|
||||
#include "objects/event.h"
|
||||
#include "objects/thread.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "scheduler.h"
|
||||
#include "serial.h"
|
||||
#include "symbol_table.h"
|
||||
#include "syscall.h"
|
||||
#include "tss.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
#ifndef GIT_VERSION
|
||||
#define GIT_VERSION
|
||||
@@ -31,18 +37,24 @@ extern "C" {
|
||||
void kernel_main(kernel::args::header *header);
|
||||
void (*__ctors)(void);
|
||||
void (*__ctors_end)(void);
|
||||
void long_ap_startup();
|
||||
void ap_startup();
|
||||
void init_ap_trampoline(void*, uintptr_t, void (*)());
|
||||
}
|
||||
|
||||
extern void __kernel_assert(const char *, unsigned, const char *);
|
||||
|
||||
using namespace kernel;
|
||||
|
||||
volatile size_t ap_startup_count;
|
||||
|
||||
/// Bootstrap the memory managers.
|
||||
void setup_pat();
|
||||
void memory_initialize_pre_ctors(args::header &kargs);
|
||||
void memory_initialize_post_ctors(args::header &kargs);
|
||||
process * load_simple_process(args::program &program);
|
||||
|
||||
void start_aps(void *kpml4);
|
||||
|
||||
/// TODO: not this. this is awful.
|
||||
args::framebuffer *fb = nullptr;
|
||||
|
||||
@@ -77,7 +89,18 @@ kernel_main(args::header *header)
|
||||
logger_init();
|
||||
|
||||
cpu_validate();
|
||||
setup_pat();
|
||||
|
||||
log::debug(logs::boot, " jsix header is at: %016lx", header);
|
||||
log::debug(logs::boot, " Memory map is at: %016lx", header->mem_map);
|
||||
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
||||
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime_services);
|
||||
log::debug(logs::boot, " Kernel PML4 is at: %016lx", header->pml4);
|
||||
|
||||
uint64_t cr0, cr4;
|
||||
asm ("mov %%cr0, %0" : "=r"(cr0));
|
||||
asm ("mov %%cr4, %0" : "=r"(cr4));
|
||||
uint64_t efer = rdmsr(msr::ia32_efer);
|
||||
log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer);
|
||||
|
||||
bool has_video = false;
|
||||
if (header->video.size > 0) {
|
||||
@@ -95,10 +118,20 @@ kernel_main(args::header *header)
|
||||
logger_clear_immediate();
|
||||
}
|
||||
|
||||
gdt_init();
|
||||
interrupts_init();
|
||||
extern TSS &g_bsp_tss;
|
||||
extern GDT &g_bsp_gdt;
|
||||
|
||||
TSS *tss = new (&g_bsp_tss) TSS;
|
||||
GDT *gdt = new (&g_bsp_gdt) GDT {tss};
|
||||
gdt->install();
|
||||
|
||||
IDT *idt = new (&g_idt) IDT;
|
||||
idt->install();
|
||||
|
||||
disable_legacy_pic();
|
||||
|
||||
memory_initialize_pre_ctors(*header);
|
||||
init_cpu(true);
|
||||
run_constructors();
|
||||
memory_initialize_post_ctors(*header);
|
||||
|
||||
@@ -116,16 +149,15 @@ kernel_main(args::header *header)
|
||||
}
|
||||
}
|
||||
|
||||
log::debug(logs::boot, " jsix header is at: %016lx", header);
|
||||
log::debug(logs::boot, " Memory map is at: %016lx", header->mem_map);
|
||||
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
||||
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime_services);
|
||||
|
||||
device_manager &devices = device_manager::get();
|
||||
devices.parse_acpi(header->acpi_table);
|
||||
|
||||
devices.init_drivers();
|
||||
devices.get_lapic()->calibrate_timer();
|
||||
devices.get_lapic().calibrate_timer();
|
||||
|
||||
start_aps(header->pml4);
|
||||
|
||||
interrupts_enable();
|
||||
|
||||
/*
|
||||
@@ -164,3 +196,80 @@ kernel_main(args::header *header)
|
||||
|
||||
sched->start();
|
||||
}
|
||||
|
||||
void
|
||||
start_aps(void *kpml4)
|
||||
{
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
|
||||
extern size_t ap_startup_code_size;
|
||||
extern process &g_kernel_process;
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
|
||||
clock &clk = clock::get();
|
||||
lapic &apic = device_manager::get().get_lapic();
|
||||
|
||||
ap_startup_count = 1; // BSP processor
|
||||
auto &ids = device_manager::get().get_apic_ids();
|
||||
log::info(logs::boot, "Starting %d other CPUs", ids.count() - 1);
|
||||
|
||||
// Since we're using address space outside kernel space, make sure
|
||||
// the kernel's vm_space is used
|
||||
cpu_data &cpu = current_cpu();
|
||||
cpu.process = &g_kernel_process;
|
||||
|
||||
// Copy the startup code somwhere the real mode trampoline can run
|
||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||
uint8_t vector = addr >> 12;
|
||||
vm_area *vma = new vm_area_fixed(addr, 0x1000, vm_flags::write);
|
||||
vm_space::kernel_space().add(addr, vma);
|
||||
kutil::memcpy(
|
||||
reinterpret_cast<void*>(addr),
|
||||
reinterpret_cast<void*>(&ap_startup),
|
||||
ap_startup_code_size);
|
||||
|
||||
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
|
||||
for (uint8_t id : ids) {
|
||||
if (id == apic.get_id()) continue;
|
||||
log::info(logs::boot, "Starting AP %d", id);
|
||||
|
||||
size_t current_count = ap_startup_count;
|
||||
uintptr_t stack_start = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_end = stack_start + stack_bytes - 2 * sizeof(void*);
|
||||
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
|
||||
|
||||
init_ap_trampoline(kpml4, stack_end, long_ap_startup);
|
||||
|
||||
apic.send_ipi(lapic::ipi_mode::init, 0, id);
|
||||
clk.spinwait(1000);
|
||||
|
||||
apic.send_ipi(lapic::ipi_mode::startup, vector, id);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(10);
|
||||
}
|
||||
|
||||
if (ap_startup_count > current_count)
|
||||
continue;
|
||||
|
||||
apic.send_ipi(lapic::ipi_mode::startup, vector, id);
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(10);
|
||||
}
|
||||
}
|
||||
|
||||
log::info(logs::boot, "%d CPUs running", ap_startup_count);
|
||||
vm_space::kernel_space().remove(vma);
|
||||
}
|
||||
|
||||
void
|
||||
long_ap_startup()
|
||||
{
|
||||
init_cpu(false);
|
||||
++ap_startup_count;
|
||||
|
||||
while(1) asm("hlt");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user