feat: Symmetric Multiprocessing, text editor improvements, merge doom libc, implement math functions
This commit is contained in:
@@ -64,6 +64,27 @@ namespace Hal {
|
||||
<< " max LVT=" << base::dec << (uint64_t)((version >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
void InitializeAP() {
|
||||
if (!g_apicBase) return;
|
||||
|
||||
// Re-assert APIC Global Enable in MSR
|
||||
uint64_t msrValue = ReadMSR(MSR_APIC_BASE);
|
||||
if (!(msrValue & (1ULL << 11))) {
|
||||
msrValue |= (1ULL << 11);
|
||||
WriteMSR(MSR_APIC_BASE, msrValue);
|
||||
}
|
||||
|
||||
// Enable APIC software enable + set spurious vector
|
||||
uint32_t svr = ReadRegister(REG_SPURIOUS);
|
||||
svr |= (1 << 8);
|
||||
svr = (svr & 0xFFFFFF00) | SPURIOUS_VECTOR;
|
||||
WriteRegister(REG_SPURIOUS, svr);
|
||||
|
||||
// Accept all interrupts
|
||||
WriteRegister(REG_TPR, 0);
|
||||
// No log here -- APs boot in parallel and the BSP logs a summary.
|
||||
}
|
||||
|
||||
void Reinitialize() {
|
||||
if (!g_apicBase) return;
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ namespace Hal {
|
||||
|
||||
void Initialize(uint64_t apicBasePhys);
|
||||
|
||||
// Initialize the Local APIC on an AP.
|
||||
// Uses the same MMIO base as the BSP (each CPU's APIC is at the same
|
||||
// physical address but routes to its own hardware).
|
||||
void InitializeAP();
|
||||
|
||||
// Re-enable the Local APIC after S3 resume.
|
||||
// The MMIO mapping and base address survive (they're in page tables / RAM).
|
||||
// Only the SVR and TPR hardware registers need reprogramming.
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace Hal {
|
||||
IoApic::RouteIrq(IRQ_KEYBOARD, IRQ_VECTOR_BASE + IRQ_KEYBOARD, bspApicId);
|
||||
IoApic::RouteIrq(IRQ_MOUSE, IRQ_VECTOR_BASE + IRQ_MOUSE, bspApicId);
|
||||
|
||||
// Step 8: Enable interrupts
|
||||
asm volatile("sti");
|
||||
// Note: sti is NOT called here. The caller (Main.cpp) enables
|
||||
// interrupts after setting up per-CPU GS base for SMP/SWAPGS.
|
||||
|
||||
KernelLogStream(OK, "APIC") << "APIC subsystem initialized, interrupts enabled";
|
||||
KernelLogStream(OK, "APIC") << "APIC subsystem initialized";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,10 +42,16 @@ namespace Hal {
|
||||
|
||||
// C linkage dispatch function called from assembly stubs
|
||||
extern "C" void HalIrqDispatch(uint64_t irqNumber) {
|
||||
// Send EOI BEFORE calling the handler. The handler may context-switch
|
||||
// (via Tick -> Schedule -> SchedContextSwitch) and never return here.
|
||||
// If EOI is deferred until after the handler, the LAPIC timer vector
|
||||
// stays masked on this CPU -- no more timer interrupts, no more
|
||||
// scheduling, and the system eventually locks up.
|
||||
// This is safe because the ISR runs with IF=0; the LAPIC won't
|
||||
// deliver a new interrupt until iretq re-enables them.
|
||||
Hal::LocalApic::SendEOI();
|
||||
|
||||
if (irqNumber < Hal::IRQ_COUNT && Hal::g_irqHandlers[irqNumber] != nullptr) {
|
||||
Hal::g_irqHandlers[irqNumber]((uint8_t)irqNumber);
|
||||
}
|
||||
|
||||
// Send End of Interrupt to the Local APIC
|
||||
Hal::LocalApic::SendEOI();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
;
|
||||
; IsrStubs.asm
|
||||
; Hardware interrupt (IRQ) entry stubs for APIC
|
||||
; Copyright (c) 2025 Daniel Hammer
|
||||
; Hardware interrupt (IRQ) entry stubs for APIC (SMP-aware with SWAPGS)
|
||||
; Copyright (c) 2025-2026 Daniel Hammer
|
||||
;
|
||||
|
||||
[bits 64]
|
||||
@@ -10,18 +10,27 @@ section .text
|
||||
; External C++ handler function
|
||||
extern HalIrqDispatch
|
||||
|
||||
; Macro to define an IRQ stub
|
||||
; Each stub pushes the IRQ number and jumps to the common handler
|
||||
; ====================================================================
|
||||
; IRQ stub macro with SWAPGS support
|
||||
; Each stub checks the saved CS to determine if we came from user mode.
|
||||
; If so, SWAPGS is executed to switch GS to per-CPU data.
|
||||
; ====================================================================
|
||||
%macro IRQ_STUB 1
|
||||
global IrqStub%1
|
||||
IrqStub%1:
|
||||
push rax ; Save rax (we use it for the IRQ number)
|
||||
mov rax, %1 ; IRQ number
|
||||
cmp qword [rsp + 8], 0x08 ; check saved CS: kernel?
|
||||
je %%from_kernel
|
||||
swapgs ; from user mode: swap to kernel GS
|
||||
%%from_kernel:
|
||||
push rax ; save rax (used for IRQ number)
|
||||
mov rax, %1 ; IRQ number
|
||||
jmp IrqCommon
|
||||
%endmacro
|
||||
|
||||
; ====================================================================
|
||||
; Common IRQ handler: saves all general-purpose registers,
|
||||
; calls the C++ dispatch function, restores registers, and returns.
|
||||
; ====================================================================
|
||||
IrqCommon:
|
||||
; rax is already on the stack and holds the IRQ number
|
||||
push rcx
|
||||
@@ -63,10 +72,17 @@ IrqCommon:
|
||||
pop rcx
|
||||
pop rax
|
||||
|
||||
; SWAPGS back if returning to user mode
|
||||
cmp qword [rsp + 8], 0x08 ; check saved CS: kernel?
|
||||
je .from_kernel_exit
|
||||
swapgs ; returning to user mode: restore user GS
|
||||
.from_kernel_exit:
|
||||
iretq
|
||||
|
||||
; ====================================================================
|
||||
; Define stubs for IRQs 0..47 (vectors 32..79)
|
||||
; 0-23: legacy ISA IRQs via IOAPIC, 24-47: MSI vectors
|
||||
; ====================================================================
|
||||
IRQ_STUB 0
|
||||
IRQ_STUB 1
|
||||
IRQ_STUB 2
|
||||
@@ -121,7 +137,9 @@ global IrqStubSpurious
|
||||
IrqStubSpurious:
|
||||
iretq
|
||||
|
||||
; ====================================================================
|
||||
; Export the stub table for C++ to reference
|
||||
; ====================================================================
|
||||
section .data
|
||||
global IrqStubTable
|
||||
IrqStubTable:
|
||||
|
||||
+10
-1
@@ -73,10 +73,15 @@ namespace Hal {
|
||||
__attribute__((interrupt)) void ExceptionHandler(System::PanicFrame* frame)
|
||||
{
|
||||
uint64_t cs = GetExceptionCS(i, frame);
|
||||
bool fromUser = (cs & 3) == 3;
|
||||
|
||||
// SWAPGS: if from user mode, GS base is user-defined.
|
||||
// Swap to kernel per-CPU GS base so scheduler calls work.
|
||||
if (fromUser) asm volatile("swapgs");
|
||||
|
||||
// If the fault originated in user-mode (ring 3), kill the process
|
||||
// instead of panicking the entire system.
|
||||
if ((cs & 3) == 3 && Sched::GetCurrentPid() >= 0) {
|
||||
if (fromUser && Sched::GetCurrentPid() >= 0) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
Kt::KernelLogStream(Kt::ERROR, "Exception")
|
||||
<< ExceptionStrings[i] << " in process \""
|
||||
@@ -88,6 +93,10 @@ namespace Hal {
|
||||
frame->InterruptVector = i;
|
||||
Panic(ExceptionStrings[i], frame);
|
||||
}
|
||||
|
||||
// Unreachable in practice (user faults exit, kernel faults panic),
|
||||
// but balance the SWAPGS for correctness.
|
||||
if (fromUser) asm volatile("swapgs");
|
||||
}
|
||||
|
||||
void LoadIDT(IDTRStruct& idtr) {
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* SmpBoot.cpp
|
||||
* Symmetric Multiprocessing bootstrap and AP entry
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "SmpBoot.hpp"
|
||||
#include <Hal/Apic/Apic.hpp>
|
||||
#include <Hal/Apic/Interrupts.hpp>
|
||||
#include <Hal/IDT.hpp>
|
||||
#include <Hal/MSR.hpp>
|
||||
#include <Hal/Cpu.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Timekeeping/ApicTimer.hpp>
|
||||
#include <limine.h>
|
||||
|
||||
// Defined in Platform/Limine.hpp (included only by Main.cpp to avoid
|
||||
// duplicating the LIMINE_BASE_REVISION tag).
|
||||
extern volatile limine_mp_request mp_request;
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
// Verify assembly offsets match struct layout
|
||||
static_assert(__builtin_offsetof(Smp::CpuData, selfPtr) == CPUDATA_SELF_PTR, "CpuData offset mismatch: selfPtr");
|
||||
static_assert(__builtin_offsetof(Smp::CpuData, kernelRsp) == CPUDATA_KERNEL_RSP, "CpuData offset mismatch: kernelRsp");
|
||||
static_assert(__builtin_offsetof(Smp::CpuData, userRspScratch)== CPUDATA_USER_RSP, "CpuData offset mismatch: userRspScratch");
|
||||
static_assert(__builtin_offsetof(Smp::CpuData, currentSlot) == CPUDATA_CURRENT_SLOT, "CpuData offset mismatch: currentSlot");
|
||||
|
||||
extern "C" void SyscallEntry();
|
||||
|
||||
// Assembly helpers (GDT.asm)
|
||||
extern "C" void LoadGDT(Hal::GDTPointer* ptr);
|
||||
extern "C" void ReloadSegments();
|
||||
extern "C" void LoadTR();
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Smp {
|
||||
|
||||
static CpuData g_cpus[MaxCPUs];
|
||||
static int g_cpuCount = 0;
|
||||
|
||||
CpuData* GetCpuData(int index) {
|
||||
if (index < 0 || index >= g_cpuCount) return nullptr;
|
||||
return &g_cpus[index];
|
||||
}
|
||||
|
||||
int GetCpuCount() {
|
||||
return g_cpuCount;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Per-CPU GDT/TSS setup
|
||||
// ====================================================================
|
||||
|
||||
static void SetupPerCpuGdtTss(CpuData& cpu) {
|
||||
// Zero the TSS
|
||||
memset(&cpu.cpuTss, 0, sizeof(Hal::TSS64));
|
||||
cpu.cpuTss.iopbOffset = sizeof(Hal::TSS64);
|
||||
|
||||
// Copy the standard GDT layout
|
||||
cpu.cpuGdt = {
|
||||
{0xFFFF, 0, 0, 0x00, 0x00, 0}, // 0x00 Null
|
||||
{0xFFFF, 0, 0, 0x9A, 0xA0, 0}, // 0x08 KernelCode
|
||||
{0xFFFF, 0, 0, 0x92, 0xA0, 0}, // 0x10 KernelData
|
||||
{0xFFFF, 0, 0, 0xF2, 0xA0, 0}, // 0x18 UserData
|
||||
{0xFFFF, 0, 0, 0xFA, 0xA0, 0}, // 0x20 UserCode
|
||||
{0, 0, 0, 0, 0, 0}, // 0x28 TSS low
|
||||
{0, 0, 0, 0, 0, 0}, // 0x30 TSS high
|
||||
};
|
||||
|
||||
// Encode the 16-byte TSS descriptor
|
||||
uint64_t base = (uint64_t)&cpu.cpuTss;
|
||||
uint32_t limit = sizeof(Hal::TSS64) - 1;
|
||||
|
||||
// Low 8 bytes
|
||||
cpu.cpuGdt.TSS.LimitLow = limit & 0xFFFF;
|
||||
cpu.cpuGdt.TSS.BaseLow = base & 0xFFFF;
|
||||
cpu.cpuGdt.TSS.BaseMiddle = (base >> 16) & 0xFF;
|
||||
cpu.cpuGdt.TSS.AccessByte = 0x89; // Present, 64-bit TSS Available
|
||||
cpu.cpuGdt.TSS.GranularityByte = (limit >> 16) & 0x0F;
|
||||
cpu.cpuGdt.TSS.BaseHigh = (base >> 24) & 0xFF;
|
||||
|
||||
// High 8 bytes (base[63:32])
|
||||
uint32_t baseUpper = (uint32_t)(base >> 32);
|
||||
cpu.cpuGdt.TSSHigh.LimitLow = baseUpper & 0xFFFF;
|
||||
cpu.cpuGdt.TSSHigh.BaseLow = (baseUpper >> 16) & 0xFFFF;
|
||||
cpu.cpuGdt.TSSHigh.BaseMiddle = 0;
|
||||
cpu.cpuGdt.TSSHigh.AccessByte = 0;
|
||||
cpu.cpuGdt.TSSHigh.GranularityByte = 0;
|
||||
cpu.cpuGdt.TSSHigh.BaseHigh = 0;
|
||||
|
||||
cpu.tss = &cpu.cpuTss;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Set GS base MSRs for per-CPU data
|
||||
// ====================================================================
|
||||
|
||||
static constexpr uint32_t IA32_GS_BASE = 0xC0000101;
|
||||
static constexpr uint32_t IA32_KERNEL_GS_BASE = 0xC0000102;
|
||||
|
||||
static void SetGSBase(CpuData* cpu) {
|
||||
// In kernel mode, GSBASE = per-CPU data pointer.
|
||||
// KernelGSBASE = 0 (user GS base, swapped in on swapgs).
|
||||
Hal::WriteMSR(IA32_GS_BASE, (uint64_t)cpu);
|
||||
Hal::WriteMSR(IA32_KERNEL_GS_BASE, 0);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// BSP initialization
|
||||
// ====================================================================
|
||||
|
||||
void InitBsp() {
|
||||
memset(g_cpus, 0, sizeof(g_cpus));
|
||||
|
||||
CpuData& bsp = g_cpus[0];
|
||||
bsp.selfPtr = (uint64_t)&bsp;
|
||||
bsp.cpuIndex = 0;
|
||||
bsp.lapicId = Hal::LocalApic::GetId();
|
||||
bsp.currentSlot = -1;
|
||||
bsp.started = true;
|
||||
|
||||
// BSP uses the global TSS (already set up in PrepareGDT)
|
||||
bsp.tss = &Hal::g_tss;
|
||||
|
||||
// Set GS base for BSP
|
||||
SetGSBase(&bsp);
|
||||
|
||||
g_cpuCount = 1;
|
||||
|
||||
KernelLogStream(OK, "SMP") << "BSP initialized (LAPIC ID " << (uint64_t)bsp.lapicId << ")";
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// AP entry point
|
||||
// Called by Limine when goto_address is written.
|
||||
// RDI = pointer to limine_mp_info for this CPU.
|
||||
// Runs on a 64KiB Limine-provided stack.
|
||||
// ====================================================================
|
||||
|
||||
static void ApEntry(limine_mp_info* info) {
|
||||
// Find our CpuData (stored in extra_argument by BootAPs)
|
||||
CpuData* cpu = (CpuData*)info->extra_argument;
|
||||
|
||||
// --- Load per-CPU GDT ---
|
||||
Hal::GDTPointer gdtPtr {
|
||||
.Size = sizeof(Hal::BasicGDT) - 1,
|
||||
.GDTAddress = (uint64_t)&cpu->cpuGdt,
|
||||
};
|
||||
LoadGDT(&gdtPtr);
|
||||
ReloadSegments();
|
||||
|
||||
// --- Load TSS ---
|
||||
LoadTR();
|
||||
|
||||
// --- Load shared IDT ---
|
||||
Hal::IDTReload();
|
||||
|
||||
// --- Switch to kernel page tables ---
|
||||
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||
|
||||
// --- Enable SSE ---
|
||||
Hal::EnableSSE();
|
||||
|
||||
// --- Set GS base ---
|
||||
SetGSBase(cpu);
|
||||
|
||||
// --- Initialize local APIC ---
|
||||
Hal::LocalApic::InitializeAP();
|
||||
|
||||
// --- Program SYSCALL MSRs ---
|
||||
uint64_t efer = Hal::ReadMSR(Hal::IA32_EFER);
|
||||
efer |= 1; // SCE
|
||||
Hal::WriteMSR(Hal::IA32_EFER, efer);
|
||||
|
||||
uint64_t star = (0x0010ULL << 48) | (0x0008ULL << 32);
|
||||
Hal::WriteMSR(Hal::IA32_STAR, star);
|
||||
Hal::WriteMSR(Hal::IA32_LSTAR, (uint64_t)SyscallEntry);
|
||||
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
||||
|
||||
// --- Program PAT (entry 1 = WC) ---
|
||||
Hal::InitializePAT();
|
||||
|
||||
// --- Calibrate and start APIC timer ---
|
||||
Timekeeping::ApicTimerInitializeAP();
|
||||
|
||||
// --- Signal that we are online ---
|
||||
cpu->started = true;
|
||||
|
||||
// --- Enable interrupts and enter idle loop ---
|
||||
asm volatile("sti");
|
||||
|
||||
for (;;) {
|
||||
asm volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Boot all APs
|
||||
// ====================================================================
|
||||
|
||||
void BootAPs() {
|
||||
if (mp_request.response == nullptr) {
|
||||
KernelLogStream(WARNING, "SMP") << "No MP response from bootloader - single CPU mode";
|
||||
return;
|
||||
}
|
||||
|
||||
auto* resp = mp_request.response;
|
||||
uint64_t cpuCount = resp->cpu_count;
|
||||
|
||||
KernelLogStream(INFO, "SMP") << "Bootloader reports " << cpuCount << " CPU(s), BSP LAPIC ID "
|
||||
<< (uint64_t)resp->bsp_lapic_id;
|
||||
|
||||
if (cpuCount <= 1) {
|
||||
KernelLogStream(INFO, "SMP") << "Single CPU system - no APs to boot";
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpuCount > (uint64_t)MaxCPUs) {
|
||||
KernelLogStream(WARNING, "SMP") << "Clamping CPU count from " << cpuCount << " to " << (uint64_t)MaxCPUs;
|
||||
cpuCount = MaxCPUs;
|
||||
}
|
||||
|
||||
// Prepare all APs, then wake them all at once. This is safe because:
|
||||
// - APs use BSP's timer calibration (no PIT contention)
|
||||
// - APs don't log (no terminal contention)
|
||||
// - Each AP's init is purely local (GDT, TSS, APIC, MSRs)
|
||||
int apIndex = 1; // BSP is index 0
|
||||
for (uint64_t i = 0; i < cpuCount; i++) {
|
||||
limine_mp_info* info = resp->cpus[i];
|
||||
|
||||
if (info->lapic_id == resp->bsp_lapic_id) continue;
|
||||
if (apIndex >= MaxCPUs) break;
|
||||
|
||||
CpuData& ap = g_cpus[apIndex];
|
||||
ap.selfPtr = (uint64_t)≈
|
||||
ap.cpuIndex = apIndex;
|
||||
ap.lapicId = info->lapic_id;
|
||||
ap.currentSlot = -1;
|
||||
ap.started = false;
|
||||
|
||||
SetupPerCpuGdtTss(ap);
|
||||
info->extra_argument = (uint64_t)≈
|
||||
|
||||
// Wake this AP (it runs ApEntry in parallel with other APs)
|
||||
__atomic_store_n(&info->goto_address, (limine_goto_address)ApEntry, __ATOMIC_SEQ_CST);
|
||||
|
||||
apIndex++;
|
||||
}
|
||||
|
||||
g_cpuCount = apIndex;
|
||||
|
||||
// Wait for all APs to come online
|
||||
for (int i = 1; i < g_cpuCount; i++) {
|
||||
volatile bool* flag = &g_cpus[i].started;
|
||||
uint64_t timeout = 100000000;
|
||||
while (!*flag && timeout > 0) {
|
||||
asm volatile("pause");
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (!*flag) {
|
||||
KernelLogStream(ERROR, "SMP") << "AP " << i << " (LAPIC "
|
||||
<< (uint64_t)g_cpus[i].lapicId << ") failed to start";
|
||||
}
|
||||
}
|
||||
|
||||
// Count how many actually started
|
||||
int onlineCount = 1; // BSP
|
||||
for (int i = 1; i < g_cpuCount; i++) {
|
||||
if (g_cpus[i].started) onlineCount++;
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "SMP") << onlineCount << " CPU(s) online";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SmpBoot.hpp
|
||||
* Symmetric Multiprocessing bootstrap and per-CPU data
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Hal/GDT.hpp>
|
||||
|
||||
// ====================================================================
|
||||
// Assembly-visible offsets into CpuData
|
||||
// These MUST match the struct layout below. Verified by static_assert
|
||||
// in SmpBoot.cpp.
|
||||
// ====================================================================
|
||||
#define CPUDATA_SELF_PTR 0
|
||||
#define CPUDATA_KERNEL_RSP 8
|
||||
#define CPUDATA_USER_RSP 16
|
||||
#define CPUDATA_CURRENT_SLOT 24
|
||||
|
||||
namespace Smp {
|
||||
|
||||
static constexpr int MaxCPUs = 64;
|
||||
|
||||
struct CpuData {
|
||||
// === Fields accessed by assembly (offsets defined above) ===
|
||||
uint64_t selfPtr; // offset 0: pointer to self
|
||||
uint64_t kernelRsp; // offset 8: kernel stack top for SYSCALL
|
||||
uint64_t userRspScratch; // offset 16: scratch for saving user RSP
|
||||
int32_t currentSlot; // offset 24: process table slot, -1 = idle
|
||||
int32_t _pad0; // offset 28: padding
|
||||
|
||||
// === C++-only fields ===
|
||||
int cpuIndex; // 0-based CPU index
|
||||
uint32_t lapicId; // local APIC ID
|
||||
uint64_t idleSavedRsp; // RSP saved when switching from idle to process
|
||||
volatile bool started; // set by AP after init is complete
|
||||
|
||||
Hal::TSS64* tss; // pointer to this CPU's TSS
|
||||
|
||||
// Per-CPU GDT and TSS (APs use these; BSP uses globals)
|
||||
Hal::BasicGDT cpuGdt __attribute__((aligned(16)));
|
||||
Hal::TSS64 cpuTss __attribute__((aligned(16)));
|
||||
};
|
||||
|
||||
// Get the current CPU's data (reads gs:0 which holds the self-pointer)
|
||||
inline CpuData* GetCurrentCpuData() {
|
||||
CpuData* ptr;
|
||||
asm volatile("mov %%gs:0, %0" : "=r"(ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Get CPU data by index
|
||||
CpuData* GetCpuData(int index);
|
||||
|
||||
// Number of online CPUs
|
||||
int GetCpuCount();
|
||||
|
||||
// Initialize BSP per-CPU data (call before interrupts are enabled)
|
||||
void InitBsp();
|
||||
|
||||
// Boot all Application Processors (call after all subsystems ready)
|
||||
void BootAPs();
|
||||
}
|
||||
@@ -1,28 +1,34 @@
|
||||
;
|
||||
; SyscallEntry.asm
|
||||
; SYSCALL/SYSRET entry point and user-mode transition
|
||||
; Copyright (c) 2025 Daniel Hammer
|
||||
; SYSCALL/SYSRET entry point and user-mode transition (SMP-aware)
|
||||
; Copyright (c) 2025-2026 Daniel Hammer
|
||||
;
|
||||
|
||||
[bits 64]
|
||||
section .text
|
||||
|
||||
extern SyscallDispatch
|
||||
extern g_kernelRsp
|
||||
|
||||
; ============================================================
|
||||
; SyscallEntry — called by the SYSCALL instruction
|
||||
; ====================================================================
|
||||
; Per-CPU data offsets (must match CpuData in SmpBoot.hpp)
|
||||
; ====================================================================
|
||||
%define CPUDATA_KERNEL_RSP 8
|
||||
%define CPUDATA_USER_RSP 16
|
||||
|
||||
; ====================================================================
|
||||
; SyscallEntry -- called by the SYSCALL instruction
|
||||
; RCX = user RIP, R11 = user RFLAGS, RAX = syscall number
|
||||
; Args: RDI, RSI, RDX, R10, R8, R9
|
||||
; Interrupts are masked (FMASK clears IF)
|
||||
; ============================================================
|
||||
; ====================================================================
|
||||
global SyscallEntry
|
||||
SyscallEntry:
|
||||
mov [rel g_userRsp], rsp ; stash user RSP
|
||||
mov rsp, [rel g_kernelRsp] ; switch to kernel stack
|
||||
swapgs ; GS now points to per-CPU CpuData
|
||||
mov [gs:CPUDATA_USER_RSP], rsp ; save user RSP to per-CPU scratch
|
||||
mov rsp, [gs:CPUDATA_KERNEL_RSP] ; switch to per-CPU kernel stack
|
||||
|
||||
; Build SyscallFrame on kernel stack (push order matches struct)
|
||||
push qword [rel g_userRsp] ; user_rsp
|
||||
push qword [gs:CPUDATA_USER_RSP] ; user_rsp
|
||||
push rcx ; user_rip
|
||||
push r11 ; user_rflags
|
||||
push rax ; syscall_nr
|
||||
@@ -61,13 +67,14 @@ SyscallEntry:
|
||||
pop rcx ; user RIP
|
||||
pop rsp ; user RSP
|
||||
|
||||
swapgs ; restore user GS
|
||||
o64 sysret
|
||||
|
||||
; ============================================================
|
||||
; JumpToUserMode — initial transition to ring 3 via IRETQ
|
||||
; ====================================================================
|
||||
; JumpToUserMode -- initial transition to ring 3 via IRETQ
|
||||
; RDI = user RIP (entry point)
|
||||
; RSI = user RSP (top of user stack)
|
||||
; ============================================================
|
||||
; ====================================================================
|
||||
global JumpToUserMode
|
||||
JumpToUserMode:
|
||||
mov ax, 0x1B ; UserData | RPL3
|
||||
@@ -79,11 +86,5 @@ JumpToUserMode:
|
||||
push 0x202 ; RFLAGS (IF=1)
|
||||
push 0x23 ; CS = UserCode | RPL3
|
||||
push rdi ; RIP = entry point
|
||||
swapgs ; switch from kernel GS to user GS
|
||||
iretq
|
||||
|
||||
; ============================================================
|
||||
; BSS: scratch space for user RSP save
|
||||
; ============================================================
|
||||
section .bss
|
||||
global g_userRsp
|
||||
g_userRsp: resq 1
|
||||
|
||||
Reference in New Issue
Block a user