[kernel] Clean up main.cpp and others

The kernel/main.cpp and kernel/memory_bootstrap.cpp files had become
something of a junk drawer. This change cleans them up in the following
ways:

- Most CPU initialization has moved to cpu.cpp, allowing several
  functions to be made static and removed from cpu.h
- Multi-core startup code has moved to the new smp.h and smp.cpp, and
  ap_startup.s has been renamed smp.s to match.
- run_constructors() has moved to memory_bootstrap.cpp, and all the
  functionality of that file has been hidden behind a new public
  interface mem::initialize().
- load_init_server() has moved from memory_bootstrap.cpp to main.cpp
This commit is contained in:
Justin C. Miller
2022-01-27 19:28:35 -08:00
parent 3f8dfbd5b2
commit fd25d3babc
15 changed files with 341 additions and 313 deletions

View File

@@ -6,89 +6,39 @@
#include <j6/signals.h>
#include <util/vector.h>
#include "apic.h"
#include "assert.h"
#include "block_device.h"
#include "clock.h"
#include "cpu.h"
#include "device_manager.h"
#include "gdt.h"
#include "idt.h"
#include "interrupts.h"
#include "io.h"
#include "logger.h"
#include "memory.h"
#include "msr.h"
#include "objects/channel.h"
#include "objects/event.h"
#include "objects/process.h"
#include "objects/system.h"
#include "objects/thread.h"
#include "objects/vm_area.h"
#include "scheduler.h"
#include "syscall.h"
#include "smp.h"
#include "sysconf.h"
#include "tss.h"
#include "vm_space.h"
#ifndef GIT_VERSION
#define GIT_VERSION
#endif
extern "C" {
void kernel_main(bootproto::args *args);
void (*__ctors)(void);
void (*__ctors_end)(void);
void long_ap_startup(cpu_data *cpu);
void ap_startup();
void ap_idle();
void init_ap_trampoline(void*, cpu_data *, void (*)());
}
volatile size_t ap_startup_count;
static bool scheduler_ready = false;
/// Bootstrap the memory managers.
void memory_initialize_pre_ctors(bootproto::args &kargs);
void memory_initialize_post_ctors(bootproto::args &kargs);
void load_init_server(bootproto::program &program, uintptr_t modules_address);
unsigned start_aps(lapic &apic, const util::vector<uint8_t> &ids, void *kpml4);
void
run_constructors()
{
void (**p)(void) = &__ctors;
while (p < &__ctors_end) {
void (*ctor)(void) = *p++;
if (ctor) ctor();
}
}
void
kernel_main(bootproto::args *args)
{
if (args->panic) {
IDT::set_nmi_handler(args->panic->entrypoint);
panic::symbol_table = util::offset_pointer(args->symbol_table, mem::linear_offset);
const void *syms = util::offset_pointer(args->symbol_table, mem::linear_offset);
panic::install(args->panic->entrypoint, syms);
}
logger_init();
cpu_validate();
extern IDT &g_bsp_idt;
extern TSS &g_bsp_tss;
extern GDT &g_bsp_gdt;
extern cpu_data g_bsp_cpu_data;
extern uintptr_t idle_stack_end;
cpu_data *cpu = &g_bsp_cpu_data;
memset(cpu, 0, sizeof(cpu_data));
cpu->self = cpu;
cpu->idt = new (&g_bsp_idt) IDT;
cpu->tss = new (&g_bsp_tss) TSS;
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
cpu->rsp0 = idle_stack_end;
cpu_early_init(cpu);
cpu_data *cpu = bsp_early_init();
kassert(args->magic == bootproto::args_magic,
"Bad kernel args magic number");
@@ -99,48 +49,24 @@ kernel_main(bootproto::args *args)
log::debug(logs::boot, "Runtime service is at: %016lx", args->runtime_services);
log::debug(logs::boot, " Kernel PML4 is at: %016lx", args->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);
disable_legacy_pic();
memory_initialize_pre_ctors(*args);
run_constructors();
memory_initialize_post_ctors(*args);
mem::initialize(*args);
cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries());
syscall_initialize();
bsp_late_init();
device_manager &devices = device_manager::get();
devices.parse_acpi(args->acpi_table);
// Need the local APIC to get the BSP's id
uintptr_t apic_base = devices.get_lapic_base();
lapic *apic = new lapic(apic_base);
apic->enable();
cpu->id = apic->get_id();
cpu->apic = apic;
devices.init_drivers();
cpu_init(cpu, true);
devices.init_drivers();
apic->calibrate_timer();
const auto &apic_ids = devices.get_apic_ids();
g_num_cpus = start_aps(*apic, apic_ids, args->pml4);
g_num_cpus = smp::start(*cpu, args->pml4);
sysconf_create();
interrupts_enable();
//g_com1.handle_interrupt();
scheduler *sched = new scheduler {g_num_cpus};
scheduler_ready = true;
// Load the init server
load_init_server(*args->init, args->modules);
@@ -148,128 +74,34 @@ kernel_main(bootproto::args *args)
sched->start();
}
unsigned
start_aps(lapic &apic, const util::vector<uint8_t> &ids, void *kpml4)
void
load_init_server(bootproto::program &program, uintptr_t modules_address)
{
using mem::frame_size;
using mem::kernel_stack_pages;
using bootproto::section_flags;
using obj::vm_flags;
extern size_t ap_startup_code_size;
extern obj::process &g_kernel_process;
extern obj::vm_area_guarded &g_kernel_stacks;
obj::process *p = new obj::process;
p->add_handle(&obj::system::get(), obj::system::init_caps);
clock &clk = clock::get();
vm_space &space = p->space();
for (const auto &sect : program.sections) {
vm_flags flags =
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
ap_startup_count = 1; // BSP processor
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 &bsp = current_cpu();
bsp.process = &g_kernel_process;
uint16_t index = bsp.index;
// 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;
obj::vm_area *vma = new obj::vm_area_fixed(addr, 0x1000, vm_flags::write);
vm_space::kernel_space().add(addr, vma);
memcpy(
reinterpret_cast<void*>(addr),
reinterpret_cast<void*>(&ap_startup),
ap_startup_code_size);
// AP idle stacks need less room than normal stacks, so pack multiple
// into a normal stack area
static constexpr size_t idle_stack_bytes = 2048; // 2KiB is generous
static constexpr size_t full_stack_bytes = kernel_stack_pages * frame_size;
static constexpr size_t idle_stacks_per = full_stack_bytes / idle_stack_bytes;
uint8_t ist_entries = IDT::current().used_ist_entries();
size_t free_stack_count = 0;
uintptr_t stack_area_start = 0;
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
apic.send_ipi_broadcast(mode, false, 0);
for (uint8_t id : ids) {
if (id == bsp.id) continue;
// Set up the CPU data structures
IDT *idt = new IDT;
TSS *tss = new TSS;
GDT *gdt = new GDT {tss};
cpu_data *cpu = new cpu_data;
memset(cpu, 0, sizeof(cpu_data));
cpu->self = cpu;
cpu->id = id;
cpu->index = ++index;
cpu->idt = idt;
cpu->tss = tss;
cpu->gdt = gdt;
tss->create_ist_stacks(ist_entries);
// Set up the CPU's idle task stack
if (free_stack_count == 0) {
stack_area_start = g_kernel_stacks.get_section();
free_stack_count = idle_stacks_per;
}
uintptr_t stack_end = stack_area_start + free_stack_count-- * idle_stack_bytes;
stack_end -= 2 * sizeof(void*); // Null frame
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
cpu->rsp0 = stack_end;
// Set up the trampoline with this CPU's data
init_ap_trampoline(kpml4, cpu, ap_idle);
// Kick it off!
size_t current_count = ap_startup_count;
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
lapic::ipi startup = lapic::ipi::startup | lapic::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);
}
// If the CPU already incremented ap_startup_count, it's done
if (ap_startup_count > current_count)
continue;
// Send the second SIPI (intel recommends this)
apic.send_ipi(startup, vector, id);
for (unsigned i = 0; i < 100; ++i) {
if (ap_startup_count > current_count) break;
clk.spinwait(100);
}
log::warn(logs::boot, "No response from AP %d within timeout", id);
obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags);
space.add(sect.virt_addr, vma);
}
log::info(logs::boot, "%d CPUs running", ap_startup_count);
vm_space::kernel_space().remove(vma);
return ap_startup_count;
}
void
long_ap_startup(cpu_data *cpu)
{
cpu_init(cpu, false);
++ap_startup_count;
while (!scheduler_ready) asm ("pause");
uintptr_t apic_base =
device_manager::get().get_lapic_base();
cpu->apic = new lapic(apic_base);
cpu->apic->enable();
scheduler::get().start();
uint64_t iopl = (3ull << 12);
obj::thread *main = p->create_thread();
main->add_thunk_user(program.entrypoint, 0, iopl);
main->set_state(obj::thread::state::ready);
// Hacky: No process exists to have created a stack for init; it needs to create
// its own stack. We take advantage of that to use rsp to pass it the init modules
// address.
auto *tcb = main->tcb();
tcb->rsp3 = modules_address;
}