Files
exectos/xtoskrnl/hl/x86/cpu.cc
Aiken Harris 4e7113a079
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
Implement HL::Cpu::StartAllProcessors to bootstrap all application processors
2026-05-17 17:33:35 +02:00

220 lines
7.5 KiB
C++

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/hl/x86/cpu.cc
* DESCRIPTION: HAL x86 (i686/AMD64) processor support
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include <xtos.hh>
/**
* Initializes the processor.
*
* @param CpuNumber
* Supplies the number of the CPU, that is being initialized.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
HL::Cpu::InitializeProcessor(VOID)
{
PKPROCESSOR_BLOCK ProcessorBlock;
KAFFINITY Affinity;
/* Get current processor block */
ProcessorBlock = KE::Processor::GetCurrentProcessorBlock();
/* Set initial stall factor, CPU number and mask interrupts */
ProcessorBlock->StallScaleFactor = INITIAL_STALL_FACTOR;
ProcessorBlock->Idr = 0xFFFFFFFF;
/* Set processor affinity */
Affinity = (KAFFINITY) 1 << ProcessorBlock->CpuNumber;
/* Apply affinity to a set of processors */
ActiveProcessors |= Affinity;
/* Initialize APIC for this processor */
HL::Pic::InitializePic();
/* 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;
}