Skip to content

Commit

Permalink
added full proper smp support and integrated it into the mandatory ke…
Browse files Browse the repository at this point in the history
…rnel initialization sequence
  • Loading branch information
FlareCoding committed Sep 8, 2024
1 parent 144eb99 commit 23285ec
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 156 deletions.
Binary file modified efi/OVMF_VARS.fd
Binary file not shown.
188 changes: 188 additions & 0 deletions kernel/src/arch/x86/ap_startup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include "ap_startup.h"
#include <acpi/acpi_controller.h>
#include <paging/page_frame_allocator.h>
#include <paging/page.h>
#include <time/ktime.h>
#include <kelevate/kelevate.h>
#include <gdt/gdt.h>
#include <interrupts/idt.h>
#include <sched/sched.h>

//
// ------------------------------------ IMPORTANT -------------------------------------
// Below addresses are also hardcoded and must match those in arch/x86/asm/ap_startup.s
//

#define AP_STARTUP_ASM_ADDRESS (void*)0x8000
#define AP_STARTUP_C_ENTRY_ADDRESS (void*)0x9000
#define AP_STARTUP_PAGE_TABLE_PTR_ADDRESS (void*)0x15000

#define AP_STARTUP_AP_RUNNING_COUNT_PTR (volatile uint8_t*)0x11000
#define AP_STARTUP_BSP_ID_PTR (volatile uint8_t*)0x11008
#define AP_STARTUP_BSP_SPINLOCK_PTR (volatile uint8_t*)0x11010

//
// Each core will have 512 bytes of stack space during bootup
// until a new 2k stack gets allocated in C entry code.
//
#define AP_STARTUP_STACK_POOL_BASE (void*)0x18000
#define AP_STARTUP_STACK_POOL_TOP (void*)0x70000
#define AP_STARTUP_STACK_POOL_PAGE_COUNT 8
#define AP_STARTUP_STACK_POOL_STACK_SIZE 512

EXTERN_C __PRIVILEGED_CODE void __ap_startup_asm();
EXTERN_C __PRIVILEGED_CODE void apStartupEntryC(int apicid);
void apStartupEntryLowered();

void _acquireApStartupSpinlockFlag() {
*AP_STARTUP_BSP_SPINLOCK_PTR = 0;
}

void _releaseApStartupSpinlockFlag() {
*AP_STARTUP_BSP_SPINLOCK_PTR = 1;
}

/**
* @brief Prepares memory mappings for Application Processor (AP) startup.
*
* This function locks the necessary physical pages and sets up the memory mappings
* required for the AP startup process. It copies the AP startup assembly code
* to a 16-bit real mode address and maps the C entry point to a lower physical
* address. These mappings ensure that the AP can transition between real mode
* and protected mode during the boot sequence.
*
* Specifically, it:
* - Locks physical pages for AP startup code, stack, and runtime data.
* - Copies the startup assembly code to the real mode address.
* - Maps the C entry point and sets up synchronization pointers for tracking AP states.
*
* This setup allows AP cores to boot properly and transition to the system's SMP
* environment.
*/
void _prepareApStartupMemoryMappings() {
auto& globalPageFrameAllocator = paging::getGlobalPageFrameAllocator();

// Copy the AP startup code to a 16bit address
globalPageFrameAllocator.lockPhysicalPage(AP_STARTUP_ASM_ADDRESS);
globalPageFrameAllocator.lockPhysicalPage(AP_STARTUP_C_ENTRY_ADDRESS);
globalPageFrameAllocator.lockPhysicalPage((void*)AP_STARTUP_AP_RUNNING_COUNT_PTR);
globalPageFrameAllocator.lockPhysicalPage(AP_STARTUP_PAGE_TABLE_PTR_ADDRESS);

// Stack region top is 0x70000, 8 pages of 512 bytes of stack
globalPageFrameAllocator.lockPhysicalPages(AP_STARTUP_STACK_POOL_BASE, AP_STARTUP_STACK_POOL_PAGE_COUNT);

// Copy the startup assembly code to the real mode address
memcpy(AP_STARTUP_ASM_ADDRESS, (void*)__ap_startup_asm, PAGE_SIZE);

// Copy the C entry point address to a lower physical address
uint64_t apStartupCEntryAddress = (uint64_t)apStartupEntryC;
memcpy(AP_STARTUP_C_ENTRY_ADDRESS, &apStartupCEntryAddress, sizeof(uint64_t));

// Copy the page table address to a lower physical address
// to setup paging during AP startup initialization sequence.
memcpy(AP_STARTUP_PAGE_TABLE_PTR_ADDRESS, paging::g_kernelRootPageTable, PAGE_SIZE);

// Configure startup flags
*AP_STARTUP_AP_RUNNING_COUNT_PTR = 0;
*AP_STARTUP_BSP_ID_PTR = 0;

// Get the BSP's Local APIC ID
__asm__ __volatile__ ("mov $1, %%eax; cpuid; shrl $24, %%ebx;" : "=b"(*AP_STARTUP_BSP_SPINLOCK_PTR) ::);
}

void initializeApCores() {
auto& acpiController = AcpiController::get();
Madt* apicTable = acpiController.getApicTable();

auto& sched = RRScheduler::get();

RUN_ELEVATED({
// Copy the necessary resources and data to the lower physical address
// accessible from the 16-bit real mode that APs are in at this point.
_prepareApStartupMemoryMappings();

// Acquire the BSP spinlock to manage the
// initialization sequence of AP cores.
_acquireApStartupSpinlockFlag();

// For each core, register it in the scheduler for SMP support
// and send the appropriate startup signal.
// *Note* starting at 1 because BSP_ID is 0
for (size_t cpu = 1; cpu < apicTable->getCpuCount(); cpu++) {
// Create a scheduler run queue for each detected core
sched.registerCpuCore(cpu);

// Get the APIC ID of the core
uint8_t apicid = apicTable->getLocalApicDescriptor(cpu).apicId;

// Send the startup IPIs
bootAndInitApCore(apicid);
}

// Let the AP cores continue on their own asynchronously
_releaseApStartupSpinlockFlag();
});
}

void bootAndInitApCore(uint8_t apicid) {
auto& lapic = Apic::getLocalApic();

lapic->sendIpi(apicid, 0x500);
msleep(20);

lapic->sendIpi(apicid, 0x600 | ((uint32_t)((uint64_t)AP_STARTUP_ASM_ADDRESS >> 12)));
msleep(20);
}

EXTERN_C __PRIVILEGED_CODE
void apStartupEntryC(int apicid) {
// Allocate a clean 2k kernel stack
void* apKernelStack = zallocPages(2);
uint64_t apKernelStackTop = (uint64_t)apKernelStack + 2 * PAGE_SIZE;
asm volatile ("mov %0, %%rsp" :: "r"(apKernelStackTop));

// Setup the GDT
initializeAndInstallGDT(apicid, (void*)apKernelStackTop);

// Initialize the default root kernel swapper task (this thread).
g_kernelSwapperTasks[apicid].state = ProcessState::RUNNING;
g_kernelSwapperTasks[apicid].pid = apicid;
zeromem(&g_kernelSwapperTasks[apicid].context, sizeof(CpuContext));
g_kernelSwapperTasks[apicid].context.rflags |= 0x200;
g_kernelSwapperTasks[apicid].elevated = 0;

// Set the current task in the per cpu region
__per_cpu_data.__cpu[apicid].currentTask = &g_kernelSwapperTasks[apicid];
__per_cpu_data.__cpu[apicid].currentTask->cpu = apicid;

// Install the existing IDT and enable interrupts
loadIdtr();
enableInterrupts();

// Enable syscalls
enableSyscallInterface();

// Update cr3
paging::setCurrentTopLevelPageTable(paging::g_kernelRootPageTable);

// Setup a clean 8k per-cpu stack
char* usermodeStack = (char*)zallocPages(8);
size_t usermodeStackSize = 8 * PAGE_SIZE;
size_t userStackTop = (uint64_t)(usermodeStack + usermodeStackSize);

__call_lowered_entry(apStartupEntryLowered, (void*)userStackTop);
while (1);
}

void apStartupEntryLowered() {
// Initialize core's LAPIC
Apic::initializeLocalApic();

// Calibrate apic timer tickrate to 100 milliseconds
KernelTimer::calibrateApicTimer(100);

// Start the kernel-wide APIC periodic timer
KernelTimer::startApicPeriodicTimer();

while (1);
}
28 changes: 28 additions & 0 deletions kernel/src/arch/x86/ap_startup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef AP_STARTUP_H
#define AP_STARTUP_H
#include "apic.h"
#include "msr.h"

/**
* @brief Boots and initializes all secondary Application Processor (AP) cores.
*
* This function handles the process of bringing up all secondary AP cores
* in the system. It sends the necessary startup signals to each AP core and
* initializes them to be part of the system's SMP (Symmetric Multiprocessing)
* environment, making them ready to execute tasks.
*/
void initializeApCores();

/**
* @brief Boots and initializes a secondary Application Processor (AP) core.
*
* This function handles the process of bringing up an AP core by sending the
* necessary startup signals and configuring its initial state. Once booted, the
* core is initialized and ready to handle tasks as part of the system's SMP (Symmetric
* Multiprocessing) environment.
*
* @param apicid The cpu ID of the AP core to be booted and initialized.
*/
void bootAndInitApCore(uint8_t apicid);

#endif
2 changes: 1 addition & 1 deletion kernel/src/arch/x86/asm/ap_startup.s
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
.equ stack_base, 0x18000 # Base of the stack region
.equ stack_size, 512 # Size of each stack (512 bytes)

.section .text
.section .ktext
.code16
__ap_startup_asm:
cli
Expand Down
12 changes: 4 additions & 8 deletions kernel/src/entry/kernel_entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
#include <arch/x86/apic_timer.h>
#include <arch/x86/gsfsbase.h>
#include <arch/x86/pat.h>
#include <arch/x86/ap_startup.h>
#include <sched/sched.h>
#include <syscall/syscalls.h>
#include <kelevate/kelevate.h>
#include <acpi/acpi_controller.h>
#include <kprint.h>
#include <time/ktime.h>
#include <kprint.h>

#include "tests/kernel_entry_tests.h"

Expand Down Expand Up @@ -177,13 +178,8 @@ void _kuser_entry() {
auto& sched = RRScheduler::get();
sched.init();

// If multiple cores are present, register all of them in the scheduler
if (acpiController.hasApicTable()) {
auto apicTable = acpiController.getApicTable();
size_t cpuCount = apicTable->getCpuCount();

kuPrint("==== Detected %lli CPUs ====\n", cpuCount);
}
// Bring up all available processor cores
initializeApCores();

#ifdef KE_TEST_MULTITHREADING
ke_test_multithreading();
Expand Down
Loading

0 comments on commit 23285ec

Please sign in to comment.