Implement HL::Cpu::StartAllProcessors to bootstrap all application processors
All checks were successful
Builds / ExectOS (i686, release) (push) Successful in 31s
Builds / ExectOS (amd64, release) (push) Successful in 33s
Builds / ExectOS (i686, debug) (push) Successful in 39s
Builds / ExectOS (amd64, debug) (push) Successful in 41s

This commit is contained in:
2026-05-17 17:33:35 +02:00
parent 8080e07281
commit 4e7113a079
3 changed files with 177 additions and 0 deletions

View File

@@ -15,6 +15,9 @@ The following is a consolidated list of available kernel parameters:
scheduler tick. Valid values include `LAPIC` (Local APIC Timer), `HPET` (High Precision Event Timer), and `PIT`
(Legacy Programmable Interval Timer). If this parameter is omitted, the kernel will autonomously probe the hardware
and select the most optimal clock source for the current CPU topology, (defaulting to the Local APIC on modern systems.
* **MAXCPUS**: Specifies the maximum number of logical processors the kernel is allowed to initialize and schedule.
Setting `MAXCPUS=1` explicitly disables Symmetric Multiprocessing (SMP), restricting execution exclusively to the Boot
Strap Processor (BSP) and ignoring all Application Processors (APs).
* **NOX2APIC**: Explicitly disables x2APIC support. When specified, the kernel bypasses hardware feature detection for
x2APIC and forces the use of the classic, memory-mapped (MMIO) xAPIC mode. This parameter is particularly useful for
troubleshooting interrupt routing issues or ensuring compatibility with specific hypervisors and legacy emulators.

View File

@@ -45,3 +45,175 @@ HL::Cpu::InitializeProcessor(VOID)
/* Set the APIC running level */
HL::RunLevel::SetRunLevel(KE::Processor::GetCurrentProcessorBlock()->RunLevel);
}
/**
* Wakes up and initializes all Application Processors (APs) and transitions them into the active kernel.
*
* @return This routine returns a status code indicating the success or failure of the operation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
HL::Cpu::StartAllProcessors(VOID)
{
ULONG CpuNumber, Index, MaxCpus, SipiVector, Timeout, TrampolinePages;
PVOID CpuStructures, TrampolineAddress, TrampolineCode;
ULONG_PTR AllocationSize, TrampolineCodeSize;
PPROCESSOR_START_BLOCK StartBlock;
PKPROCESSOR_BLOCK ProcessorBlock;
PACPI_SYSTEM_INFO SysInfo;
WCHAR ParameterValue[16];
XTSTATUS Status;
/* Determine the maximum number of CPUs to start */
HL::Acpi::GetSystemInformation(&SysInfo);
MaxCpus = SysInfo->CpuCount;
/* Check if user forced a specific CPU limit */
if(KE::BootInformation::GetKernelParameterValue(L"MAXCPUS", ParameterValue, 16) == STATUS_SUCCESS)
{
/* Convert string value to number */
Status = RTL::WideString::WideStringToNumber(ParameterValue, 0, &MaxCpus);
if(Status == STATUS_SUCCESS)
{
/* Ensure safe boundaries */
if(MaxCpus == 0)
{
/* Fallback to 1 CPU (BSP) */
MaxCpus = 1;
}
}
else
{
/* Failed to parse value, fallback to ACPI value */
MaxCpus = SysInfo->CpuCount;
}
}
/* Check if single core CPU or set a CPU limit */
if(MaxCpus == 1 || SysInfo->CpuCount == 1)
{
/* Single core CPU, return success */
return STATUS_SUCCESS;
}
/* Get trampoline information */
AR::ProcSup::GetTrampolineInformation(TrampolineApStartup, &TrampolineCode, &TrampolineCodeSize);
/* Verify trampoline information */
if(TrampolineCode == NULLPTR || TrampolineCodeSize == 0)
{
/* Failed to get trampoline information, return error */
return STATUS_UNSUCCESSFUL;
}
/* Compute trampoline memory allocation size (trampoline + processor start block + temporary stack) */
AllocationSize = TrampolineCodeSize + sizeof(PROCESSOR_START_BLOCK) + 512;
TrampolinePages = (ULONG)(ROUND_UP(AllocationSize, MM_PAGE_SIZE) / MM_PAGE_SIZE);
/* Allocate real mode memory for AP trampoline */
Status = MM::HardwarePool::AllocateRealModeMemory(TrampolinePages, &TrampolineAddress);
if(Status != STATUS_SUCCESS)
{
/* Failed to allocate memory, print error message and return error */
DebugPrint(L"Failed to allocate %lu pages for AP Trampoline!\n", TrampolinePages);
return Status;
}
/* Copy trampoline code to low memory */
RTL::Memory::CopyMemory(TrampolineAddress, TrampolineCode, TrampolineCodeSize);
/* Get start block address relative to trampoline address */
StartBlock = (PPROCESSOR_START_BLOCK)((PUCHAR)TrampolineAddress + TrampolineCodeSize);
/* Get SIPI vector */
SipiVector = (ULONG)((ULONG_PTR)TrampolineAddress >> 12);
/* Loop over all CPUs */
CpuNumber = 0;
for(Index = 0; Index < SysInfo->CpuCount; Index++)
{
/* Check if destination CPU is the BSP */
if(SysInfo->CpuInfo[Index].ApicId == HL::Pic::GetCpuApicId())
{
/* Skip current CPU */
continue;
}
/* Increment CPU number */
CpuNumber++;
/* Verify if the CPU limit has been reached */
if((CpuNumber) >= MaxCpus)
{
/* Maximum allowed CPUs reached, break the loop */
break;
}
/* Allocate memory for the processor structures (Stacks, GDT, and Processor Block) */
Status = MM::KernelPool::AllocateProcessorStructures(&CpuStructures);
if(Status != STATUS_SUCCESS)
{
/* Failed to allocate memory, unmap memory and return error */
MM::HardwarePool::UnmapHardwareMemory(TrampolineAddress, TrampolinePages, TRUE);
return Status;
}
/* Get ProcessorBlock and Stack address */
AR::ProcSup::InitializeProcessorStructures(CpuStructures, NULLPTR, NULLPTR, &ProcessorBlock,
&StartBlock->Stack, NULLPTR, NULLPTR);
/* Set processor number directly in the processor block */
ProcessorBlock->CpuNumber = CpuNumber;
/* Save processor block in the array */
KE::Processor::RegisterProcessorBlock(CpuNumber, ProcessorBlock);
/* Initialize processor start block */
StartBlock->Cr3 = AR::CpuFunc::ReadControlRegister(3);
StartBlock->Cr4 = AR::CpuFunc::ReadControlRegister(4);
StartBlock->EntryPoint = (PVOID)&KE::KernelInit::BootstrapApplicationProcessor;
StartBlock->ProcessorStructures = CpuStructures;
StartBlock->Started = FALSE;
/* Memory barrier */
AR::CpuFunc::MemoryBarrier();
/* Send INIT IPI and wait for 10ms */
HL::Pic::SendIpi(SysInfo->CpuInfo[Index].ApicId, 0, APIC_DM_INIT, APIC_DSH_Destination, APIC_TGM_EDGE);
HL::Timer::StallExecution(10000);
/* Send STARTUP IPI (SIPI) and wait for 200us */
HL::Pic::SendIpi(SysInfo->CpuInfo[Index].ApicId, SipiVector, APIC_DM_STARTUP, APIC_DSH_Destination, APIC_TGM_EDGE);
HL::Timer::StallExecution(200);
/* Send STARTUP IPI (SIPI) again */
HL::Pic::SendIpi(SysInfo->CpuInfo[Index].ApicId, SipiVector, APIC_DM_STARTUP, APIC_DSH_Destination, APIC_TGM_EDGE);
/* Wait until the processor has started or timeout expires */
Timeout = 0;
while(!StartBlock->Started && Timeout < 100000)
{
/* Yield processor and wait for 10us */
AR::CpuFunc::YieldProcessor();
HL::Timer::StallExecution(10);
Timeout++;
}
/* Check if the processor has not started */
if(!StartBlock->Started)
{
/* Free processor structures and unregister processor block */
MM::KernelPool::FreeProcessorStructures(CpuStructures);
KE::Processor::RegisterProcessorBlock(CpuNumber, NULLPTR);
/* Decrement the CPU counter back */
CpuNumber--;
}
}
/* Unmap trampoline memory and return success */
MM::HardwarePool::UnmapHardwareMemory(TrampolineAddress, TrampolinePages, TRUE);
return STATUS_SUCCESS;
}

View File

@@ -19,8 +19,10 @@ namespace HL
{
private:
STATIC KAFFINITY ActiveProcessors;
public:
STATIC XTAPI VOID InitializeProcessor(VOID);
STATIC XTAPI XTSTATUS StartAllProcessors(VOID);
};
}