From 5a92173586648d5a177eb176214eb77924c0a9ed Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Mon, 11 May 2026 00:07:21 +0200 Subject: [PATCH 01/32] Implement targeted IPI broadcasting using processor block array --- sdk/xtdk/amd64/ketypes.h | 2 + sdk/xtdk/i686/ketypes.h | 2 + xtoskrnl/hl/acpi.cc | 18 ++++++++ xtoskrnl/hl/x86/pic.cc | 74 +++++++++++++++---------------- xtoskrnl/includes/hl/acpi.hh | 1 + xtoskrnl/includes/ke/proc.hh | 7 +++ xtoskrnl/ke/amd64/proc.cc | 86 ++++++++++++++++++++++++++++++++++++ xtoskrnl/ke/data.cc | 6 +++ xtoskrnl/ke/i686/proc.cc | 86 ++++++++++++++++++++++++++++++++++++ 9 files changed, 244 insertions(+), 38 deletions(-) diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index 9ccc36f..5790d02 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -524,6 +524,8 @@ typedef struct _KPROCESSOR_BLOCK KAFFINITY SetMember; ULONG StallScaleFactor; UCHAR CpuNumber; + ULONG HardwareId; + VOLATILE BOOLEAN Started; PINTERRUPT_HANDLER InterruptDispatchTable[256]; } KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK; diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index 462082f..37a0d4d 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -483,6 +483,8 @@ typedef struct _KPROCESSOR_BLOCK KAFFINITY SetMember; ULONG StallScaleFactor; UCHAR CpuNumber; + ULONG HardwareId; + VOLATILE BOOLEAN Started; PINTERRUPT_HANDLER InterruptDispatchTable[256]; } KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK; diff --git a/xtoskrnl/hl/acpi.cc b/xtoskrnl/hl/acpi.cc index b0adceb..661d9d3 100644 --- a/xtoskrnl/hl/acpi.cc +++ b/xtoskrnl/hl/acpi.cc @@ -127,6 +127,24 @@ HL::Acpi::GetAcpiTimerInfo(OUT PACPI_TIMER_INFO *AcpiTimerInfo) } } +/** + * Gets the ACPI system information structure containing processor and topology data. + * + * @param SystemInfo + * Supplies a pointer to the memory area where the pointer to the system information structure will be stored. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +HL::Acpi::GetSystemInformation(OUT PACPI_SYSTEM_INFO *SystemInfo) +{ + /* Return a pointer to the ACPI system information */ + *SystemInfo = &HL::Acpi::SystemInfo; +} + /** * Performs an initialization of the ACPI subsystem. * diff --git a/xtoskrnl/hl/x86/pic.cc b/xtoskrnl/hl/x86/pic.cc index a2e7f9f..41b4737 100644 --- a/xtoskrnl/hl/x86/pic.cc +++ b/xtoskrnl/hl/x86/pic.cc @@ -402,6 +402,9 @@ HL::Pic::InitializeApic(VOID) WriteApicRegister(APIC_LDR, (1UL << CpuNumber) << 24); } + /* Report the APIC ID to the kernel logic */ + KE::Processor::RegisterHardwareId(GetCpuApicId()); + /* Configure the spurious interrupt vector */ SpuriousRegister.Long = ReadApicRegister(APIC_SIVR); SpuriousRegister.Vector = APIC_VECTOR_SPURIOUS; @@ -781,58 +784,53 @@ VOID HL::Pic::SendBroadcastIpi(IN ULONG Vector, IN BOOLEAN Self) { - APIC_COMMAND_REGISTER Register; + PKPROCESSOR_BLOCK CurrentProcessorBlock, TargetProcessorBlock; + PACPI_SYSTEM_INFO SysInfo; BOOLEAN Interrupts; + ULONG Index; - /* SMP not implemented */ - if(TRUE) + /* Get the current processor block */ + CurrentProcessorBlock = KE::Processor::GetCurrentProcessorBlock(); + if(CurrentProcessorBlock == NULLPTR) { - /* Check if IPI is addressed to the current CPU */ - if(Self) - { - /* Send IPI to the current CPU */ - SendSelfIpi(Vector); - return; - } - else - { - /* Nothing to do */ - return; - } + /* Processor block not available, return */ + return; } + /* Get the ACPI system information */ + HL::Acpi::GetSystemInformation(&SysInfo); + /* Check whether interrupts are enabled */ Interrupts = AR::CpuFunc::InterruptsEnabled(); /* Disable interrupts */ AR::CpuFunc::ClearInterruptFlag(); - /* Prepare the APIC command register */ - Register.LongLong = 0; - Register.DeliveryMode = APIC_DM_FIXED; - Register.DestinationShortHand = Self ? APIC_DSH_AllIncludingSelf : APIC_DSH_AllExclusingSelf; - Register.Level = 1; - Register.TriggerMode = APIC_TGM_EDGE; - Register.Vector = Vector; + /* Iterate over all logical CPUs */ + for(Index = 0; Index < SysInfo->CpuCount; Index++) + { + /* Retrieve the target processor block by its Logical CPU Number */ + TargetProcessorBlock = KE::Processor::GetProcessorBlock(Index); - /* Check current APIC mode */ - if(ApicMode == APIC_MODE_X2APIC) - { - /* In x2APIC mode, writing the full 64-bit value to the ICR MSR is sufficient */ - WriteApicRegister(APIC_ICR0, Register.LongLong); - } - else - { - /* Wait for the APIC to clear the delivery status */ - while((ReadApicRegister(APIC_ICR0) & 0x1000) != 0) + /* Only send to processors that exist and have successfully started */ + if(TargetProcessorBlock != NULLPTR && TargetProcessorBlock->Started) { - /* Yield the processor */ - AR::CpuFunc::YieldProcessor(); + /* Check if this processor originated the broadcast */ + if(TargetProcessorBlock->HardwareId == CurrentProcessorBlock->HardwareId) + { + /* Check if this is a self broadcast */ + if(Self) + { + /* Dispatch the IPI to the current processor */ + SendSelfIpi(Vector); + } + } + else + { + /* Dispatch the IPI to the target processor */ + SendIpi(TargetProcessorBlock->HardwareId, Vector); + } } - - /* In xAPIC compatibility mode, write the command to the ICR registers */ - WriteApicRegister(APIC_ICR1, Register.Long1); - WriteApicRegister(APIC_ICR0, Register.Long0); } /* Check whether interrupts need to be re-enabled */ diff --git a/xtoskrnl/includes/hl/acpi.hh b/xtoskrnl/includes/hl/acpi.hh index 4e1a246..a1bbce4 100644 --- a/xtoskrnl/includes/hl/acpi.hh +++ b/xtoskrnl/includes/hl/acpi.hh @@ -30,6 +30,7 @@ namespace HL STATIC XTAPI XTSTATUS GetAcpiTable(IN ULONG Signature, OUT PACPI_DESCRIPTION_HEADER *AcpiTable); STATIC XTAPI VOID GetAcpiTimerInfo(OUT PACPI_TIMER_INFO *AcpiTimerInfo); + STATIC XTAPI VOID GetSystemInformation(OUT PACPI_SYSTEM_INFO *SystemInfo); STATIC XTAPI XTSTATUS InitializeAcpi(VOID); STATIC XTAPI XTSTATUS InitializeAcpiSystemInformation(VOID); diff --git a/xtoskrnl/includes/ke/proc.hh b/xtoskrnl/includes/ke/proc.hh index 08ee47a..e9b63c9 100644 --- a/xtoskrnl/includes/ke/proc.hh +++ b/xtoskrnl/includes/ke/proc.hh @@ -17,11 +17,18 @@ namespace KE { class Processor { + private: + STATIC ULONG InstalledCpus; + STATIC PKPROCESSOR_BLOCK *ProcessorBlocks; + public: STATIC XTAPI PKPROCESSOR_BLOCK GetCurrentProcessorBlock(VOID); STATIC XTAPI PKPROCESSOR_CONTROL_BLOCK GetCurrentProcessorControlBlock(VOID); STATIC XTAPI ULONG GetCurrentProcessorNumber(VOID); STATIC XTAPI PKTHREAD GetCurrentThread(VOID); + STATIC XTAPI PKPROCESSOR_BLOCK GetProcessorBlock(IN ULONG CpuNumber); + STATIC XTAPI XTSTATUS InitializeProcessorStructures(IN ULONG CpuCount); + STATIC XTAPI VOID RegisterHardwareId(IN ULONG HardwareId); STATIC XTAPI VOID SaveProcessorState(OUT PKPROCESSOR_STATE CpuState); }; } diff --git a/xtoskrnl/ke/amd64/proc.cc b/xtoskrnl/ke/amd64/proc.cc index 3666888..7622b80 100644 --- a/xtoskrnl/ke/amd64/proc.cc +++ b/xtoskrnl/ke/amd64/proc.cc @@ -66,6 +66,92 @@ KE::Processor::GetCurrentThread(VOID) return (PKTHREAD)AR::CpuFunc::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Prcb.CurrentThread)); } +/** + * Gets the processor block for the specified processor number. + * + * @param CpuNumber + * Supplies the zero-indexed processor number. + * + * @return This routine returns a pointer to the processor block, or NULLPTR if invalid. + * + * @since XT 1.0 + */ +XTAPI +PKPROCESSOR_BLOCK +KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + { + /* Invalid CPU number, return NULLPTR */ + return NULLPTR; + } + + /* Return requested processor block */ + return ProcessorBlocks[CpuNumber]; +} + +/** + * Initializes the global processor structures by allocating an array of processor block pointers. + * + * @param CpuCount + * Supplies the total number of processors present in the system. + * + * @return This routine returns a status code indicating the success or failure of the allocation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +{ + XTSTATUS Status; + + /* Save number of CPUs installed */ + InstalledCpus = CpuCount; + + /* Allocate an array of pointers */ + Status = MM::Allocator::AllocatePool(NonPagedPool, + CpuCount * sizeof(PKPROCESSOR_BLOCK), + (PVOID*)&ProcessorBlocks); + if(Status != STATUS_SUCCESS) + { + /* Failed to allocate memory, return error */ + return Status; + } + + /* Zero the array initially */ + RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Registers the hardware APIC ID for the currently executing processor. + * + * @param ApicId + * Supplies the hardware APIC ID to register in the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterHardwareId(IN ULONG HardwareId) +{ + PKPROCESSOR_BLOCK CurrentBlock; + + /* Retrieve the processor block for the executing core */ + CurrentBlock = GetCurrentProcessorBlock(); + if(CurrentBlock != NULLPTR) + { + /* Register the hardware identifier for IPI targeting */ + CurrentBlock->HardwareId = HardwareId; + } +} + /** * Saves the current processor state. * diff --git a/xtoskrnl/ke/data.cc b/xtoskrnl/ke/data.cc index 1b2852c..9ae23c1 100644 --- a/xtoskrnl/ke/data.cc +++ b/xtoskrnl/ke/data.cc @@ -21,6 +21,12 @@ ETHREAD KE::KThread::InitialThread = {}; /* Kernel UBSAN active frame flag */ BOOLEAN KE::KUbsan::ActiveFrame = FALSE; +/* Total number of installed processors in the system */ +ULONG KE::Processor::InstalledCpus; + +/* Array of pointers to processor control blocks */ +PKPROCESSOR_BLOCK *KE::Processor::ProcessorBlocks; + /* Kernel shared data (KSD) */ PKSHARED_DATA KE::SharedData::KernelSharedData; diff --git a/xtoskrnl/ke/i686/proc.cc b/xtoskrnl/ke/i686/proc.cc index c405675..84f8f89 100644 --- a/xtoskrnl/ke/i686/proc.cc +++ b/xtoskrnl/ke/i686/proc.cc @@ -66,6 +66,92 @@ KE::Processor::GetCurrentThread(VOID) return (PKTHREAD)AR::CpuFunc::ReadFSDualWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Prcb.CurrentThread)); } +/** + * Gets the processor block for the specified processor number. + * + * @param CpuNumber + * Supplies the zero-indexed processor number. + * + * @return This routine returns a pointer to the processor block, or NULLPTR if invalid. + * + * @since XT 1.0 + */ +XTAPI +PKPROCESSOR_BLOCK +KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + { + /* Invalid CPU number, return NULLPTR */ + return NULLPTR; + } + + /* Return requested processor block */ + return ProcessorBlocks[CpuNumber]; +} + +/** + * Initializes the global processor structures by allocating an array of processor block pointers. + * + * @param CpuCount + * Supplies the total number of processors present in the system. + * + * @return This routine returns a status code indicating the success or failure of the allocation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +{ + XTSTATUS Status; + + /* Save number of CPUs installed */ + InstalledCpus = CpuCount; + + /* Allocate an array of pointers */ + Status = MM::Allocator::AllocatePool(NonPagedPool, + CpuCount * sizeof(PKPROCESSOR_BLOCK), + (PVOID*)&ProcessorBlocks); + if(Status != STATUS_SUCCESS) + { + /* Failed to allocate memory, return error */ + return Status; + } + + /* Zero the array initially */ + RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Registers the hardware APIC ID for the currently executing processor. + * + * @param ApicId + * Supplies the hardware APIC ID to register in the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterHardwareId(IN ULONG HardwareId) +{ + PKPROCESSOR_BLOCK CurrentBlock; + + /* Retrieve the processor block for the executing core */ + CurrentBlock = GetCurrentProcessorBlock(); + if(CurrentBlock != NULLPTR) + { + /* Register the hardware identifier for IPI targeting */ + CurrentBlock->HardwareId = HardwareId; + } +} + /** * Saves the current processor state. * From 06635ed014b484aad5f601adb925c909e0c4acb9 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Tue, 12 May 2026 19:35:06 +0200 Subject: [PATCH 02/32] Parameterize maximum allocation address in hardware pool and scan memory descriptors in reverse --- xtoskrnl/hl/acpi.cc | 2 +- xtoskrnl/includes/mm/hlpool.hh | 1 + xtoskrnl/mm/hlpool.cc | 16 ++++++++++------ xtoskrnl/mm/mmgr.cc | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/xtoskrnl/hl/acpi.cc b/xtoskrnl/hl/acpi.cc index 661d9d3..6fc4689 100644 --- a/xtoskrnl/hl/acpi.cc +++ b/xtoskrnl/hl/acpi.cc @@ -496,7 +496,7 @@ HL::Acpi::InitializeAcpiSystemStructure(VOID) PageCount = SIZE_TO_PAGES(CpuCount * sizeof(PROCESSOR_IDENTITY)); /* Allocate memory for CPU information */ - Status = MM::HardwarePool::AllocateHardwareMemory(PageCount, TRUE, &PhysicalAddress); + Status = MM::HardwarePool::AllocateHardwareMemory(PageCount, TRUE, MM_MAXIMUM_PHYSICAL_ADDRESS, &PhysicalAddress); if(Status != STATUS_SUCCESS) { /* Failed to allocate memory, return error */ diff --git a/xtoskrnl/includes/mm/hlpool.hh b/xtoskrnl/includes/mm/hlpool.hh index 007605c..de53a94 100644 --- a/xtoskrnl/includes/mm/hlpool.hh +++ b/xtoskrnl/includes/mm/hlpool.hh @@ -25,6 +25,7 @@ namespace MM public: STATIC XTAPI XTSTATUS AllocateHardwareMemory(IN PFN_NUMBER PageCount, IN BOOLEAN Aligned, + IN ULONGLONG MaximumAddress, OUT PPHYSICAL_ADDRESS Buffer); STATIC XTAPI XTSTATUS MapHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, IN PFN_NUMBER PageCount, diff --git a/xtoskrnl/mm/hlpool.cc b/xtoskrnl/mm/hlpool.cc index 2504f8b..847d6ea 100644 --- a/xtoskrnl/mm/hlpool.cc +++ b/xtoskrnl/mm/hlpool.cc @@ -18,6 +18,9 @@ * @param Aligned * Specifies whether allocated memory should be aligned to 64k boundary or not. * + * @param MaximumAddress + * Supplies the maximum acceptable physical address for the allocation. + * * @param Buffer * Supplies a buffer that receives the physical address. * @@ -29,18 +32,19 @@ XTAPI XTSTATUS MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, IN BOOLEAN Aligned, + IN ULONGLONG MaximumAddress, OUT PPHYSICAL_ADDRESS Buffer) { PLOADER_MEMORY_DESCRIPTOR Descriptor, ExtraDescriptor, HardwareDescriptor; + PLIST_ENTRY ListEntry, LoaderMemoryDescriptors; PFN_NUMBER Alignment, MaxPage; ULONGLONG PhysicalAddress; - PLIST_ENTRY ListEntry, LoaderMemoryDescriptors; /* Assume failure */ (*Buffer).QuadPart = 0; - /* Calculate maximum page address */ - MaxPage = MM_MAXIMUM_PHYSICAL_ADDRESS >> MM_PAGE_SHIFT; + /* Calculate maximum page address based on the requested limit */ + MaxPage = MaximumAddress >> MM_PAGE_SHIFT; /* Make sure there are at least 2 descriptors available */ if((UsedHardwareAllocationDescriptors + 2) > MM_HARDWARE_ALLOCATION_DESCRIPTORS) @@ -53,7 +57,7 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, LoaderMemoryDescriptors = KE::BootInformation::GetMemoryDescriptors(); /* Scan memory descriptors provided by the boot loader */ - ListEntry = LoaderMemoryDescriptors->Flink; + ListEntry = LoaderMemoryDescriptors->Blink; while(ListEntry != LoaderMemoryDescriptors) { Descriptor = CONTAIN_RECORD(ListEntry, LOADER_MEMORY_DESCRIPTOR, ListEntry); @@ -75,8 +79,8 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, } } - /* Move to next descriptor */ - ListEntry = ListEntry->Flink; + /* Move to previous descriptor */ + ListEntry = ListEntry->Blink; } /* Make sure we found a descriptor */ diff --git a/xtoskrnl/mm/mmgr.cc b/xtoskrnl/mm/mmgr.cc index 4f75947..ce5e4af 100644 --- a/xtoskrnl/mm/mmgr.cc +++ b/xtoskrnl/mm/mmgr.cc @@ -298,7 +298,7 @@ MM::Manager::MapKernelSharedData(VOID) XTSTATUS Status; /* Allocate one physical page from the hardware pool for the shared data */ - Status = MM::HardwarePool::AllocateHardwareMemory(1, FALSE, &PhysAddr); + Status = MM::HardwarePool::AllocateHardwareMemory(1, FALSE, MM_MAXIMUM_PHYSICAL_ADDRESS, &PhysAddr); if(Status != STATUS_SUCCESS) { /* Memory allocation failed, return error code */ From 58981e008716c9ecf897b15c3f15eb8e5223ccbc Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 09:58:13 +0200 Subject: [PATCH 03/32] Add MapVirtualAddress to memory manager paging interface --- xtoskrnl/includes/mm/amd64/paging.hh | 3 ++ xtoskrnl/includes/mm/i686/paging.hh | 3 ++ xtoskrnl/mm/amd64/paging.cc | 58 ++++++++++++++++++++++++++++ xtoskrnl/mm/i686/paging.cc | 56 +++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/xtoskrnl/includes/mm/amd64/paging.hh b/xtoskrnl/includes/mm/amd64/paging.hh index 5a39583..6f927bb 100644 --- a/xtoskrnl/includes/mm/amd64/paging.hh +++ b/xtoskrnl/includes/mm/amd64/paging.hh @@ -51,6 +51,9 @@ namespace MM STATIC XTAPI PVOID GetPxeVirtualAddress(IN PMMPXE PxePointer); STATIC XTAPI BOOLEAN GetXpaStatus(VOID); STATIC XTAPI VOID InitializePageMapSupport(VOID); + STATIC XTAPI XTSTATUS MapVirtualAddress(IN PVOID VirtualAddress, + IN PFN_NUMBER PageFrameNumber, + IN ULONGLONG Attributes); STATIC XTAPI BOOLEAN PteValid(IN PMMPTE PtePointer); STATIC XTAPI VOID SetNextEntry(IN PMMPTE Pte, IN ULONG_PTR Value); diff --git a/xtoskrnl/includes/mm/i686/paging.hh b/xtoskrnl/includes/mm/i686/paging.hh index 7487c06..3924111 100644 --- a/xtoskrnl/includes/mm/i686/paging.hh +++ b/xtoskrnl/includes/mm/i686/paging.hh @@ -47,6 +47,9 @@ namespace MM STATIC XTAPI PVOID GetPteVirtualAddress(IN PMMPTE PtePointer); STATIC XTAPI BOOLEAN GetXpaStatus(VOID); STATIC XTAPI VOID InitializePageMapSupport(VOID); + STATIC XTAPI XTSTATUS MapVirtualAddress(IN PVOID VirtualAddress, + IN PFN_NUMBER PageFrameNumber, + IN ULONGLONG Attributes); STATIC XTAPI BOOLEAN PteValid(IN PMMPTE PtePointer); STATIC XTAPI VOID SetNextEntry(IN PMMPTE Pte, IN ULONG_PTR Value); diff --git a/xtoskrnl/mm/amd64/paging.cc b/xtoskrnl/mm/amd64/paging.cc index a04e007..62bfcc8 100644 --- a/xtoskrnl/mm/amd64/paging.cc +++ b/xtoskrnl/mm/amd64/paging.cc @@ -97,6 +97,64 @@ MM::Paging::GetPxeVirtualAddress(IN PMMPXE PxePointer) return PmlRoutines->GetPxeVirtualAddress(PxePointer); } +/** + * Maps a specific virtual address to a specific physical page frame. + * + * @param VirtualAddress + * The virtual address to map. + * + * @param PageFrameNumber + * The physical frame number to back the virtual address. + * + * @param Attributes + * Specifies the attributes (protections, caching) to apply to the PTE. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +MM::Paging::MapVirtualAddress(IN PVOID VirtualAddress, + IN PFN_NUMBER PageFrameNumber, + IN ULONGLONG Attributes) +{ + MMPTE TemplatePte; + PMMPTE PointerPte; + + /* Initialize the template PTE */ + MM::Paging::ClearPte(&TemplatePte); + MM::Paging::SetPte(&TemplatePte, 0, Attributes | MM_PTE_CACHE_ENABLE); + + /* Check if XPA is enabled */ + if(MM::Paging::GetXpaStatus()) + { + /* Map Page 5-level Entry*/ + MM::Pte::MapP5E(VirtualAddress, VirtualAddress, (PMMP5E)&TemplatePte); + } + + /* Map PXE, PPE and PDE for the corresponding virtual address */ + MM::Pte::MapPXE(VirtualAddress, VirtualAddress, (PMMPXE)&TemplatePte); + MM::Pte::MapPPE(VirtualAddress, VirtualAddress, (PMMPPE)&TemplatePte); + MM::Pte::MapPDE(VirtualAddress, VirtualAddress, (PMMPDE)&TemplatePte); + + /* Get PTE address */ + PointerPte = MM::Paging::GetPteAddress(VirtualAddress); + + /* Initialize the template PTE */ + MM::Paging::ClearPte(&TemplatePte); + MM::Paging::SetPte(&TemplatePte, PageFrameNumber, Attributes); + + /* Write the PTE */ + MM::Paging::WritePte(PointerPte, TemplatePte); + + /* Flush the TLB to reflect the changes */ + MM::Paging::FlushTlb(); + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Fills a section of memory with zeroes like RtlZeroMemory(), but in more efficient way. * diff --git a/xtoskrnl/mm/i686/paging.cc b/xtoskrnl/mm/i686/paging.cc index ed85f18..1be0f18 100644 --- a/xtoskrnl/mm/i686/paging.cc +++ b/xtoskrnl/mm/i686/paging.cc @@ -25,6 +25,62 @@ MM::Paging::GetExtendedPhysicalAddressingStatus(VOID) return ((AR::CpuFunc::ReadControlRegister(4) & CR4_PAE) != 0) ? TRUE : FALSE; } +/** + * Maps a specific virtual address to a specific physical page frame (i686 specific). + * + * @param VirtualAddress + * The virtual address to map. + * + * @param PageFrameNumber + * The physical frame number to back the virtual address. + * + * @param Attributes + * Specifies the attributes (protections, caching) to apply to the PTE. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +MM::Paging::MapVirtualAddress(IN PVOID VirtualAddress, + IN PFN_NUMBER PageFrameNumber, + IN ULONGLONG Attributes) +{ + MMPTE TemplatePte; + PMMPTE PointerPte; + + /* Initialize the template PTE */ + MM::Paging::ClearPte(&TemplatePte); + MM::Paging::SetPte(&TemplatePte, 0, Attributes | MM_PTE_CACHE_ENABLE); + + /* Check if XPA is enabled */ + if(MM::Paging::GetXpaStatus()) + { + /* Map Page Directory Pointer Table */ + MM::Pte::MapPPE(VirtualAddress, VirtualAddress, (PMMPPE)&TemplatePte); + } + + /* Map Page Directory Entry */ + MM::Pte::MapPDE(VirtualAddress, VirtualAddress, (PMMPDE)&TemplatePte); + + /* Get PTE address */ + PointerPte = MM::Paging::GetPteAddress(VirtualAddress); + + /* Initialize the template PTE */ + MM::Paging::ClearPte(&TemplatePte); + MM::Paging::SetPte(&TemplatePte, PageFrameNumber, Attributes); + + /* Write the PTE */ + MM::Paging::WritePte(PointerPte, TemplatePte); + + /* Flush the TLB to reflect the changes */ + MM::Paging::FlushTlb(); + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Fills a section of memory with zeroes like RtlZeroMemory(), but in more efficient way. * From c8868ead47518d610c20d96566c675acddd76557 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 10:08:30 +0200 Subject: [PATCH 04/32] Add AllocateRealModeMemory routine for low memory allocations --- xtoskrnl/includes/mm/hlpool.hh | 2 ++ xtoskrnl/mm/hlpool.cc | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/xtoskrnl/includes/mm/hlpool.hh b/xtoskrnl/includes/mm/hlpool.hh index de53a94..41daac4 100644 --- a/xtoskrnl/includes/mm/hlpool.hh +++ b/xtoskrnl/includes/mm/hlpool.hh @@ -27,6 +27,8 @@ namespace MM IN BOOLEAN Aligned, IN ULONGLONG MaximumAddress, OUT PPHYSICAL_ADDRESS Buffer); + STATIC XTAPI XTSTATUS AllocateRealModeMemory(IN PFN_NUMBER PageCount, + OUT PVOID *MemoryAddress); STATIC XTAPI XTSTATUS MapHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, IN PFN_NUMBER PageCount, IN BOOLEAN FlushTlb, diff --git a/xtoskrnl/mm/hlpool.cc b/xtoskrnl/mm/hlpool.cc index 847d6ea..d35a776 100644 --- a/xtoskrnl/mm/hlpool.cc +++ b/xtoskrnl/mm/hlpool.cc @@ -4,6 +4,7 @@ * FILE: xtoskrnl/mm/hlpool.cc * DESCRIPTION: Hardware layer pool memory management * DEVELOPERS: Rafal Kupiec + * Aiken Harris */ #include @@ -146,6 +147,51 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, return STATUS_SUCCESS; } +/** + * Allocates a physical page in low memory (addressable in real-mode) and maps it into the virtual address space. + * + * @param TrampolineAddress + * Supplies a pointer to a variable that receives the identity-mapped virtual address of the allocated memory. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +MM::HardwarePool::AllocateRealModeMemory(IN PFN_NUMBER PageCount, + OUT PVOID *MemoryAddress) +{ + PHYSICAL_ADDRESS PhysicalAddress; + PFN_NUMBER PageFrameNumber; + PVOID VirtualAddress; + XTSTATUS Status; + + /* Allocate physical memory in first 1MB */ + Status = AllocateHardwareMemory(PageCount, TRUE, 0x100000, &PhysicalAddress); + if(Status != STATUS_SUCCESS) + { + /* Failed to allocate memory, return error */ + return Status; + } + + /* Calculate virtual address and page frame number */ + VirtualAddress = (PVOID)(ULONG_PTR)PhysicalAddress.QuadPart; + PageFrameNumber = PhysicalAddress.QuadPart >> MM_PAGE_SHIFT; + + /* Identity map the memory to the virtual address */ + Status = MM::Paging::MapVirtualAddress(VirtualAddress, PageFrameNumber, MM_PTE_EXECUTE_READWRITE); + if(Status != STATUS_SUCCESS) + { + /* Failed to map memory, return error */ + return Status; + } + + /* Set the trampoline virtual address and return success */ + *MemoryAddress = VirtualAddress; + return STATUS_SUCCESS; +} + /** * Maps physical address to the virtual memory area used by kernel hardware layer. * From 760e58f9939360e76bd81398b80531ff11008b2b Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 10:10:03 +0200 Subject: [PATCH 05/32] Correct doxygen parameter name --- xtoskrnl/mm/hlpool.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtoskrnl/mm/hlpool.cc b/xtoskrnl/mm/hlpool.cc index d35a776..7935c97 100644 --- a/xtoskrnl/mm/hlpool.cc +++ b/xtoskrnl/mm/hlpool.cc @@ -150,7 +150,7 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, /** * Allocates a physical page in low memory (addressable in real-mode) and maps it into the virtual address space. * - * @param TrampolineAddress + * @param MemoryAddress * Supplies a pointer to a variable that receives the identity-mapped virtual address of the allocated memory. * * @return This routine returns a status code. From 757eac08c6a435a90ab24599b0245cb15b2f9450 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 11:27:01 +0200 Subject: [PATCH 06/32] Refactor APIC IPI dispatching --- xtoskrnl/hl/x86/pic.cc | 137 +++++++++++++++++++++--------------- xtoskrnl/includes/hl/pic.hh | 5 +- 2 files changed, 86 insertions(+), 56 deletions(-) diff --git a/xtoskrnl/hl/x86/pic.cc b/xtoskrnl/hl/x86/pic.cc index 41b4737..7d546f6 100644 --- a/xtoskrnl/hl/x86/pic.cc +++ b/xtoskrnl/hl/x86/pic.cc @@ -828,7 +828,7 @@ HL::Pic::SendBroadcastIpi(IN ULONG Vector, else { /* Dispatch the IPI to the target processor */ - SendIpi(TargetProcessorBlock->HardwareId, Vector); + SendIpi(TargetProcessorBlock->HardwareId, Vector, APIC_DM_FIXED, APIC_DSH_Destination, APIC_TGM_EDGE); } } } @@ -865,6 +865,15 @@ HL::Pic::SendEoi(VOID) * @param Vector * Supplies the IPI vector to send. * + * @param DeliveryMode + * Supplies the delivery mode for the IPI. + * + * @param DestinationShorthand + * Supplies the shorthand. + * + * @param TriggerMode + * Supplies the trigger mode (Edge or Level). + * * @return This routine does not return any value. * * @since XT 1.0 @@ -872,19 +881,85 @@ HL::Pic::SendEoi(VOID) XTAPI VOID HL::Pic::SendIpi(IN ULONG ApicId, - IN ULONG Vector) + IN ULONG Vector, + IN APIC_DM DeliveryMode, + IN APIC_DSH DestinationShortHand, + IN ULONG TriggerMode) { + APIC_COMMAND_REGISTER Register; + BOOLEAN Interrupts; + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunc::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunc::ClearInterruptFlag(); + + /* Check current APIC mode and destination */ + if(ApicMode == APIC_MODE_X2APIC && DestinationShortHand == APIC_DSH_Self) + { + /* In x2APIC mode, a dedicated Self-IPI register is used */ + WriteApicRegister(APIC_SIPI, Vector); + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Check whether interrupts need to be re-enabled */ + AR::CpuFunc::SetInterruptFlag(); + } + + /* Nothing more to do */ + return; + } + + /* Prepare APIC command register struct */ + Register.LongLong = 0; + Register.DeliveryMode = DeliveryMode; + Register.Destination = ApicId; + Register.DestinationShortHand = DestinationShortHand; + Register.Level = 1; + Register.TriggerMode = TriggerMode; + Register.Vector = Vector; + /* Check current APIC mode */ if(ApicMode == APIC_MODE_X2APIC) { + /* Set destination APIC ID */ + Register.Long1 = ApicId; + /* Send IPI using x2APIC mode */ - WriteApicRegister(APIC_ICR0, ((ULONGLONG)ApicId << 32) | Vector); + WriteApicRegister(APIC_ICR0, Register.LongLong); } else { - /* Send IPI using xAPIC compatibility mode */ - WriteApicRegister(APIC_ICR1, ApicId << 24); - WriteApicRegister(APIC_ICR0, Vector); + /* Wait for the APIC to clear the delivery status */ + while((ReadApicRegister(APIC_ICR0) & 0x1000) != 0) + { + /* Yield the processor */ + AR::CpuFunc::YieldProcessor(); + } + + /* In xAPIC compatibility mode, write the command to the ICR registers */ + WriteApicRegister(APIC_ICR1, Register.Long1); + WriteApicRegister(APIC_ICR0, Register.Long0); + + /* Check if this is a Self-IPI */ + if(DestinationShortHand == APIC_DSH_Self) + { + /* Wait until the interrupt physically arrives in the requested state */ + while((ReadApicRegister((APIC_REGISTER)(APIC_IRR + (Vector / 32))) & (1UL << (Vector % 32))) == 0) + { + /* Yield the processor */ + AR::CpuFunc::YieldProcessor(); + } + } + } + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunc::SetInterruptFlag(); } } @@ -902,55 +977,7 @@ XTAPI VOID HL::Pic::SendSelfIpi(IN ULONG Vector) { - APIC_COMMAND_REGISTER Register; - BOOLEAN Interrupts; - - /* Check whether interrupts are enabled */ - Interrupts = AR::CpuFunc::InterruptsEnabled(); - - /* Disable interrupts */ - AR::CpuFunc::ClearInterruptFlag(); - - /* Check current APIC mode */ - if(ApicMode == APIC_MODE_X2APIC) - { - /* In x2APIC mode, a dedicated Self-IPI register is used */ - WriteApicRegister(APIC_SIPI, Vector); - } - else - { - /* Prepare APIC command register */ - Register.LongLong = 0; - Register.DeliveryMode = APIC_DM_FIXED; - Register.DestinationShortHand = APIC_DSH_Self; - Register.TriggerMode = APIC_TGM_EDGE; - Register.Vector = Vector; - - /* Wait for the APIC to clear the delivery status */ - while((ReadApicRegister(APIC_ICR0) & 0x1000) != 0) - { - /* Yield the processor */ - AR::CpuFunc::YieldProcessor(); - } - - /* In xAPIC compatibility mode, write the command to the ICR registers */ - WriteApicRegister(APIC_ICR1, Register.Long1); - WriteApicRegister(APIC_ICR0, Register.Long0); - - /* Wait until the interrupt physically arrives in the requested state */ - while((ReadApicRegister((APIC_REGISTER)(APIC_IRR + (Vector / 32))) & (1UL << (Vector % 32))) == 0) - { - /* Yield the processor */ - AR::CpuFunc::YieldProcessor(); - } - } - - /* Check whether interrupts need to be re-enabled */ - if(Interrupts) - { - /* Re-enable interrupts */ - AR::CpuFunc::SetInterruptFlag(); - } + SendIpi(0, Vector, APIC_DM_FIXED, APIC_DSH_Self, APIC_TGM_EDGE); } /** diff --git a/xtoskrnl/includes/hl/pic.hh b/xtoskrnl/includes/hl/pic.hh index a8b6e38..4d5c8a7 100644 --- a/xtoskrnl/includes/hl/pic.hh +++ b/xtoskrnl/includes/hl/pic.hh @@ -38,7 +38,10 @@ namespace HL IN BOOLEAN Self); STATIC XTAPI VOID SendEoi(VOID); STATIC XTAPI VOID SendIpi(IN ULONG ApicId, - IN ULONG Vector); + IN ULONG Vector, + IN APIC_DM DeliveryMode, + IN APIC_DSH Destination, + IN ULONG TriggerMode); STATIC XTAPI VOID SendSelfIpi(IN ULONG Vector); STATIC XTFASTCALL VOID WriteApicRegister(IN APIC_REGISTER Register, IN ULONGLONG Value); From fd7e18989d74f48e7c49bec59a33ac2e0dced46f Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 14:33:41 +0200 Subject: [PATCH 07/32] Protect debug print output with spinlock --- xtoskrnl/includes/kd/dbgio.hh | 5 +++-- xtoskrnl/kd/data.cc | 3 +++ xtoskrnl/kd/dbgio.cc | 13 ++++++++++--- xtoskrnl/kd/exports.cc | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/xtoskrnl/includes/kd/dbgio.hh b/xtoskrnl/includes/kd/dbgio.hh index 86aeaf8..fc04162 100644 --- a/xtoskrnl/includes/kd/dbgio.hh +++ b/xtoskrnl/includes/kd/dbgio.hh @@ -21,6 +21,7 @@ namespace KD STATIC PKD_PRINT_ROUTINE KdPrint; private: + STATIC KSPIN_LOCK DebugIoLock; STATIC KD_DEBUG_MODE DebugMode; STATIC PKD_INIT_ROUTINE IoProvidersInitRoutines[KDBG_PROVIDERS_COUNT]; STATIC LIST_ENTRY Providers; @@ -30,8 +31,8 @@ namespace KD public: STATIC XTCDECL VOID DbgPrint(PCWSTR Format, ...); - STATIC XTCDECL VOID DbgPrintEx(PCWSTR Format, - VA_LIST Arguments); + STATIC XTCDECL VOID DbgPrint(PCWSTR Format, + VA_LIST Arguments); STATIC XTAPI XTSTATUS InitializeDebugIoProviders(VOID); STATIC XTAPI VOID SetPrintRoutine(PKD_PRINT_ROUTINE DebugPrintRoutine); diff --git a/xtoskrnl/kd/data.cc b/xtoskrnl/kd/data.cc index acaaa9c..4a999a3 100644 --- a/xtoskrnl/kd/data.cc +++ b/xtoskrnl/kd/data.cc @@ -9,6 +9,9 @@ #include +/* Debug I/O spinlock */ +KSPIN_LOCK KD::DebugIo::DebugIoLock; + /* Kernel Debugger mode */ KD_DEBUG_MODE KD::DebugIo::DebugMode; diff --git a/xtoskrnl/kd/dbgio.cc b/xtoskrnl/kd/dbgio.cc index febf449..9e0925b 100644 --- a/xtoskrnl/kd/dbgio.cc +++ b/xtoskrnl/kd/dbgio.cc @@ -33,7 +33,7 @@ KD::DebugIo::DbgPrint(PCWSTR Format, VA_START(Arguments, Format); /* Call the actual debug print routine */ - DbgPrintEx(Format, Arguments); + DbgPrint(Format, Arguments); /* Clean up the va_list */ VA_END(Arguments); @@ -54,12 +54,16 @@ KD::DebugIo::DbgPrint(PCWSTR Format, */ XTCDECL VOID -KD::DebugIo::DbgPrintEx(PCWSTR Format, - VA_LIST Arguments) +KD::DebugIo::DbgPrint(PCWSTR Format, + VA_LIST Arguments) { PLIST_ENTRY DispatchTableEntry; PKD_DISPATCH_TABLE DispatchTable; + /* Raise runlevel and acquire the Debug I/O lock */ + KE::RaiseRunLevel RunLevel(HIGH_LEVEL); + KE::SpinLockGuard SpinLock(&DebugIoLock); + /* Iterate over all registered debug providers */ DispatchTableEntry = Providers.Flink; while(DispatchTableEntry != &Providers) @@ -218,6 +222,9 @@ KD::DebugIo::InitializeDebugIoProviders(VOID) ULONG Index; XTSTATUS ProviderStatus, Status; + /* Initialize debug I/O spinlock */ + KE::SpinLock::InitializeSpinLock(&DebugIoLock); + /* Initialize debug providers list */ RTL::LinkedList::InitializeListHead(&Providers); diff --git a/xtoskrnl/kd/exports.cc b/xtoskrnl/kd/exports.cc index 622fade..06c727a 100644 --- a/xtoskrnl/kd/exports.cc +++ b/xtoskrnl/kd/exports.cc @@ -33,7 +33,7 @@ DbgPrint(PCWSTR Format, /* Initialise the va_list */ VA_START(Arguments, Format); - KD::DebugIo::DbgPrintEx(Format, Arguments); + KD::DebugIo::DbgPrint(Format, Arguments); /* Clean up the va_list */ VA_END(Arguments); From b1ecdc3439d42b0308468c8197e62d7e03c05ef9 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 20:31:30 +0200 Subject: [PATCH 08/32] Add AP startup assembly trampoline for i686 --- sdk/xtadk/i686/ke.cc | 9 ++++ sdk/xtdk/i686/ketypes.h | 16 ++++++- xtoskrnl/ar/i686/archsup.S | 89 +++++++++++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/sdk/xtadk/i686/ke.cc b/sdk/xtadk/i686/ke.cc index e266c23..bc3db83 100644 --- a/sdk/xtadk/i686/ke.cc +++ b/sdk/xtadk/i686/ke.cc @@ -54,4 +54,13 @@ GenerateAssemblyDefinitions(VOID) /* Generate KTRAP_FRAME size and REGISTERS_SIZE */ ADK_SIZE(KTRAP_FRAME); ADK_SIZE_FROM(REGISTERS_SIZE, KTRAP_FRAME, Eax); + + /* Generate PROCESSOR_START_BLOCK offsets */ + ADK_OFFSET(PROCESSOR_START_BLOCK, Cr3); + ADK_OFFSET(PROCESSOR_START_BLOCK, Cr4); + ADK_OFFSET(PROCESSOR_START_BLOCK, EntryPoint); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorBlock); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorNumber); + ADK_OFFSET(PROCESSOR_START_BLOCK, Stack); + ADK_OFFSET(PROCESSOR_START_BLOCK, Started); } diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index 37a0d4d..b02b98b 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -137,8 +137,8 @@ #define KERNEL_STACK_GUARD_PAGES 1 /* Processor structures size */ -#define KPROCESSOR_STRUCTURES_SIZE ((2 * KERNEL_STACK_SIZE) + (GDT_ENTRIES * sizeof(KGDTENTRY)) + sizeof(KTSS) + \ - sizeof(KPROCESSOR_BLOCK) + MM_PAGE_SIZE) +#define KPROCESSOR_STRUCTURES_SIZE ((KERNEL_STACKS * KERNEL_STACK_SIZE) + (GDT_ENTRIES * sizeof(KGDTENTRY)) + \ + sizeof(KTSS) + sizeof(KPROCESSOR_BLOCK) + MM_PAGE_SIZE) /* Kernel frames */ #define KTRAP_FRAME_ALIGN 0x08 @@ -431,6 +431,18 @@ typedef struct _KSPECIAL_REGISTERS ULONG Reserved[6]; } KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; +/* Processor start block structure definition */ +typedef struct _PROCESSOR_START_BLOCK +{ + ULONG_PTR Cr3; + ULONG_PTR Cr4; + PVOID EntryPoint; + PVOID ProcessorBlock; + ULONG ProcessorNumber; + PVOID Stack; + BOOLEAN Started; +} PROCESSOR_START_BLOCK, *PPROCESSOR_START_BLOCK; + /* Processor state frame structure definition */ typedef struct _KPROCESSOR_STATE { diff --git a/xtoskrnl/ar/i686/archsup.S b/xtoskrnl/ar/i686/archsup.S index aa24d5c..fd36c15 100644 --- a/xtoskrnl/ar/i686/archsup.S +++ b/xtoskrnl/ar/i686/archsup.S @@ -234,7 +234,7 @@ _ArHandleSpuriousInterrupt: iret /** - * Starts an application processor (AP). This is just a stub. + * Starts an application processor (AP). * * @return This routine does not return any value. * @@ -242,6 +242,93 @@ _ArHandleSpuriousInterrupt: */ .global _ArStartApplicationProcessor _ArStartApplicationProcessor: + /* Enter 16-bit (real mode) */ + .code16 + + /* Disable interrupts and clear direction flag */ + cli + cld + + /* Establish a flat addressing baseline */ + movw %cs, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw $0x1000, %sp + + /* Calculate absolute physical base address */ + xorl %ebx, %ebx + movw %cs, %bx + shll $4, %ebx + + /* Load the temporary Global Descriptor Table */ + leal (ApTemporaryGdtDesc - _ArStartApplicationProcessor)(%ebx), %eax + movl %eax, (ApTemporaryGdtBase - _ArStartApplicationProcessor) + lgdtl (ApTemporaryGdtSize - _ArStartApplicationProcessor) + + /* Enable Protected Mode */ + movl %cr0, %eax + orl $0x01, %eax + movl %eax, %cr0 + + /* Far return to enter 32-bit protected mode */ + leal (ApEnterProtectedMode - _ArStartApplicationProcessor)(%ebx), %eax + pushl $0x08 + pushl %eax + lretl + +ApEnterProtectedMode: + /* Enter 32-bit protected mode */ + .code32 + + /* Setup all data segment registers */ + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + xorw %ax, %ax + movw %ax, %fs + movw %ax, %gs + + /* Locate PROCESSOR_START_BLOCK structure */ + leal (_ArStartApplicationProcessorEnd - _ArStartApplicationProcessor)(%ebx), %edi + + /* Load CR4 from BSP, but mask PCIDE and PGE */ + movl PROCESSOR_START_BLOCK_Cr4(%edi), %eax + andl $~0x00020080, %eax + movl %eax, %cr4 + + /* Load the Kernel Page Directory Base from BSP */ + movl PROCESSOR_START_BLOCK_Cr3(%edi), %eax + movl %eax, %cr3 + + /* Enable Paging */ + movl %cr0, %eax + orl $0x80000000, %eax + movl %eax, %cr0 + + /* Load dedicated Stack for AP */ + movl PROCESSOR_START_BLOCK_Stack(%edi), %esp + + /* Save the pointer to PROCESSOR_START_BLOCK */ + movl %edi, %ecx + pushl %edi + + /* Call the EntryPoint routine */ + movl PROCESSOR_START_BLOCK_EntryPoint(%edi), %eax + call *%eax + + /* Fire the breakpoint and halt if the entry point returns */ +.ApNoReturnPoint: + int $0x03 + hlt + jmp .ApNoReturnPoint + +/* Data section for temporary GDT */ +.align 8 +ApTemporaryGdtSize: .short _ArStartApplicationProcessorEnd - ApTemporaryGdtDesc - 1 +ApTemporaryGdtBase: .long 0x00000000 +ApTemporaryGdtDesc: .quad 0x0000000000000000, 0x00CF9A000000FFFF, 0x00CF92000000FFFF .global _ArStartApplicationProcessorEnd _ArStartApplicationProcessorEnd: From 42bbdc9b26d52f8d6c79a9c9a59bfd9aa74a95a5 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 21:24:27 +0200 Subject: [PATCH 09/32] Fix AP trampoline stack setup --- xtoskrnl/ar/i686/archsup.S | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xtoskrnl/ar/i686/archsup.S b/xtoskrnl/ar/i686/archsup.S index fd36c15..2b5e33a 100644 --- a/xtoskrnl/ar/i686/archsup.S +++ b/xtoskrnl/ar/i686/archsup.S @@ -254,13 +254,16 @@ _ArStartApplicationProcessor: movw %ax, %ds movw %ax, %es movw %ax, %ss - movw $0x1000, %sp /* Calculate absolute physical base address */ xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx + /* Set up a temporary stack for the AP initialization */ + movl %ebx, %esp + addl $0x1000, %esp + /* Load the temporary Global Descriptor Table */ leal (ApTemporaryGdtDesc - _ArStartApplicationProcessor)(%ebx), %eax movl %eax, (ApTemporaryGdtBase - _ArStartApplicationProcessor) From ae18468badc9e899c23a7a3bef92ab18336d9a2d Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 21:48:42 +0200 Subject: [PATCH 10/32] Add AP startup assembly trampoline for AMD64 --- sdk/xtadk/amd64/ke.cc | 9 +++ sdk/xtdk/amd64/ketypes.h | 18 +++++- xtoskrnl/ar/amd64/archsup.S | 119 +++++++++++++++++++++++++++++++++++- 3 files changed, 142 insertions(+), 4 deletions(-) diff --git a/sdk/xtadk/amd64/ke.cc b/sdk/xtadk/amd64/ke.cc index 56902fb..e28ab7b 100644 --- a/sdk/xtadk/amd64/ke.cc +++ b/sdk/xtadk/amd64/ke.cc @@ -80,4 +80,13 @@ GenerateAssemblyDefinitions(VOID) /* Generate KTRAP_FRAME size and REGISTERS_SIZE */ ADK_SIZE(KTRAP_FRAME); ADK_SIZE_FROM(REGISTERS_SIZE, KTRAP_FRAME, Rax); + + /* Generate PROCESSOR_START_BLOCK offsets */ + ADK_OFFSET(PROCESSOR_START_BLOCK, Cr3); + ADK_OFFSET(PROCESSOR_START_BLOCK, Cr4); + ADK_OFFSET(PROCESSOR_START_BLOCK, EntryPoint); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorBlock); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorNumber); + ADK_OFFSET(PROCESSOR_START_BLOCK, Stack); + ADK_OFFSET(PROCESSOR_START_BLOCK, Started); } diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index 5790d02..5c972a1 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -118,8 +118,8 @@ #define KERNEL_STACK_GUARD_PAGES 1 /* Processor structures size */ -#define KPROCESSOR_STRUCTURES_SIZE ((2 * KERNEL_STACK_SIZE) + (GDT_ENTRIES * sizeof(KGDTENTRY)) + sizeof(KTSS) + \ - sizeof(KPROCESSOR_BLOCK) + MM_PAGE_SIZE) +#define KPROCESSOR_STRUCTURES_SIZE ((KERNEL_STACKS * KERNEL_STACK_SIZE) + (GDT_ENTRIES * sizeof(KGDTENTRY)) + \ + sizeof(KTSS) + sizeof(KPROCESSOR_BLOCK) + MM_PAGE_SIZE) /* Kernel frames */ #define KEXCEPTION_FRAME_SIZE sizeof(KEXCEPTION_FRAME) @@ -470,11 +470,23 @@ typedef struct _KSPECIAL_REGISTERS ULONG64 MsrSyscallMask; } KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; +/* Processor start block structure definition */ +typedef struct _PROCESSOR_START_BLOCK +{ + ULONG_PTR Cr3; + ULONG_PTR Cr4; + PVOID EntryPoint; + PVOID ProcessorBlock; + ULONG ProcessorNumber; + PVOID Stack; + BOOLEAN Started; +} PROCESSOR_START_BLOCK, *PPROCESSOR_START_BLOCK; + /* Processor state frame structure definition */ typedef struct _KPROCESSOR_STATE { - KSPECIAL_REGISTERS SpecialRegisters; CONTEXT ContextFrame; + KSPECIAL_REGISTERS SpecialRegisters; } KPROCESSOR_STATE, *PKPROCESSOR_STATE; /* Processor Control Block (PRCB) structure definition */ diff --git a/xtoskrnl/ar/amd64/archsup.S b/xtoskrnl/ar/amd64/archsup.S index 29c1d05..9bfd3f1 100644 --- a/xtoskrnl/ar/amd64/archsup.S +++ b/xtoskrnl/ar/amd64/archsup.S @@ -359,7 +359,7 @@ ArHandleSpuriousInterrupt: iretq /** - * Starts an application processor (AP). This is just a stub. + * Starts an application processor (AP). * * @return This routine does not return any value. * @@ -367,6 +367,123 @@ ArHandleSpuriousInterrupt: */ .global ArStartApplicationProcessor ArStartApplicationProcessor: + /* Enter 16-bit real mode */ + .code16 + + /* Disable interrupts and clear direction flag */ + cli + cld + + /* Establish a flat addressing baseline */ + movw %cs, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + + /* Calculate absolute physical base address */ + xorl %ebx, %ebx + movw %cs, %bx + shll $4, %ebx + + /* Set up a temporary stack for the AP initialization */ + movl %ebx, %esp + addl $0x1000, %esp + + /* Load the temporary Global Descriptor Table */ + leal (ApTemporaryGdtDesc - ArStartApplicationProcessor)(%ebx), %eax + movl %eax, (ApTemporaryGdtBase - ArStartApplicationProcessor) + lgdtl (ApTemporaryGdtSize - ArStartApplicationProcessor) + + /* Enable Protected Mode */ + movl %cr0, %eax + orl $0x01, %eax + movl %eax, %cr0 + + /* Far return to enter 32-bit protected mode */ + leal (ApEnterProtectedMode - ArStartApplicationProcessor)(%ebx), %eax + pushl $0x08 + pushl %eax + lretl + +ApEnterProtectedMode: + /* Enter 32-bit protected mode */ + .code32 + + /* Setup all data segment registers */ + movw $0x18, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + xorw %ax, %ax + movw %ax, %fs + movw %ax, %gs + + /* Locate PROCESSOR_START_BLOCK structure */ + leal (ArStartApplicationProcessorEnd - ArStartApplicationProcessor)(%ebx), %edi + + /* Load CR4 from BSP, but mask PCIDE and PGE */ + movl PROCESSOR_START_BLOCK_Cr4(%edi), %eax + andl $~(CR4_PGE | CR4_PCIDE), %eax + movl %eax, %cr4 + + /* Load the Kernel Page Directory Base from BSP */ + movl PROCESSOR_START_BLOCK_Cr3(%edi), %eax + movl %eax, %cr3 + + /* Enable Long Mode and No-Execute */ + movl $0xC0000080, %ecx + rdmsr + orl $0x00000900, %eax + wrmsr + + /* Enable Paging */ + movl %cr0, %eax + orl $0x80000000, %eax + movl %eax, %cr0 + + /* Far return to enter 64-bit long mode */ + leal (ApEnterLongMode - ArStartApplicationProcessor)(%ebx), %eax + pushl $0x10 + pushl %eax + lretl + +ApEnterLongMode: + /* Enter 64-bit long mode */ + .code64 + + /* Clear all segment registers */ + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* Zero-extend EDI into RDI to ensure safe 64-bit pointer usage */ + movl %edi, %edi + + /* Load dedicated Stack for AP */ + movq PROCESSOR_START_BLOCK_Stack(%rdi), %rsp + + /* Save the pointer to PROCESSOR_START_BLOCK */ + movq %rdi, %rcx + pushq %rdi + + /* Call the EntryPoint routine */ + movq PROCESSOR_START_BLOCK_EntryPoint(%rdi), %rax + call *%rax + + /* Fire the breakpoint and halt if the entry point returns */ +.ApNoReturnPoint: + int $0x03 + hlt + jmp .ApNoReturnPoint + +/* Data section for temporary GDT */ +.align 8 +ApTemporaryGdtSize: .short ArStartApplicationProcessorEnd - ApTemporaryGdtDesc - 1 +ApTemporaryGdtBase: .quad 0x0000000000000000 +ApTemporaryGdtDesc: .quad 0x0000000000000000, 0x00CF9A000000FFFF, 0x00AF9A000000FFFF, 0x00CF92000000FFFF .global ArStartApplicationProcessorEnd ArStartApplicationProcessorEnd: From 6b852556a53266013f9a627cc413f009d64b8f17 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Wed, 13 May 2026 22:01:49 +0200 Subject: [PATCH 11/32] Replace magic numbers with architectural defines --- xtoskrnl/ar/amd64/archsup.S | 12 ++++++------ xtoskrnl/ar/i686/archsup.S | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/xtoskrnl/ar/amd64/archsup.S b/xtoskrnl/ar/amd64/archsup.S index 9bfd3f1..a9e482b 100644 --- a/xtoskrnl/ar/amd64/archsup.S +++ b/xtoskrnl/ar/amd64/archsup.S @@ -401,7 +401,7 @@ ArStartApplicationProcessor: /* Far return to enter 32-bit protected mode */ leal (ApEnterProtectedMode - ArStartApplicationProcessor)(%ebx), %eax - pushl $0x08 + pushl $KGDT_R0_CMCODE pushl %eax lretl @@ -410,7 +410,7 @@ ApEnterProtectedMode: .code32 /* Setup all data segment registers */ - movw $0x18, %ax + movw $KGDT_R0_DATA, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss @@ -431,19 +431,19 @@ ApEnterProtectedMode: movl %eax, %cr3 /* Enable Long Mode and No-Execute */ - movl $0xC0000080, %ecx + movl $X86_MSR_EFER, %ecx rdmsr - orl $0x00000900, %eax + orl $(X86_MSR_EFER_LME | X86_MSR_EFER_NXE), %eax wrmsr /* Enable Paging */ movl %cr0, %eax - orl $0x80000000, %eax + orl $CR0_PG, %eax movl %eax, %cr0 /* Far return to enter 64-bit long mode */ leal (ApEnterLongMode - ArStartApplicationProcessor)(%ebx), %eax - pushl $0x10 + pushl $KGDT_R0_CODE pushl %eax lretl diff --git a/xtoskrnl/ar/i686/archsup.S b/xtoskrnl/ar/i686/archsup.S index 2b5e33a..6272dac 100644 --- a/xtoskrnl/ar/i686/archsup.S +++ b/xtoskrnl/ar/i686/archsup.S @@ -242,7 +242,7 @@ _ArHandleSpuriousInterrupt: */ .global _ArStartApplicationProcessor _ArStartApplicationProcessor: - /* Enter 16-bit (real mode) */ + /* Enter 16-bit real mode */ .code16 /* Disable interrupts and clear direction flag */ @@ -276,7 +276,7 @@ _ArStartApplicationProcessor: /* Far return to enter 32-bit protected mode */ leal (ApEnterProtectedMode - _ArStartApplicationProcessor)(%ebx), %eax - pushl $0x08 + pushl $KGDT_R0_CODE pushl %eax lretl @@ -285,7 +285,7 @@ ApEnterProtectedMode: .code32 /* Setup all data segment registers */ - movw $0x10, %ax + movw $KGDT_R0_DATA, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss @@ -298,7 +298,7 @@ ApEnterProtectedMode: /* Load CR4 from BSP, but mask PCIDE and PGE */ movl PROCESSOR_START_BLOCK_Cr4(%edi), %eax - andl $~0x00020080, %eax + andl $~(CR4_PGE | CR4_PCIDE), %eax movl %eax, %cr4 /* Load the Kernel Page Directory Base from BSP */ @@ -307,7 +307,7 @@ ApEnterProtectedMode: /* Enable Paging */ movl %cr0, %eax - orl $0x80000000, %eax + orl $CR0_PG, %eax movl %eax, %cr0 /* Load dedicated Stack for AP */ From 908bc87b06e242d2f9ff72793e735cb4baac527d Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 11:06:00 +0200 Subject: [PATCH 12/32] Add NULL checks to InitializeProcessorStructures and reorder TSS allocation --- xtoskrnl/ar/amd64/procsup.cc | 43 +++++++++++++++++++++++++++--------- xtoskrnl/ar/i686/procsup.cc | 43 +++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/xtoskrnl/ar/amd64/procsup.cc b/xtoskrnl/ar/amd64/procsup.cc index 8789045..1ee51de 100644 --- a/xtoskrnl/ar/amd64/procsup.cc +++ b/xtoskrnl/ar/amd64/procsup.cc @@ -439,26 +439,49 @@ AR::ProcSup::InitializeProcessorStructures(IN PVOID ProcessorStructures, Address = ROUND_UP((UINT_PTR)ProcessorStructures, MM_PAGE_SIZE) + KERNEL_STACK_SIZE; /* Assign a space for kernel boot stack and advance */ - *KernelBootStack = (PVOID)Address; + if(KernelBootStack != NULLPTR) + { + /* Return kernel boot stack address */ + *KernelBootStack = (PVOID)Address; + } Address += KERNEL_STACK_SIZE; /* Assign a space for kernel fault stack and advance */ - *KernelFaultStack = (PVOID)Address; + if(KernelFaultStack != NULLPTR) + { + /* Return kernel fault stack address */ + *KernelFaultStack = (PVOID)Address; + } Address += KERNEL_STACK_SIZE; /* Assign a space for kernel NMI stack, no advance needed as stack grows down */ - *KernelNmiStack = (PVOID)Address; + if(KernelNmiStack != NULLPTR) + { + /* Return kernel NMI stack address */ + *KernelNmiStack = (PVOID)Address; + } /* Assign a space for GDT and advance */ - *Gdt = (PKGDTENTRY)(PVOID)Address; - Address += sizeof(InitialGdt); + if(Gdt != NULLPTR) + { + /* Return GDT base address */ + *Gdt = (PKGDTENTRY)(PVOID)Address; + } + Address += (GDT_ENTRIES * sizeof(KGDTENTRY)); + + /* Assign a space for TSS and advance */ + if(Tss != NULLPTR) + { + *Tss = (PKTSS)(PVOID)Address; + } + Address += sizeof(KTSS); /* Assign a space for Processor Block and advance */ - *ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address; - Address += sizeof(InitialProcessorBlock); - - /* Assign a space for TSS */ - *Tss = (PKTSS)(PVOID)Address; + if(ProcessorBlock != NULLPTR) + { + /* Return processor block address */ + *ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address; + } } /** diff --git a/xtoskrnl/ar/i686/procsup.cc b/xtoskrnl/ar/i686/procsup.cc index 2fbd860..937d818 100644 --- a/xtoskrnl/ar/i686/procsup.cc +++ b/xtoskrnl/ar/i686/procsup.cc @@ -391,26 +391,49 @@ AR::ProcSup::InitializeProcessorStructures(IN PVOID ProcessorStructures, Address = ROUND_UP((UINT_PTR)ProcessorStructures, MM_PAGE_SIZE) + KERNEL_STACK_SIZE; /* Assign a space for kernel boot stack and advance */ - *KernelBootStack = (PVOID)Address; + if(KernelBootStack != NULLPTR) + { + /* Return kernel boot stack address */ + *KernelBootStack = (PVOID)Address; + } Address += KERNEL_STACK_SIZE; /* Assign a space for kernel fault stack and advance */ - *KernelFaultStack = (PVOID)Address; + if(KernelFaultStack != NULLPTR) + { + /* Return kernel fault stack address */ + *KernelFaultStack = (PVOID)Address; + } Address += KERNEL_STACK_SIZE; /* Assign a space for kernel NMI stack, no advance needed as stack grows down */ - *KernelNmiStack = (PVOID)Address; + if(KernelNmiStack != NULLPTR) + { + /* Return kernel NMI stack address */ + *KernelNmiStack = (PVOID)Address; + } /* Assign a space for GDT and advance */ - *Gdt = (PKGDTENTRY)(PVOID)Address; - Address += sizeof(InitialGdt); + if(Gdt != NULLPTR) + { + /* Return GDT base address */ + *Gdt = (PKGDTENTRY)(PVOID)Address; + } + Address += (GDT_ENTRIES * sizeof(KGDTENTRY)); + + /* Assign a space for TSS and advance */ + if(Tss != NULLPTR) + { + *Tss = (PKTSS)(PVOID)Address; + } + Address += sizeof(KTSS); /* Assign a space for Processor Block and advance */ - *ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address; - Address += sizeof(InitialProcessorBlock); - - /* Assign a space for TSS */ - *Tss = (PKTSS)(PVOID)Address; + if(ProcessorBlock != NULLPTR) + { + /* Return processor block address */ + *ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address; + } } /** From 9185ceade6890447bdb630f5f3b7205a41538112 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 11:07:42 +0200 Subject: [PATCH 13/32] Decouple processor block initialization from AllocateProcessorStructures --- xtoskrnl/includes/mm/kpool.hh | 3 +-- xtoskrnl/mm/kpool.cc | 17 +---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/xtoskrnl/includes/mm/kpool.hh b/xtoskrnl/includes/mm/kpool.hh index 1a11bbf..1ec941e 100644 --- a/xtoskrnl/includes/mm/kpool.hh +++ b/xtoskrnl/includes/mm/kpool.hh @@ -20,8 +20,7 @@ namespace MM public: STATIC XTAPI XTSTATUS AllocateKernelStack(OUT PVOID *Stack, IN ULONG StackSize); - STATIC XTAPI XTSTATUS AllocateProcessorStructures(IN ULONG CpuNumber, - OUT PVOID *StructuresData); + STATIC XTAPI XTSTATUS AllocateProcessorStructures(OUT PVOID *StructuresData); STATIC XTAPI VOID FreeKernelStack(IN PVOID Stack, IN ULONG StackSize); STATIC XTAPI VOID FreeProcessorStructures(IN PVOID StructuresData); diff --git a/xtoskrnl/mm/kpool.cc b/xtoskrnl/mm/kpool.cc index b3bc4b0..36b3062 100644 --- a/xtoskrnl/mm/kpool.cc +++ b/xtoskrnl/mm/kpool.cc @@ -95,9 +95,6 @@ MM::KernelPool::AllocateKernelStack(OUT PVOID *Stack, /** * Allocates a buffer for structures needed by a processor and assigns it to a corresponding CPU. * - * @param CpuNumber - * Specifies the zero-indexed CPU number as an owner of the allocated structures. - * * @param StructuresData * Supplies a pointer to the memory area that will contain the allocated buffer. * @@ -107,12 +104,9 @@ MM::KernelPool::AllocateKernelStack(OUT PVOID *Stack, */ XTAPI XTSTATUS -MM::KernelPool::AllocateProcessorStructures(IN ULONG CpuNumber, - OUT PVOID *StructuresData) +MM::KernelPool::AllocateProcessorStructures(OUT PVOID *StructuresData) { - PKPROCESSOR_BLOCK ProcessorBlock; PVOID ProcessorStructures; - UINT_PTR Address; XTSTATUS Status; /* Assign memory for processor structures */ @@ -126,15 +120,6 @@ MM::KernelPool::AllocateProcessorStructures(IN ULONG CpuNumber, /* Make sure all structures are zeroed */ RTL::Memory::ZeroMemory(ProcessorStructures, KPROCESSOR_STRUCTURES_SIZE); - /* Align address to page size boundary and find a space for processor block */ - Address = ROUND_UP((UINT_PTR)ProcessorStructures, MM_PAGE_SIZE); - ProcessorBlock = (PKPROCESSOR_BLOCK)((PUCHAR)Address + - (KERNEL_STACKS * KERNEL_STACK_SIZE) + - (GDT_ENTRIES * sizeof(KGDTENTRY))); - - /* Store processor number in the processor block */ - ProcessorBlock->CpuNumber = CpuNumber; - /* Return pointer to the processor structures */ *StructuresData = ProcessorStructures; From 1cff58c106683e3d20bc2fcab7431863001277fb Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 13:15:55 +0200 Subject: [PATCH 14/32] Refactor processor block initialization and add registration function --- xtoskrnl/includes/ke/proc.hh | 4 +++- xtoskrnl/ke/amd64/proc.cc | 41 +++++++++++++++++++++++++++++------- xtoskrnl/ke/i686/proc.cc | 41 +++++++++++++++++++++++++++++------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/xtoskrnl/includes/ke/proc.hh b/xtoskrnl/includes/ke/proc.hh index e9b63c9..082c3d0 100644 --- a/xtoskrnl/includes/ke/proc.hh +++ b/xtoskrnl/includes/ke/proc.hh @@ -27,8 +27,10 @@ namespace KE STATIC XTAPI ULONG GetCurrentProcessorNumber(VOID); STATIC XTAPI PKTHREAD GetCurrentThread(VOID); STATIC XTAPI PKPROCESSOR_BLOCK GetProcessorBlock(IN ULONG CpuNumber); - STATIC XTAPI XTSTATUS InitializeProcessorStructures(IN ULONG CpuCount); + STATIC XTAPI XTSTATUS InitializeProcessorBlocks(); STATIC XTAPI VOID RegisterHardwareId(IN ULONG HardwareId); + STATIC XTAPI VOID RegisterProcessorBlock(ULONG CpuNumber, + PKPROCESSOR_BLOCK ProcessorBlock); STATIC XTAPI VOID SaveProcessorState(OUT PKPROCESSOR_STATE CpuState); }; } diff --git a/xtoskrnl/ke/amd64/proc.cc b/xtoskrnl/ke/amd64/proc.cc index 7622b80..0449133 100644 --- a/xtoskrnl/ke/amd64/proc.cc +++ b/xtoskrnl/ke/amd64/proc.cc @@ -81,7 +81,7 @@ PKPROCESSOR_BLOCK KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) { /* Check if the requested CPU number is within dynamic bounds */ - if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + if(CpuNumber >= InstalledCpus || ProcessorBlocks == NULLPTR || ProcessorBlocks[CpuNumber] == NULLPTR) { /* Invalid CPU number, return NULLPTR */ return NULLPTR; @@ -94,25 +94,24 @@ KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) /** * Initializes the global processor structures by allocating an array of processor block pointers. * - * @param CpuCount - * Supplies the total number of processors present in the system. - * * @return This routine returns a status code indicating the success or failure of the allocation. * * @since XT 1.0 */ XTAPI XTSTATUS -KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +KE::Processor::InitializeProcessorBlocks() { + PACPI_SYSTEM_INFO SystemInfo; XTSTATUS Status; /* Save number of CPUs installed */ - InstalledCpus = CpuCount; + HL::Acpi::GetSystemInformation(&SystemInfo); + InstalledCpus = SystemInfo->CpuCount; /* Allocate an array of pointers */ Status = MM::Allocator::AllocatePool(NonPagedPool, - CpuCount * sizeof(PKPROCESSOR_BLOCK), + InstalledCpus * sizeof(PKPROCESSOR_BLOCK), (PVOID*)&ProcessorBlocks); if(Status != STATUS_SUCCESS) { @@ -121,7 +120,7 @@ KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) } /* Zero the array initially */ - RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + RTL::Memory::ZeroMemory(ProcessorBlocks, InstalledCpus * sizeof(PKPROCESSOR_BLOCK)); /* Return success */ return STATUS_SUCCESS; @@ -152,6 +151,32 @@ KE::Processor::RegisterHardwareId(IN ULONG HardwareId) } } +/** + * Registers or deregisters a processor block in the global CPU table. + * + * @param CpuNumber + * Specifies the logical processor number. + * + * @param ProcessorBlock + * Supplies a pointer to the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterProcessorBlock(ULONG CpuNumber, + PKPROCESSOR_BLOCK ProcessorBlock) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks != NULLPTR && CpuNumber < InstalledCpus) + { + /* Register processor block */ + ProcessorBlocks[CpuNumber] = ProcessorBlock; + } +} + /** * Saves the current processor state. * diff --git a/xtoskrnl/ke/i686/proc.cc b/xtoskrnl/ke/i686/proc.cc index 84f8f89..ced7deb 100644 --- a/xtoskrnl/ke/i686/proc.cc +++ b/xtoskrnl/ke/i686/proc.cc @@ -81,7 +81,7 @@ PKPROCESSOR_BLOCK KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) { /* Check if the requested CPU number is within dynamic bounds */ - if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + if(CpuNumber >= InstalledCpus || ProcessorBlocks == NULLPTR || ProcessorBlocks[CpuNumber] == NULLPTR) { /* Invalid CPU number, return NULLPTR */ return NULLPTR; @@ -94,25 +94,24 @@ KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) /** * Initializes the global processor structures by allocating an array of processor block pointers. * - * @param CpuCount - * Supplies the total number of processors present in the system. - * * @return This routine returns a status code indicating the success or failure of the allocation. * * @since XT 1.0 */ XTAPI XTSTATUS -KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +KE::Processor::InitializeProcessorBlocks() { + PACPI_SYSTEM_INFO SystemInfo; XTSTATUS Status; /* Save number of CPUs installed */ - InstalledCpus = CpuCount; + HL::Acpi::GetSystemInformation(&SystemInfo); + InstalledCpus = SystemInfo->CpuCount; /* Allocate an array of pointers */ Status = MM::Allocator::AllocatePool(NonPagedPool, - CpuCount * sizeof(PKPROCESSOR_BLOCK), + InstalledCpus * sizeof(PKPROCESSOR_BLOCK), (PVOID*)&ProcessorBlocks); if(Status != STATUS_SUCCESS) { @@ -121,7 +120,7 @@ KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) } /* Zero the array initially */ - RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + RTL::Memory::ZeroMemory(ProcessorBlocks, InstalledCpus * sizeof(PKPROCESSOR_BLOCK)); /* Return success */ return STATUS_SUCCESS; @@ -152,6 +151,32 @@ KE::Processor::RegisterHardwareId(IN ULONG HardwareId) } } +/** + * Registers or deregisters a processor block in the global CPU table. + * + * @param CpuNumber + * Specifies the logical processor number. + * + * @param ProcessorBlock + * Supplies a pointer to the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterProcessorBlock(ULONG CpuNumber, + PKPROCESSOR_BLOCK ProcessorBlock) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks != NULLPTR && CpuNumber < InstalledCpus) + { + /* Register processor block */ + ProcessorBlocks[CpuNumber] = ProcessorBlock; + } +} + /** * Saves the current processor state. * From 7a2a27b1b9244e6951c341844568fadff0ace6d3 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 18:00:56 +0200 Subject: [PATCH 15/32] Consolidate ProcessorBlock and ProcessorNumber into ProcessorStructures --- sdk/xtadk/amd64/ke.cc | 3 +-- sdk/xtadk/i686/ke.cc | 3 +-- sdk/xtdk/amd64/ketypes.h | 3 +-- sdk/xtdk/i686/ketypes.h | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/sdk/xtadk/amd64/ke.cc b/sdk/xtadk/amd64/ke.cc index e28ab7b..b481521 100644 --- a/sdk/xtadk/amd64/ke.cc +++ b/sdk/xtadk/amd64/ke.cc @@ -85,8 +85,7 @@ GenerateAssemblyDefinitions(VOID) ADK_OFFSET(PROCESSOR_START_BLOCK, Cr3); ADK_OFFSET(PROCESSOR_START_BLOCK, Cr4); ADK_OFFSET(PROCESSOR_START_BLOCK, EntryPoint); - ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorBlock); - ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorNumber); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorStructures); ADK_OFFSET(PROCESSOR_START_BLOCK, Stack); ADK_OFFSET(PROCESSOR_START_BLOCK, Started); } diff --git a/sdk/xtadk/i686/ke.cc b/sdk/xtadk/i686/ke.cc index bc3db83..671e9ce 100644 --- a/sdk/xtadk/i686/ke.cc +++ b/sdk/xtadk/i686/ke.cc @@ -59,8 +59,7 @@ GenerateAssemblyDefinitions(VOID) ADK_OFFSET(PROCESSOR_START_BLOCK, Cr3); ADK_OFFSET(PROCESSOR_START_BLOCK, Cr4); ADK_OFFSET(PROCESSOR_START_BLOCK, EntryPoint); - ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorBlock); - ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorNumber); + ADK_OFFSET(PROCESSOR_START_BLOCK, ProcessorStructures); ADK_OFFSET(PROCESSOR_START_BLOCK, Stack); ADK_OFFSET(PROCESSOR_START_BLOCK, Started); } diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index 5c972a1..b2d4a37 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -476,8 +476,7 @@ typedef struct _PROCESSOR_START_BLOCK ULONG_PTR Cr3; ULONG_PTR Cr4; PVOID EntryPoint; - PVOID ProcessorBlock; - ULONG ProcessorNumber; + PVOID ProcessorStructures; PVOID Stack; BOOLEAN Started; } PROCESSOR_START_BLOCK, *PPROCESSOR_START_BLOCK; diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index b02b98b..1c5b0b3 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -437,8 +437,7 @@ typedef struct _PROCESSOR_START_BLOCK ULONG_PTR Cr3; ULONG_PTR Cr4; PVOID EntryPoint; - PVOID ProcessorBlock; - ULONG ProcessorNumber; + PVOID ProcessorStructures; PVOID Stack; BOOLEAN Started; } PROCESSOR_START_BLOCK, *PPROCESSOR_START_BLOCK; From 5b7761fe7d3c5c2d3394ca18200356a011ed33a3 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 18:10:29 +0200 Subject: [PATCH 16/32] Expose InitializeProcessorStructures as a public method --- xtoskrnl/includes/ar/amd64/procsup.hh | 14 +++++++------- xtoskrnl/includes/ar/i686/procsup.hh | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/xtoskrnl/includes/ar/amd64/procsup.hh b/xtoskrnl/includes/ar/amd64/procsup.hh index 6a1d6c8..9591cc4 100644 --- a/xtoskrnl/includes/ar/amd64/procsup.hh +++ b/xtoskrnl/includes/ar/amd64/procsup.hh @@ -32,6 +32,13 @@ namespace AR OUT PVOID *TrampolineCode, OUT PULONG_PTR TrampolineSize); STATIC XTAPI VOID InitializeProcessor(IN PVOID ProcessorStructures); + STATIC XTAPI VOID InitializeProcessorStructures(IN PVOID ProcessorStructures, + OUT PKGDTENTRY *Gdt, + OUT PKTSS *Tss, + OUT PKPROCESSOR_BLOCK *ProcessorBlock, + OUT PVOID *KernelBootStack, + OUT PVOID *KernelFaultStack, + OUT PVOID *KernelNmiStack); STATIC XTAPI VOID SetIdtGate(IN PKIDTENTRY Idt, IN USHORT Vector, IN PVOID Handler, @@ -50,13 +57,6 @@ namespace AR IN PKTSS Tss, IN PVOID DpcStack); STATIC XTAPI VOID InitializeProcessorRegisters(VOID); - STATIC XTAPI VOID InitializeProcessorStructures(IN PVOID ProcessorStructures, - OUT PKGDTENTRY *Gdt, - OUT PKTSS *Tss, - OUT PKPROCESSOR_BLOCK *ProcessorBlock, - OUT PVOID *KernelBootStack, - OUT PVOID *KernelFaultStack, - OUT PVOID *KernelNmiStack); STATIC XTAPI VOID InitializeSegments(VOID); STATIC XTAPI VOID InitializeTss(IN PKPROCESSOR_BLOCK ProcessorBlock, IN PVOID KernelBootStack, diff --git a/xtoskrnl/includes/ar/i686/procsup.hh b/xtoskrnl/includes/ar/i686/procsup.hh index fd1c6fc..2f1b93c 100644 --- a/xtoskrnl/includes/ar/i686/procsup.hh +++ b/xtoskrnl/includes/ar/i686/procsup.hh @@ -35,6 +35,13 @@ namespace AR OUT PVOID *TrampolineCode, OUT PULONG_PTR TrampolineSize); STATIC XTAPI VOID InitializeProcessor(IN PVOID ProcessorStructures); + STATIC XTAPI VOID InitializeProcessorStructures(IN PVOID ProcessorStructures, + OUT PKGDTENTRY *Gdt, + OUT PKTSS *Tss, + OUT PKPROCESSOR_BLOCK *ProcessorBlock, + OUT PVOID *KernelBootStack, + OUT PVOID *KernelFaultStack, + OUT PVOID *KernelNmiStack); STATIC XTAPI VOID SetIdtGate(IN PKIDTENTRY Idt, IN USHORT Vector, IN PVOID Handler, @@ -53,13 +60,6 @@ namespace AR IN PKTSS Tss, IN PVOID DpcStack); STATIC XTAPI VOID InitializeProcessorRegisters(VOID); - STATIC XTAPI VOID InitializeProcessorStructures(IN PVOID ProcessorStructures, - OUT PKGDTENTRY *Gdt, - OUT PKTSS *Tss, - OUT PKPROCESSOR_BLOCK *ProcessorBlock, - OUT PVOID *KernelBootStack, - OUT PVOID *KernelFaultStack, - OUT PVOID *KernelNmiStack); STATIC XTAPI VOID InitializeSegments(VOID); STATIC XTAPI VOID InitializeTss(IN PKPROCESSOR_BLOCK ProcessorBlock, IN PVOID KernelBootStack, From ca4f3acc0edb5446a0f438a2cc5125d45b400911 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 18:59:00 +0200 Subject: [PATCH 17/32] Properly identify CPU vendor --- xtoskrnl/ar/amd64/procsup.cc | 181 +++++++++++++++----------- xtoskrnl/ar/i686/procsup.cc | 176 ++++++++++++++----------- xtoskrnl/includes/ar/amd64/procsup.hh | 1 + xtoskrnl/includes/ar/i686/procsup.hh | 2 +- 4 files changed, 211 insertions(+), 149 deletions(-) diff --git a/xtoskrnl/ar/amd64/procsup.cc b/xtoskrnl/ar/amd64/procsup.cc index 1ee51de..16aab6b 100644 --- a/xtoskrnl/ar/amd64/procsup.cc +++ b/xtoskrnl/ar/amd64/procsup.cc @@ -20,6 +20,7 @@ XTAPI PVOID AR::ProcSup::GetBootStack(VOID) { + /* Return base address of kernel boot stack */ return (PVOID)((ULONG_PTR)BootStack + KERNEL_STACK_SIZE); } @@ -29,19 +30,23 @@ AR::ProcSup::GetTrampolineInformation(IN TRAMPOLINE_TYPE TrampolineType, OUT PVOID *TrampolineCode, OUT PULONG_PTR TrampolineSize) { + /* Get trampoline information */ switch(TrampolineType) { case TrampolineApStartup: + /* Get AP startup trampoline information */ *TrampolineCode = (PVOID)ArStartApplicationProcessor; *TrampolineSize = (ULONG_PTR)ArStartApplicationProcessorEnd - (ULONG_PTR)ArStartApplicationProcessor; break; case TrampolineEnableXpa: + /* Get Enable XPA trampoline information */ *TrampolineCode = (PVOID)ArEnableExtendedPhysicalAddressing; *TrampolineSize = (ULONG_PTR)ArEnableExtendedPhysicalAddressingEnd - (ULONG_PTR)ArEnableExtendedPhysicalAddressing; break; default: + /* Unknown trampoline type */ *TrampolineCode = NULLPTR; *TrampolineSize = 0; break; @@ -64,9 +69,6 @@ AR::ProcSup::IdentifyProcessor(VOID) CPUID_REGISTERS CpuRegisters; CPUID_SIGNATURE CpuSignature; - /* Not fully implemented yet */ - UNIMPLEMENTED; - /* Get current processor control block */ Prcb = KE::Processor::GetCurrentProcessorControlBlock(); @@ -75,13 +77,29 @@ AR::ProcSup::IdentifyProcessor(VOID) CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; AR::CpuFunc::CpuId(&CpuRegisters); - /* Store CPU vendor in processor control block */ - Prcb->CpuId.Vendor = (CPU_VENDOR)CpuRegisters.Ebx; + /* Store CPU vendor name in processor control block */ *(PULONG)&Prcb->CpuId.VendorName[0] = CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[4] = CpuRegisters.Edx; *(PULONG)&Prcb->CpuId.VendorName[8] = CpuRegisters.Ecx; Prcb->CpuId.VendorName[12] = '\0'; + /* Resolve CPU vendor */ + if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "AuthenticAMD", 12) == 12) + { + /* AMD CPU */ + Prcb->CpuId.Vendor = CPU_VENDOR_AMD; + } + else if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "GenuineIntel", 12) == 12) + { + /* Intel CPU */ + Prcb->CpuId.Vendor = CPU_VENDOR_INTEL; + } + else + { + /* Unknown CPU vendor */ + Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; + } + /* Get CPU standard features */ RtlZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; @@ -116,17 +134,13 @@ AR::ProcSup::IdentifyProcessor(VOID) Prcb->CpuId.Model += (CpuSignature.ExtendedModel << 4); } } - else - { - /* Unknown CPU vendor */ - Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; - } - /* TODO: Store a list of CPU features in processor control block */ + /* Identify processor features */ + IdentifyProcessorFeatures(); } /** - * Initializes AMD64 processor specific structures. + * Identifies processor features and stores them in Processor Control Block (PRCB). * * @return This routine does not return any value. * @@ -134,71 +148,12 @@ AR::ProcSup::IdentifyProcessor(VOID) */ XTAPI VOID -AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) +AR::ProcSup::IdentifyProcessorFeatures(VOID) { - PVOID KernelBootStack, KernelFaultStack, KernelNmiStack; - KDESCRIPTOR GdtDescriptor, IdtDescriptor; - PKPROCESSOR_BLOCK ProcessorBlock; - PKGDTENTRY Gdt; - PKIDTENTRY Idt; - PKTSS Tss; + PKPROCESSOR_CONTROL_BLOCK Prcb; - /* Check if processor structures buffer provided */ - if(ProcessorStructures) - { - /* Assign CPU structures from provided buffer */ - InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock, - &KernelBootStack, &KernelFaultStack, &KernelNmiStack); - - /* Use global IDT */ - Idt = InitialIdt; - } - else - { - /* Use initial structures */ - Gdt = InitialGdt; - Idt = InitialIdt; - Tss = &InitialTss; - KernelBootStack = (PVOID)((ULONG_PTR)&BootStack + KERNEL_STACK_SIZE); - KernelFaultStack = (PVOID)((ULONG_PTR)&FaultStack + KERNEL_STACK_SIZE); - KernelNmiStack = (PVOID)((ULONG_PTR)&NmiStack + KERNEL_STACK_SIZE); - ProcessorBlock = &InitialProcessorBlock; - } - - /* Initialize processor block */ - InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack); - - /* Initialize GDT, IDT and TSS */ - InitializeGdt(ProcessorBlock); - InitializeIdt(ProcessorBlock); - InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack); - - /* Set GDT and IDT descriptors */ - GdtDescriptor.Base = Gdt; - GdtDescriptor.Limit = (GDT_ENTRIES * sizeof(KGDTENTRY)) - 1; - IdtDescriptor.Base = Idt; - IdtDescriptor.Limit = (IDT_ENTRIES * sizeof(KIDTENTRY)) - 1; - - /* Load GDT, IDT and TSS */ - AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit); - AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); - AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); - - /* Enter passive IRQ level */ - HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); - - /* Initialize segment registers */ - InitializeSegments(); - - /* Set GS base */ - AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_GSBASE, (ULONGLONG)ProcessorBlock); - AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_KERNEL_GSBASE, (ULONGLONG)ProcessorBlock); - - /* Initialize processor registers */ - InitializeProcessorRegisters(); - - /* Identify processor */ - IdentifyProcessor(); + /* Get current processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); } /** @@ -281,6 +236,82 @@ AR::ProcSup::InitializeIdt(IN PKPROCESSOR_BLOCK ProcessorBlock) SetIdtGate(ProcessorBlock->IdtBase, 0xE1, (PVOID)ArInterruptEntry[0xE1], KGDT_R0_CODE, KIDT_IST_RESERVED, KIDT_ACCESS_RING0, AMD64_INTERRUPT_GATE); } +/** + * Initializes AMD64 processor specific structures. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) +{ + PVOID KernelBootStack, KernelFaultStack, KernelNmiStack; + KDESCRIPTOR GdtDescriptor, IdtDescriptor; + PKPROCESSOR_BLOCK ProcessorBlock; + PKGDTENTRY Gdt; + PKIDTENTRY Idt; + PKTSS Tss; + + /* Check if processor structures buffer provided */ + if(ProcessorStructures) + { + /* Assign CPU structures from provided buffer */ + InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock, + &KernelBootStack, &KernelFaultStack, &KernelNmiStack); + + /* Use global IDT */ + Idt = InitialIdt; + } + else + { + /* Use initial structures */ + Gdt = InitialGdt; + Idt = InitialIdt; + Tss = &InitialTss; + KernelBootStack = (PVOID)((ULONG_PTR)&BootStack + KERNEL_STACK_SIZE); + KernelFaultStack = (PVOID)((ULONG_PTR)&FaultStack + KERNEL_STACK_SIZE); + KernelNmiStack = (PVOID)((ULONG_PTR)&NmiStack + KERNEL_STACK_SIZE); + ProcessorBlock = &InitialProcessorBlock; + } + + /* Initialize processor block */ + InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack); + + /* Initialize GDT, IDT and TSS */ + InitializeGdt(ProcessorBlock); + InitializeIdt(ProcessorBlock); + InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack); + + /* Set GDT and IDT descriptors */ + GdtDescriptor.Base = Gdt; + GdtDescriptor.Limit = (GDT_ENTRIES * sizeof(KGDTENTRY)) - 1; + IdtDescriptor.Base = Idt; + IdtDescriptor.Limit = (IDT_ENTRIES * sizeof(KIDTENTRY)) - 1; + + /* Load GDT, IDT and TSS */ + AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit); + AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); + AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); + + /* Enter passive IRQ level */ + HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); + + /* Initialize segment registers */ + InitializeSegments(); + + /* Set GS base */ + AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_GSBASE, (ULONGLONG)ProcessorBlock); + AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_KERNEL_GSBASE, (ULONGLONG)ProcessorBlock); + + /* Initialize processor registers */ + InitializeProcessorRegisters(); + + /* Identify processor */ + IdentifyProcessor(); +} + /** * Initializes processor block. * diff --git a/xtoskrnl/ar/i686/procsup.cc b/xtoskrnl/ar/i686/procsup.cc index 937d818..4957b83 100644 --- a/xtoskrnl/ar/i686/procsup.cc +++ b/xtoskrnl/ar/i686/procsup.cc @@ -20,6 +20,7 @@ XTAPI PVOID AR::ProcSup::GetBootStack(VOID) { + /* Return base address of kernel boot stack */ return (PVOID)((ULONG_PTR)BootStack + KERNEL_STACK_SIZE); } @@ -29,14 +30,17 @@ AR::ProcSup::GetTrampolineInformation(IN TRAMPOLINE_TYPE TrampolineType, OUT PVOID *TrampolineCode, OUT PULONG_PTR TrampolineSize) { + /* Get trampoline information */ switch(TrampolineType) { case TrampolineApStartup: + /* Get AP startup trampoline information */ *TrampolineCode = (PVOID)ArStartApplicationProcessor; *TrampolineSize = (ULONG_PTR)ArStartApplicationProcessorEnd - (ULONG_PTR)ArStartApplicationProcessor; break; default: + /* Unknown trampoline type */ *TrampolineCode = NULLPTR; *TrampolineSize = 0; break; @@ -44,8 +48,7 @@ AR::ProcSup::GetTrampolineInformation(IN TRAMPOLINE_TYPE TrampolineType, } /** - * Identifies processor type (vendor, model, stepping) as well as looks for available CPU features and stores them - * in Processor Control Block (PRCB). + * Identifies processor type (vendor, model, stepping) and stores them in Processor Control Block (PRCB). * * @return This routine does not return any value. * @@ -59,9 +62,6 @@ AR::ProcSup::IdentifyProcessor(VOID) CPUID_REGISTERS CpuRegisters; CPUID_SIGNATURE CpuSignature; - /* Not fully implemented yet */ - UNIMPLEMENTED; - /* Get current processor control block */ Prcb = KE::Processor::GetCurrentProcessorControlBlock(); @@ -70,13 +70,29 @@ AR::ProcSup::IdentifyProcessor(VOID) CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; AR::CpuFunc::CpuId(&CpuRegisters); - /* Store CPU vendor in processor control block */ - Prcb->CpuId.Vendor = (CPU_VENDOR)CpuRegisters.Ebx; + /* Store CPU vendor name in processor control block */ *(PULONG)&Prcb->CpuId.VendorName[0] = CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[4] = CpuRegisters.Edx; *(PULONG)&Prcb->CpuId.VendorName[8] = CpuRegisters.Ecx; Prcb->CpuId.VendorName[12] = '\0'; + /* Resolve CPU vendor */ + if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "AuthenticAMD", 12) == 12) + { + /* AMD CPU */ + Prcb->CpuId.Vendor = CPU_VENDOR_AMD; + } + else if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "GenuineIntel", 12) == 12) + { + /* Intel CPU */ + Prcb->CpuId.Vendor = CPU_VENDOR_INTEL; + } + else + { + /* Unknown CPU vendor */ + Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; + } + /* Get CPU standard features */ RtlZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; @@ -111,17 +127,13 @@ AR::ProcSup::IdentifyProcessor(VOID) Prcb->CpuId.Model += (CpuSignature.ExtendedModel << 4); } } - else - { - /* Unknown CPU vendor */ - Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; - } - /* TODO: Store a list of CPU features in processor control block */ + /* Identify processor features */ + IdentifyProcessorFeatures(); } /** - * Initializes i686 processor specific structures. + * Identifies processor features and stores them in Processor Control Block (PRCB). * * @return This routine does not return any value. * @@ -129,67 +141,12 @@ AR::ProcSup::IdentifyProcessor(VOID) */ XTAPI VOID -AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) +AR::ProcSup::IdentifyProcessorFeatures(VOID) { - KDESCRIPTOR GdtDescriptor, IdtDescriptor; - PVOID KernelBootStack, KernelFaultStack, KernelNmiStack; - PKPROCESSOR_BLOCK ProcessorBlock; - PKGDTENTRY Gdt; - PKIDTENTRY Idt; - PKTSS Tss; + PKPROCESSOR_CONTROL_BLOCK Prcb; - /* Check if processor structures buffer provided */ - if(ProcessorStructures) - { - /* Assign CPU structures from provided buffer */ - InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock, - &KernelBootStack, &KernelFaultStack, &KernelNmiStack); - - /* Use global IDT */ - Idt = InitialIdt; - } - else - { - /* Use initial structures */ - Gdt = InitialGdt; - Idt = InitialIdt; - Tss = &InitialTss; - KernelBootStack = (PVOID)((ULONG_PTR)&BootStack + KERNEL_STACK_SIZE); - KernelFaultStack = (PVOID)((ULONG_PTR)&FaultStack + KERNEL_STACK_SIZE); - KernelNmiStack = (PVOID)((ULONG_PTR)&NmiStack + KERNEL_STACK_SIZE); - ProcessorBlock = &InitialProcessorBlock; - } - - /* Initialize processor block */ - InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack); - - /* Initialize GDT, IDT and TSS */ - InitializeGdt(ProcessorBlock); - InitializeIdt(ProcessorBlock); - InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack); - - /* Set GDT and IDT descriptors */ - GdtDescriptor.Base = Gdt; - GdtDescriptor.Limit = (GDT_ENTRIES * sizeof(KGDTENTRY)) - 1; - IdtDescriptor.Base = Idt; - IdtDescriptor.Limit = (IDT_ENTRIES * sizeof(KIDTENTRY)) - 1; - - /* Load GDT, IDT and TSS */ - AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit); - AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); - AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); - - /* Enter passive IRQ level */ - HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); - - /* Initialize segment registers */ - InitializeSegments(); - - /* Initialize processor registers */ - InitializeProcessorRegisters(); - - /* Identify processor */ - IdentifyProcessor(); + /* Get current processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); } /** @@ -274,6 +231,79 @@ AR::ProcSup::InitializeIdt(IN PKPROCESSOR_BLOCK ProcessorBlock) SetIdtGate(ProcessorBlock->IdtBase, 0x2E, (PVOID)ArTrapEntry[0x2E], KGDT_R0_CODE, 0, KIDT_ACCESS_RING3, I686_INTERRUPT_GATE); } + +/** + * Initializes i686 processor specific structures. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) +{ + KDESCRIPTOR GdtDescriptor, IdtDescriptor; + PVOID KernelBootStack, KernelFaultStack, KernelNmiStack; + PKPROCESSOR_BLOCK ProcessorBlock; + PKGDTENTRY Gdt; + PKIDTENTRY Idt; + PKTSS Tss; + + /* Check if processor structures buffer provided */ + if(ProcessorStructures) + { + /* Assign CPU structures from provided buffer */ + InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock, + &KernelBootStack, &KernelFaultStack, &KernelNmiStack); + + /* Use global IDT */ + Idt = InitialIdt; + } + else + { + /* Use initial structures */ + Gdt = InitialGdt; + Idt = InitialIdt; + Tss = &InitialTss; + KernelBootStack = (PVOID)((ULONG_PTR)&BootStack + KERNEL_STACK_SIZE); + KernelFaultStack = (PVOID)((ULONG_PTR)&FaultStack + KERNEL_STACK_SIZE); + KernelNmiStack = (PVOID)((ULONG_PTR)&NmiStack + KERNEL_STACK_SIZE); + ProcessorBlock = &InitialProcessorBlock; + } + + /* Initialize processor block */ + InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack); + + /* Initialize GDT, IDT and TSS */ + InitializeGdt(ProcessorBlock); + InitializeIdt(ProcessorBlock); + InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack); + + /* Set GDT and IDT descriptors */ + GdtDescriptor.Base = Gdt; + GdtDescriptor.Limit = (GDT_ENTRIES * sizeof(KGDTENTRY)) - 1; + IdtDescriptor.Base = Idt; + IdtDescriptor.Limit = (IDT_ENTRIES * sizeof(KIDTENTRY)) - 1; + + /* Load GDT, IDT and TSS */ + AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit); + AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); + AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); + + /* Enter passive IRQ level */ + HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); + + /* Initialize segment registers */ + InitializeSegments(); + + /* Initialize processor registers */ + InitializeProcessorRegisters(); + + /* Identify processor */ + IdentifyProcessor(); +} + /** * Initializes processor block. * diff --git a/xtoskrnl/includes/ar/amd64/procsup.hh b/xtoskrnl/includes/ar/amd64/procsup.hh index 9591cc4..878a846 100644 --- a/xtoskrnl/includes/ar/amd64/procsup.hh +++ b/xtoskrnl/includes/ar/amd64/procsup.hh @@ -49,6 +49,7 @@ namespace AR private: STATIC XTAPI VOID IdentifyProcessor(VOID); + STATIC XTAPI VOID IdentifyProcessorFeatures(VOID); STATIC XTAPI VOID InitializeGdt(IN PKPROCESSOR_BLOCK ProcessorBlock); STATIC XTAPI VOID InitializeIdt(IN PKPROCESSOR_BLOCK ProcessorBlock); STATIC XTAPI VOID InitializeProcessorBlock(OUT PKPROCESSOR_BLOCK ProcessorBlock, diff --git a/xtoskrnl/includes/ar/i686/procsup.hh b/xtoskrnl/includes/ar/i686/procsup.hh index 2f1b93c..eb482ed 100644 --- a/xtoskrnl/includes/ar/i686/procsup.hh +++ b/xtoskrnl/includes/ar/i686/procsup.hh @@ -52,6 +52,7 @@ namespace AR private: STATIC XTAPI VOID IdentifyProcessor(VOID); + STATIC XTAPI VOID IdentifyProcessorFeatures(VOID); STATIC XTAPI VOID InitializeGdt(IN PKPROCESSOR_BLOCK ProcessorBlock); STATIC XTAPI VOID InitializeIdt(IN PKPROCESSOR_BLOCK ProcessorBlock); STATIC XTAPI VOID InitializeProcessorBlock(OUT PKPROCESSOR_BLOCK ProcessorBlock, @@ -79,7 +80,6 @@ namespace AR IN ULONG_PTR Base); STATIC XTAPI VOID SetNonMaskableInterruptTssEntry(IN PKPROCESSOR_BLOCK ProcessorBlock, IN PVOID KernelNmiStack); - }; } From ac675b037ed42051ce6d7543c1ad65558ef42089 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 19:28:45 +0200 Subject: [PATCH 18/32] Add FeatureBits field to CPU identification structure --- sdk/xtdk/amd64/artypes.h | 1 + sdk/xtdk/i686/artypes.h | 1 + 2 files changed, 2 insertions(+) diff --git a/sdk/xtdk/amd64/artypes.h b/sdk/xtdk/amd64/artypes.h index 84908b5..58984d9 100644 --- a/sdk/xtdk/amd64/artypes.h +++ b/sdk/xtdk/amd64/artypes.h @@ -431,6 +431,7 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame); typedef struct _CPU_IDENTIFICATION { USHORT Family; + ULONGLONG FeatureBits; USHORT Model; USHORT Stepping; CPU_VENDOR Vendor; diff --git a/sdk/xtdk/i686/artypes.h b/sdk/xtdk/i686/artypes.h index 68c506c..cce90ec 100644 --- a/sdk/xtdk/i686/artypes.h +++ b/sdk/xtdk/i686/artypes.h @@ -396,6 +396,7 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame); typedef struct _CPU_IDENTIFICATION { USHORT Family; + ULONGLONG FeatureBits; USHORT Model; USHORT Stepping; CPU_VENDOR Vendor; From 6df6a012d221d352e8ee2bc1fc95a43a0081c739 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 21:12:09 +0200 Subject: [PATCH 19/32] Partially revert ca4f3acc0e --- xtoskrnl/ar/amd64/procsup.cc | 25 +++++++------------------ xtoskrnl/ar/i686/procsup.cc | 25 +++++++------------------ 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/xtoskrnl/ar/amd64/procsup.cc b/xtoskrnl/ar/amd64/procsup.cc index 16aab6b..cd22542 100644 --- a/xtoskrnl/ar/amd64/procsup.cc +++ b/xtoskrnl/ar/amd64/procsup.cc @@ -77,29 +77,13 @@ AR::ProcSup::IdentifyProcessor(VOID) CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; AR::CpuFunc::CpuId(&CpuRegisters); - /* Store CPU vendor name in processor control block */ + /* Store CPU vendor in processor control block */ + Prcb->CpuId.Vendor = (CPU_VENDOR)CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[0] = CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[4] = CpuRegisters.Edx; *(PULONG)&Prcb->CpuId.VendorName[8] = CpuRegisters.Ecx; Prcb->CpuId.VendorName[12] = '\0'; - /* Resolve CPU vendor */ - if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "AuthenticAMD", 12) == 12) - { - /* AMD CPU */ - Prcb->CpuId.Vendor = CPU_VENDOR_AMD; - } - else if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "GenuineIntel", 12) == 12) - { - /* Intel CPU */ - Prcb->CpuId.Vendor = CPU_VENDOR_INTEL; - } - else - { - /* Unknown CPU vendor */ - Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; - } - /* Get CPU standard features */ RtlZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; @@ -134,6 +118,11 @@ AR::ProcSup::IdentifyProcessor(VOID) Prcb->CpuId.Model += (CpuSignature.ExtendedModel << 4); } } + else + { + /* Unknown CPU vendor */ + Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; + } /* Identify processor features */ IdentifyProcessorFeatures(); diff --git a/xtoskrnl/ar/i686/procsup.cc b/xtoskrnl/ar/i686/procsup.cc index 4957b83..2f05926 100644 --- a/xtoskrnl/ar/i686/procsup.cc +++ b/xtoskrnl/ar/i686/procsup.cc @@ -70,29 +70,13 @@ AR::ProcSup::IdentifyProcessor(VOID) CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; AR::CpuFunc::CpuId(&CpuRegisters); - /* Store CPU vendor name in processor control block */ + /* Store CPU vendor in processor control block */ + Prcb->CpuId.Vendor = (CPU_VENDOR)CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[0] = CpuRegisters.Ebx; *(PULONG)&Prcb->CpuId.VendorName[4] = CpuRegisters.Edx; *(PULONG)&Prcb->CpuId.VendorName[8] = CpuRegisters.Ecx; Prcb->CpuId.VendorName[12] = '\0'; - /* Resolve CPU vendor */ - if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "AuthenticAMD", 12) == 12) - { - /* AMD CPU */ - Prcb->CpuId.Vendor = CPU_VENDOR_AMD; - } - else if(RTL::Memory::CompareMemory(Prcb->CpuId.VendorName, "GenuineIntel", 12) == 12) - { - /* Intel CPU */ - Prcb->CpuId.Vendor = CPU_VENDOR_INTEL; - } - else - { - /* Unknown CPU vendor */ - Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; - } - /* Get CPU standard features */ RtlZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; @@ -127,6 +111,11 @@ AR::ProcSup::IdentifyProcessor(VOID) Prcb->CpuId.Model += (CpuSignature.ExtendedModel << 4); } } + else + { + /* Unknown CPU vendor */ + Prcb->CpuId.Vendor = CPU_VENDOR_UNKNOWN; + } /* Identify processor features */ IdentifyProcessorFeatures(); From ed52d421ead214fcf768eb1bd716ade03e693fd0 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 21:35:43 +0200 Subject: [PATCH 20/32] Implement processor feature enumeration mapping --- sdk/xtdk/amd64/ketypes.h | 56 ++++++++++++++++ sdk/xtdk/i686/ketypes.h | 56 ++++++++++++++++ xtoskrnl/ar/amd64/procsup.cc | 121 +++++++++++++++++++++++++++++++++++ xtoskrnl/ar/i686/procsup.cc | 121 +++++++++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+) diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index b2d4a37..e4eb649 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -69,6 +69,62 @@ #define AMD64_INTERRUPT_GATE 0xE #define AMD64_TRAP_GATE 0xF +/* Kernel CPU Standard Features */ +#define KCF_VME (1ULL << 0) /* Virtual 8086 Mode Enhancements */ +#define KCF_LARGE_PAGE (1ULL << 1) /* Page Size Extensions */ +#define KCF_RDTSC (1ULL << 2) /* Time Stamp Counter */ +#define KCF_PAE (1ULL << 3) /* Physical Address Extension */ +#define KCF_MCE (1ULL << 4) /* Machine Check Exception */ +#define KCF_CMPXCHG8B (1ULL << 5) /* CMPXCHG8B Instruction */ +#define KCF_APIC (1ULL << 6) /* APIC On-Chip */ +#define KCF_FAST_SYSCALL (1ULL << 7) /* SYSENTER/SYSEXIT Instructions */ +#define KCF_MTRR (1ULL << 8) /* Memory Type Range Registers */ +#define KCF_GLOBAL_PAGE (1ULL << 9) /* Page Global Enable */ +#define KCF_MCA (1ULL << 10) /* Machine Check Architecture */ +#define KCF_CMOV (1ULL << 11) /* Conditional Move Instructions */ +#define KCF_PAT (1ULL << 12) /* Page Attribute Table */ +#define KCF_PSE36 (1ULL << 13) /* 36-bit Page Size Extension */ +#define KCF_CLFLUSH (1ULL << 14) /* CLFLUSH Instruction */ +#define KCF_FXSR (1ULL << 15) /* FXSAVE/FXRSTOR Instructions */ +#define KCF_ACPI (1ULL << 16) /* Thermal Monitor and Software Controlled Clock */ +#define KCF_MMX (1ULL << 17) /* MMX Technology */ +#define KCF_SSE (1ULL << 18) /* Streaming SIMD Extensions */ +#define KCF_SSE2 (1ULL << 19) /* Streaming SIMD Extensions 2 */ +#define KCF_SMT (1ULL << 20) /* Hyper-Threading Technology */ +#define KCF_SSE3 (1ULL << 21) /* Streaming SIMD Extensions 3 */ +#define KCF_VMX (1ULL << 22) /* Intel Virtual Machine Extensions */ +#define KCF_SSSE3 (1ULL << 23) /* Supplemental SSE3 Instructions */ +#define KCF_SSE41 (1ULL << 24) /* SSE4.1 Instructions */ +#define KCF_SSE42 (1ULL << 25) /* SSE4.2 Instructions */ +#define KCF_X2APIC (1ULL << 26) /* x2APIC Support */ +#define KCF_POPCNT (1ULL << 27) /* POPCNT Instruction */ +#define KCF_TSC_DEADLINE (1ULL << 28) /* TSC Deadline Timer */ +#define KCF_AES (1ULL << 29) /* AES-NI Instruction Set */ +#define KCF_XSAVE (1ULL << 30) /* XSAVE/XRSTOR Instructions */ +#define KCF_AVX (1ULL << 31) /* Advanced Vector Extensions */ +#define KCF_RDRAND (1ULL << 32) /* RDRAND Instruction */ +#define KCF_FSGSBASE (1ULL << 33) /* RDFSBASE/WRFSBASE Instructions */ +#define KCF_AVX2 (1ULL << 34) /* AVX2 Instructions */ +#define KCF_SMEP (1ULL << 35) /* Supervisor Mode Execution Prevention */ +#define KCF_RDSEED (1ULL << 36) /* RDSEED Instruction */ +#define KCF_SMAP (1ULL << 37) /* Supervisor Mode Access Prevention */ +#define KCF_SHA (1ULL << 38) /* SHA Extensions */ +#define KCF_LA57 (1ULL << 39) /* 57-bit Linear Addresses */ +#define KCF_ARAT (1ULL << 40) /* Always Running APIC Timer */ + +/* Kernel CPU Extended Features */ +#define KCF_SVM (1ULL << 0) /* AMD Secure Virtual Machine */ +#define KCF_SSE4A (1ULL << 1) /* SSE4A Instructions */ +#define KCF_FMA4 (1ULL << 2) /* FMA4 Instructions */ +#define KCF_TOPOEXT (1ULL << 3) /* AMD Topology Extensions */ +#define KCF_SYSCALL (1ULL << 4) /* SYSCALL/SYSRET Instructions */ +#define KCF_NX_BIT (1ULL << 5) /* No-Execute Page Protection */ +#define KCF_RDTSCP (1ULL << 6) /* RDTSCP Instruction */ +#define KCF_64BIT (1ULL << 7) /* Long Mode Support */ +#define KCF_3DNOW_EXT (1ULL << 8) /* 3DNow! Extensions */ +#define KCF_3DNOW (1ULL << 9) /* 3DNow! Instructions */ +#define KCF_INVARIANT_TSC (1ULL << 10) /* Invariant Time Stamp Counter */ + /* Context control flags */ #define CONTEXT_ARCHITECTURE 0x00100000 #define CONTEXT_CONTROL (CONTEXT_ARCHITECTURE | 0x01) diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index 1c5b0b3..5a7fd47 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -90,6 +90,62 @@ #define I686_INTERRUPT_GATE 0xE #define I686_TRAP_GATE 0xF +/* Kernel CPU Standard Features */ +#define KCF_VME (1ULL << 0) /* Virtual 8086 Mode Enhancements */ +#define KCF_LARGE_PAGE (1ULL << 1) /* Page Size Extensions */ +#define KCF_RDTSC (1ULL << 2) /* Time Stamp Counter */ +#define KCF_PAE (1ULL << 3) /* Physical Address Extension */ +#define KCF_MCE (1ULL << 4) /* Machine Check Exception */ +#define KCF_CMPXCHG8B (1ULL << 5) /* CMPXCHG8B Instruction */ +#define KCF_APIC (1ULL << 6) /* APIC On-Chip */ +#define KCF_FAST_SYSCALL (1ULL << 7) /* SYSENTER/SYSEXIT Instructions */ +#define KCF_MTRR (1ULL << 8) /* Memory Type Range Registers */ +#define KCF_GLOBAL_PAGE (1ULL << 9) /* Page Global Enable */ +#define KCF_MCA (1ULL << 10) /* Machine Check Architecture */ +#define KCF_CMOV (1ULL << 11) /* Conditional Move Instructions */ +#define KCF_PAT (1ULL << 12) /* Page Attribute Table */ +#define KCF_PSE36 (1ULL << 13) /* 36-bit Page Size Extension */ +#define KCF_CLFLUSH (1ULL << 14) /* CLFLUSH Instruction */ +#define KCF_FXSR (1ULL << 15) /* FXSAVE/FXRSTOR Instructions */ +#define KCF_ACPI (1ULL << 16) /* Thermal Monitor and Software Controlled Clock */ +#define KCF_MMX (1ULL << 17) /* MMX Technology */ +#define KCF_SSE (1ULL << 18) /* Streaming SIMD Extensions */ +#define KCF_SSE2 (1ULL << 19) /* Streaming SIMD Extensions 2 */ +#define KCF_SMT (1ULL << 20) /* Hyper-Threading Technology */ +#define KCF_SSE3 (1ULL << 21) /* Streaming SIMD Extensions 3 */ +#define KCF_VMX (1ULL << 22) /* Intel Virtual Machine Extensions */ +#define KCF_SSSE3 (1ULL << 23) /* Supplemental SSE3 Instructions */ +#define KCF_SSE41 (1ULL << 24) /* SSE4.1 Instructions */ +#define KCF_SSE42 (1ULL << 25) /* SSE4.2 Instructions */ +#define KCF_X2APIC (1ULL << 26) /* x2APIC Support */ +#define KCF_POPCNT (1ULL << 27) /* POPCNT Instruction */ +#define KCF_TSC_DEADLINE (1ULL << 28) /* TSC Deadline Timer */ +#define KCF_AES (1ULL << 29) /* AES-NI Instruction Set */ +#define KCF_XSAVE (1ULL << 30) /* XSAVE/XRSTOR Instructions */ +#define KCF_AVX (1ULL << 31) /* Advanced Vector Extensions */ +#define KCF_RDRAND (1ULL << 32) /* RDRAND Instruction */ +#define KCF_FSGSBASE (1ULL << 33) /* RDFSBASE/WRFSBASE Instructions */ +#define KCF_AVX2 (1ULL << 34) /* AVX2 Instructions */ +#define KCF_SMEP (1ULL << 35) /* Supervisor Mode Execution Prevention */ +#define KCF_RDSEED (1ULL << 36) /* RDSEED Instruction */ +#define KCF_SMAP (1ULL << 37) /* Supervisor Mode Access Prevention */ +#define KCF_SHA (1ULL << 38) /* SHA Extensions */ +#define KCF_LA57 (1ULL << 39) /* 57-bit Linear Addresses */ +#define KCF_ARAT (1ULL << 40) /* Always Running APIC Timer */ + +/* Kernel CPU Extended Features */ +#define KCF_SVM (1ULL << 0) /* AMD Secure Virtual Machine */ +#define KCF_SSE4A (1ULL << 1) /* SSE4A Instructions */ +#define KCF_FMA4 (1ULL << 2) /* FMA4 Instructions */ +#define KCF_TOPOEXT (1ULL << 3) /* AMD Topology Extensions */ +#define KCF_SYSCALL (1ULL << 4) /* SYSCALL/SYSRET Instructions */ +#define KCF_NX_BIT (1ULL << 5) /* No-Execute Page Protection */ +#define KCF_RDTSCP (1ULL << 6) /* RDTSCP Instruction */ +#define KCF_64BIT (1ULL << 7) /* Long Mode Support */ +#define KCF_3DNOW_EXT (1ULL << 8) /* 3DNow! Extensions */ +#define KCF_3DNOW (1ULL << 9) /* 3DNow! Instructions */ +#define KCF_INVARIANT_TSC (1ULL << 10) /* Invariant Time Stamp Counter */ + /* Context control flags */ #define CONTEXT_ARCHITECTURE 0x00010000 #define CONTEXT_CONTROL (CONTEXT_ARCHITECTURE | 0x01) diff --git a/xtoskrnl/ar/amd64/procsup.cc b/xtoskrnl/ar/amd64/procsup.cc index cd22542..63ad4f4 100644 --- a/xtoskrnl/ar/amd64/procsup.cc +++ b/xtoskrnl/ar/amd64/procsup.cc @@ -139,10 +139,131 @@ XTAPI VOID AR::ProcSup::IdentifyProcessorFeatures(VOID) { + ULONG MaxExtendedLeaf, MaxStandardLeaf; PKPROCESSOR_CONTROL_BLOCK Prcb; + CPUID_REGISTERS CpuRegisters; /* Get current processor control block */ Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Get maximum CPUID standard leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; + AR::CpuFunc::CpuId(&CpuRegisters); + MaxStandardLeaf = CpuRegisters.Eax; + + /* Get maximum CPUID extended leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_EXTENDED_MAX; + AR::CpuFunc::CpuId(&CpuRegisters); + MaxExtendedLeaf = CpuRegisters.Eax; + + /* Check if CPU supports standard features leaf */ + if(MaxStandardLeaf >= CPUID_GET_STANDARD1_FEATURES) + { + /* Get CPU standard features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU standard features in processor control block */ + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE3) Prcb->CpuId.FeatureBits |= KCF_SSE3; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_VMX) Prcb->CpuId.FeatureBits |= KCF_VMX; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSSE3) Prcb->CpuId.FeatureBits |= KCF_SSSE3; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4_1) Prcb->CpuId.FeatureBits |= KCF_SSE41; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4_2) Prcb->CpuId.FeatureBits |= KCF_SSE42; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_X2APIC) Prcb->CpuId.FeatureBits |= KCF_X2APIC; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_POPCNT) Prcb->CpuId.FeatureBits |= KCF_POPCNT; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TSC_DEADLINE) Prcb->CpuId.FeatureBits |= KCF_TSC_DEADLINE; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_AES) Prcb->CpuId.FeatureBits |= KCF_AES; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_XSAVE) Prcb->CpuId.FeatureBits |= KCF_XSAVE; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_AVX) Prcb->CpuId.FeatureBits |= KCF_AVX; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_RDRAND) Prcb->CpuId.FeatureBits |= KCF_RDRAND; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_VME) Prcb->CpuId.FeatureBits |= KCF_VME; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PSE) Prcb->CpuId.FeatureBits |= KCF_LARGE_PAGE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSC) Prcb->CpuId.FeatureBits |= KCF_RDTSC; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PAE) Prcb->CpuId.FeatureBits |= KCF_PAE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MCE) Prcb->CpuId.FeatureBits |= KCF_MCE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CX8) Prcb->CpuId.FeatureBits |= KCF_CMPXCHG8B; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_APIC) Prcb->CpuId.FeatureBits |= KCF_APIC; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SEP) Prcb->CpuId.FeatureBits |= KCF_FAST_SYSCALL; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MTRR) Prcb->CpuId.FeatureBits |= KCF_MTRR; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PGE) Prcb->CpuId.FeatureBits |= KCF_GLOBAL_PAGE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MCA) Prcb->CpuId.FeatureBits |= KCF_MCA; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CMOV) Prcb->CpuId.FeatureBits |= KCF_CMOV; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PAT) Prcb->CpuId.FeatureBits |= KCF_PAT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PSE36) Prcb->CpuId.FeatureBits |= KCF_PSE36; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CLFLUSH) Prcb->CpuId.FeatureBits |= KCF_CLFLUSH; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_FXSR) Prcb->CpuId.FeatureBits |= KCF_FXSR; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_ACPI) Prcb->CpuId.FeatureBits |= KCF_ACPI; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MMX) Prcb->CpuId.FeatureBits |= KCF_MMX; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SSE) Prcb->CpuId.FeatureBits |= KCF_SSE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SSE2) Prcb->CpuId.FeatureBits |= KCF_SSE2; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_HTT) Prcb->CpuId.FeatureBits |= KCF_SMT; + } + + /* Check if CPU supports standard7 features leaf */ + if(MaxStandardLeaf >= CPUID_GET_STANDARD7_FEATURES) + { + /* Get CPU standard features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_STANDARD7_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU standard7 features in processor control block */ + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_FSGSBASE) Prcb->CpuId.FeatureBits |= KCF_FSGSBASE; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_AVX2) Prcb->CpuId.FeatureBits |= KCF_AVX2; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SMEP) Prcb->CpuId.FeatureBits |= KCF_SMEP; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_RDSEED) Prcb->CpuId.FeatureBits |= KCF_RDSEED; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SMAP) Prcb->CpuId.FeatureBits |= KCF_SMAP; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SHA) Prcb->CpuId.FeatureBits |= KCF_SHA; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_LA57) Prcb->CpuId.FeatureBits |= KCF_LA57; + } + + /* Check if CPU supports power management leaf */ + if(MaxStandardLeaf >= CPUID_GET_POWER_MANAGEMENT) + { + /* Get CPU power management features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_POWER_MANAGEMENT; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU power management features in processor control block */ + if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) Prcb->CpuId.FeatureBits |= KCF_ARAT; + } + + /* Check if CPU supports extended features leaf */ + if(MaxExtendedLeaf >= CPUID_GET_EXTENDED_FEATURES) + { + /* Get CPU extended features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_EXTENDED_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU extended features in processor control block */ + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SVM) Prcb->CpuId.ExtendedFeatureBits |= KCF_SVM; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4A) Prcb->CpuId.ExtendedFeatureBits |= KCF_SSE4A; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_FMA4) Prcb->CpuId.ExtendedFeatureBits |= KCF_FMA4; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TOPOLOGY_EXTENSIONS) Prcb->CpuId.ExtendedFeatureBits |= KCF_TOPOEXT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SYSCALL_SYSRET) Prcb->CpuId.ExtendedFeatureBits |= KCF_SYSCALL; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_NX) Prcb->CpuId.ExtendedFeatureBits |= KCF_NX_BIT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_RDTSCP) Prcb->CpuId.ExtendedFeatureBits |= KCF_RDTSCP; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_LONG_MODE) Prcb->CpuId.ExtendedFeatureBits |= KCF_64BIT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_3DNOW_EXT) Prcb->CpuId.ExtendedFeatureBits |= KCF_3DNOW_EXT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_3DNOW) Prcb->CpuId.ExtendedFeatureBits |= KCF_3DNOW; + } + + /* Check if CPU supports advanced power management leaf */ + if(MaxExtendedLeaf >= CPUID_GET_ADVANCED_POWER_MANAGEMENT) + { + /* Get CPU advanced power management features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_ADVANCED_POWER_MANAGEMENT; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU advanced power management features in processor control block */ + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) Prcb->CpuId.ExtendedFeatureBits |= KCF_INVARIANT_TSC; + } } /** diff --git a/xtoskrnl/ar/i686/procsup.cc b/xtoskrnl/ar/i686/procsup.cc index 2f05926..bf049c0 100644 --- a/xtoskrnl/ar/i686/procsup.cc +++ b/xtoskrnl/ar/i686/procsup.cc @@ -132,10 +132,131 @@ XTAPI VOID AR::ProcSup::IdentifyProcessorFeatures(VOID) { + ULONG MaxExtendedLeaf, MaxStandardLeaf; PKPROCESSOR_CONTROL_BLOCK Prcb; + CPUID_REGISTERS CpuRegisters; /* Get current processor control block */ Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Get maximum CPUID standard leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; + AR::CpuFunc::CpuId(&CpuRegisters); + MaxStandardLeaf = CpuRegisters.Eax; + + /* Get maximum CPUID extended leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_EXTENDED_MAX; + AR::CpuFunc::CpuId(&CpuRegisters); + MaxExtendedLeaf = CpuRegisters.Eax; + + /* Check if CPU supports standard features leaf */ + if(MaxStandardLeaf >= CPUID_GET_STANDARD1_FEATURES) + { + /* Get CPU standard features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU standard features in processor control block */ + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE3) Prcb->CpuId.FeatureBits |= KCF_SSE3; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_VMX) Prcb->CpuId.FeatureBits |= KCF_VMX; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSSE3) Prcb->CpuId.FeatureBits |= KCF_SSSE3; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4_1) Prcb->CpuId.FeatureBits |= KCF_SSE41; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4_2) Prcb->CpuId.FeatureBits |= KCF_SSE42; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_X2APIC) Prcb->CpuId.FeatureBits |= KCF_X2APIC; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_POPCNT) Prcb->CpuId.FeatureBits |= KCF_POPCNT; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TSC_DEADLINE) Prcb->CpuId.FeatureBits |= KCF_TSC_DEADLINE; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_AES) Prcb->CpuId.FeatureBits |= KCF_AES; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_XSAVE) Prcb->CpuId.FeatureBits |= KCF_XSAVE; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_AVX) Prcb->CpuId.FeatureBits |= KCF_AVX; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_RDRAND) Prcb->CpuId.FeatureBits |= KCF_RDRAND; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_VME) Prcb->CpuId.FeatureBits |= KCF_VME; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PSE) Prcb->CpuId.FeatureBits |= KCF_LARGE_PAGE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSC) Prcb->CpuId.FeatureBits |= KCF_RDTSC; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PAE) Prcb->CpuId.FeatureBits |= KCF_PAE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MCE) Prcb->CpuId.FeatureBits |= KCF_MCE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CX8) Prcb->CpuId.FeatureBits |= KCF_CMPXCHG8B; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_APIC) Prcb->CpuId.FeatureBits |= KCF_APIC; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SEP) Prcb->CpuId.FeatureBits |= KCF_FAST_SYSCALL; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MTRR) Prcb->CpuId.FeatureBits |= KCF_MTRR; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PGE) Prcb->CpuId.FeatureBits |= KCF_GLOBAL_PAGE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MCA) Prcb->CpuId.FeatureBits |= KCF_MCA; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CMOV) Prcb->CpuId.FeatureBits |= KCF_CMOV; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PAT) Prcb->CpuId.FeatureBits |= KCF_PAT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_PSE36) Prcb->CpuId.FeatureBits |= KCF_PSE36; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_CLFLUSH) Prcb->CpuId.FeatureBits |= KCF_CLFLUSH; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_FXSR) Prcb->CpuId.FeatureBits |= KCF_FXSR; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_ACPI) Prcb->CpuId.FeatureBits |= KCF_ACPI; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_MMX) Prcb->CpuId.FeatureBits |= KCF_MMX; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SSE) Prcb->CpuId.FeatureBits |= KCF_SSE; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SSE2) Prcb->CpuId.FeatureBits |= KCF_SSE2; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_HTT) Prcb->CpuId.FeatureBits |= KCF_SMT; + } + + /* Check if CPU supports standard7 features leaf */ + if(MaxStandardLeaf >= CPUID_GET_STANDARD7_FEATURES) + { + /* Get CPU standard features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_STANDARD7_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU standard7 features in processor control block */ + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_FSGSBASE) Prcb->CpuId.FeatureBits |= KCF_FSGSBASE; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_AVX2) Prcb->CpuId.FeatureBits |= KCF_AVX2; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SMEP) Prcb->CpuId.FeatureBits |= KCF_SMEP; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_RDSEED) Prcb->CpuId.FeatureBits |= KCF_RDSEED; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SMAP) Prcb->CpuId.FeatureBits |= KCF_SMAP; + if(CpuRegisters.Ebx & CPUID_FEATURES_EBX_SHA) Prcb->CpuId.FeatureBits |= KCF_SHA; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_LA57) Prcb->CpuId.FeatureBits |= KCF_LA57; + } + + /* Check if CPU supports power management leaf */ + if(MaxStandardLeaf >= CPUID_GET_POWER_MANAGEMENT) + { + /* Get CPU power management features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_POWER_MANAGEMENT; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU power management features in processor control block */ + if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) Prcb->CpuId.FeatureBits |= KCF_ARAT; + } + + /* Check if CPU supports extended features leaf */ + if(MaxExtendedLeaf >= CPUID_GET_EXTENDED_FEATURES) + { + /* Get CPU extended features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_EXTENDED_FEATURES; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU extended features in processor control block */ + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SVM) Prcb->CpuId.ExtendedFeatureBits |= KCF_SVM; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_SSE4A) Prcb->CpuId.ExtendedFeatureBits |= KCF_SSE4A; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_FMA4) Prcb->CpuId.ExtendedFeatureBits |= KCF_FMA4; + if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TOPOLOGY_EXTENSIONS) Prcb->CpuId.ExtendedFeatureBits |= KCF_TOPOEXT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_SYSCALL_SYSRET) Prcb->CpuId.ExtendedFeatureBits |= KCF_SYSCALL; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_NX) Prcb->CpuId.ExtendedFeatureBits |= KCF_NX_BIT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_RDTSCP) Prcb->CpuId.ExtendedFeatureBits |= KCF_RDTSCP; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_LONG_MODE) Prcb->CpuId.ExtendedFeatureBits |= KCF_64BIT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_3DNOW_EXT) Prcb->CpuId.ExtendedFeatureBits |= KCF_3DNOW_EXT; + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_3DNOW) Prcb->CpuId.ExtendedFeatureBits |= KCF_3DNOW; + } + + /* Check if CPU supports advanced power management leaf */ + if(MaxExtendedLeaf >= CPUID_GET_ADVANCED_POWER_MANAGEMENT) + { + /* Get CPU advanced power management features */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); + CpuRegisters.Leaf = CPUID_GET_ADVANCED_POWER_MANAGEMENT; + AR::CpuFunc::CpuId(&CpuRegisters); + + /* Store CPU advanced power management features in processor control block */ + if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) Prcb->CpuId.ExtendedFeatureBits |= KCF_INVARIANT_TSC; + } } /** From 63d18aad1e63bb1e383b54d8e1fcce9c7f631130 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 21:37:11 +0200 Subject: [PATCH 21/32] Add ExtendedFeatureBits field --- sdk/xtdk/amd64/artypes.h | 1 + sdk/xtdk/i686/artypes.h | 1 + 2 files changed, 2 insertions(+) diff --git a/sdk/xtdk/amd64/artypes.h b/sdk/xtdk/amd64/artypes.h index 58984d9..ae187e4 100644 --- a/sdk/xtdk/amd64/artypes.h +++ b/sdk/xtdk/amd64/artypes.h @@ -430,6 +430,7 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame); /* Processor identification information */ typedef struct _CPU_IDENTIFICATION { + ULONGLONG ExtendedFeatureBits; USHORT Family; ULONGLONG FeatureBits; USHORT Model; diff --git a/sdk/xtdk/i686/artypes.h b/sdk/xtdk/i686/artypes.h index cce90ec..c3e335b 100644 --- a/sdk/xtdk/i686/artypes.h +++ b/sdk/xtdk/i686/artypes.h @@ -395,6 +395,7 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame); /* Processor identification information */ typedef struct _CPU_IDENTIFICATION { + ULONGLONG ExtendedFeatureBits; USHORT Family; ULONGLONG FeatureBits; USHORT Model; From 14cbd63b01982d3070570dbf68a59f19eaf99e61 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 14 May 2026 21:52:42 +0200 Subject: [PATCH 22/32] Remove premature SetRunLevel call from InitializeProcessor before APIC initialization --- xtoskrnl/ar/amd64/procsup.cc | 3 --- xtoskrnl/ar/i686/procsup.cc | 3 --- 2 files changed, 6 deletions(-) diff --git a/xtoskrnl/ar/amd64/procsup.cc b/xtoskrnl/ar/amd64/procsup.cc index 63ad4f4..7f103dd 100644 --- a/xtoskrnl/ar/amd64/procsup.cc +++ b/xtoskrnl/ar/amd64/procsup.cc @@ -405,9 +405,6 @@ AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); - /* Enter passive IRQ level */ - HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); - /* Initialize segment registers */ InitializeSegments(); diff --git a/xtoskrnl/ar/i686/procsup.cc b/xtoskrnl/ar/i686/procsup.cc index bf049c0..8940e95 100644 --- a/xtoskrnl/ar/i686/procsup.cc +++ b/xtoskrnl/ar/i686/procsup.cc @@ -401,9 +401,6 @@ AR::ProcSup::InitializeProcessor(IN PVOID ProcessorStructures) AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit); AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS); - /* Enter passive IRQ level */ - HL::RunLevel::SetRunLevel(PASSIVE_LEVEL); - /* Initialize segment registers */ InitializeSegments(); From efff262fb502e33cfc0d41901b0640c14baadb55 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 15 May 2026 09:13:56 +0200 Subject: [PATCH 23/32] Replace CPUID queries with cached PRCB feature bits --- xtoskrnl/hl/x86/pic.cc | 50 +++++++++--------------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/xtoskrnl/hl/x86/pic.cc b/xtoskrnl/hl/x86/pic.cc index 7d546f6..0132078 100644 --- a/xtoskrnl/hl/x86/pic.cc +++ b/xtoskrnl/hl/x86/pic.cc @@ -78,28 +78,13 @@ XTAPI BOOLEAN HL::Pic::CheckApicSupport(VOID) { - CPUID_REGISTERS CpuRegisters; + PKPROCESSOR_CONTROL_BLOCK Prcb; - /* Prepare CPUID registers */ - CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; + /* Get current processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); - /* Get CPUID */ - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Check APIC status from the CPUID results */ - if(!(CpuRegisters.Edx & CPUID_FEATURES_EDX_APIC)) - { - /* APIC is not supported */ - return FALSE; - } - - /* APIC is supported */ - return TRUE; + /* Return APIC status */ + return (Prcb->CpuId.FeatureBits & KCF_APIC) ? TRUE : FALSE; } /** @@ -116,28 +101,13 @@ XTAPI BOOLEAN HL::Pic::CheckX2ApicSupport(VOID) { - CPUID_REGISTERS CpuRegisters; + PKPROCESSOR_CONTROL_BLOCK Prcb; - /* Prepare CPUID registers */ - CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; + /* Get current processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); - /* Get CPUID */ - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Check x2APIC status from the CPUID results */ - if(!(CpuRegisters.Ecx & CPUID_FEATURES_ECX_X2APIC)) - { - /* x2APIC is not supported */ - return FALSE; - } - - /* x2APIC is supported */ - return TRUE; + /* Return x2APIC status */ + return (Prcb->CpuId.FeatureBits & KCF_X2APIC) ? TRUE : FALSE; } /** From 38b2e7a1edf903728d60fddb8438b26cc287eea0 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 15 May 2026 11:33:23 +0200 Subject: [PATCH 24/32] Read previously identified CPU features from PRCB --- xtoskrnl/hl/x86/timer.cc | 117 ++++----------------------------------- 1 file changed, 12 insertions(+), 105 deletions(-) diff --git a/xtoskrnl/hl/x86/timer.cc b/xtoskrnl/hl/x86/timer.cc index cb63f6c..7aad052 100644 --- a/xtoskrnl/hl/x86/timer.cc +++ b/xtoskrnl/hl/x86/timer.cc @@ -925,84 +925,31 @@ XTAPI VOID HL::Timer::QueryTimerCapabilities(VOID) { + PKPROCESSOR_CONTROL_BLOCK Prcb; CPUID_REGISTERS CpuRegisters; ULONG MaxStandardLeaf; - ULONG MaxExtendedLeaf; + + /* Get current processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Set timer capabilities based on supported CPU features */ + TimerCapabilities.Arat = (Prcb->CpuId.FeatureBits & KCF_ARAT) != 0; + TimerCapabilities.InvariantTsc= (Prcb->CpuId.ExtendedFeatureBits & KCF_INVARIANT_TSC) != 0; + TimerCapabilities.RDTSCP = (Prcb->CpuId.ExtendedFeatureBits & KCF_RDTSCP) != 0; + TimerCapabilities.TscDeadline = (Prcb->CpuId.FeatureBits & KCF_TSC_DEADLINE) != 0; /* Query maximum standard CPUID leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_VENDOR_STRING; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; AR::CpuFunc::CpuId(&CpuRegisters); - - /* Save maximum supported standard CPUID leaf */ MaxStandardLeaf = CpuRegisters.Eax; - /* Query maximum extended CPUID leaf */ - CpuRegisters.Leaf = CPUID_GET_EXTENDED_MAX; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Save maximum supported extended CPUID leaf */ - MaxExtendedLeaf = CpuRegisters.Eax; - - /* Check TSC-Deadline mode if leaf supported */ - if(MaxStandardLeaf >= CPUID_GET_STANDARD1_FEATURES) - { - /* Query the standard feature CPUI leaf */ - CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Verify TSC-Deadline support */ - if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TSC_DEADLINE) - { - /* Mark the TSC-Deadline mode as supported */ - TimerCapabilities.TscDeadline = TRUE; - } - } - - /* Check Always Running APIC Timer - ARAT if leaf supported */ - if(MaxStandardLeaf >= CPUID_GET_POWER_MANAGEMENT) - { - /* Query the thermal and power management features CPUID leaf */ - CpuRegisters.Leaf = CPUID_GET_POWER_MANAGEMENT; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Verify Always Running APIC Timer support */ - if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) - { - /* Mark the ARAT as supported */ - TimerCapabilities.Arat = TRUE; - } - } - /* Check Always Running Timer - ART if leaf supported */ if(MaxStandardLeaf >= CPUID_GET_TSC_CRYSTAL_CLOCK) { /* Query the Time Stamp Counter and Core Crystal Clock information CPUID leaf */ + RTL::Memory::ZeroMemory(&CpuRegisters, sizeof(CPUID_REGISTERS)); CpuRegisters.Leaf = CPUID_GET_TSC_CRYSTAL_CLOCK; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; AR::CpuFunc::CpuId(&CpuRegisters); /* Verify Always Running Timer support */ @@ -1023,46 +970,6 @@ HL::Timer::QueryTimerCapabilities(VOID) } } } - - /* Check RDTSCP instruction support if leaf supported */ - if(MaxExtendedLeaf >= CPUID_GET_EXTENDED_FEATURES) - { - /* Query the extended processor features CPUID leaf */ - CpuRegisters.Leaf = CPUID_GET_EXTENDED_FEATURES; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Verify RDTSCP support */ - if(CpuRegisters.Edx & CPUID_FEATURES_EDX_RDTSCP) - { - /* Mark the RDTSCP instruction as supported */ - TimerCapabilities.RDTSCP = TRUE; - } - } - - /* Check Invariant TSC if leaf supported */ - if(MaxExtendedLeaf >= CPUID_GET_ADVANCED_POWER_MANAGEMENT) - { - /* Query the advanced power management features CPUID leaf */ - CpuRegisters.Leaf = CPUID_GET_ADVANCED_POWER_MANAGEMENT; - CpuRegisters.SubLeaf = 0; - CpuRegisters.Eax = 0; - CpuRegisters.Ebx = 0; - CpuRegisters.Ecx = 0; - CpuRegisters.Edx = 0; - AR::CpuFunc::CpuId(&CpuRegisters); - - /* Verify Invariant TSC support */ - if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) - { - /* Mark the Invariant TSC feature as supported */ - TimerCapabilities.InvariantTsc = TRUE; - } - } } /** From c824e15cdbc41b103305ce340549bd680dd1a227 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 15 May 2026 11:42:19 +0200 Subject: [PATCH 25/32] Fix stack allocation documentation and remove redundant parameter --- xtoskrnl/mm/kpool.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/xtoskrnl/mm/kpool.cc b/xtoskrnl/mm/kpool.cc index 36b3062..1673ac6 100644 --- a/xtoskrnl/mm/kpool.cc +++ b/xtoskrnl/mm/kpool.cc @@ -16,11 +16,8 @@ * @param Stack * Supplies a pointer to the memory area that will contain a new kernel stack. * - * @param LargeStack - * Determines whether the stack is large or small. - * - * @param SystemNode - * Specifies a preferred node used for new stack on multi-processor systems. + * @param StackSize + * Supplies the size of the stack to be allocated, in bytes. * * @return This routine returns a status code. * @@ -133,8 +130,8 @@ MM::KernelPool::AllocateProcessorStructures(OUT PVOID *StructuresData) * @param Stack * Supplies a pointer to the memory area containing a kernel stack. * - * @param LargeStack - * Determines whether the stack is large or small. + * @param StackSize + * Supplies the size of the stack to be freed, in bytes. * * @return This routine does not return any value. * @@ -197,6 +194,6 @@ MM::KernelPool::FreeProcessorStructures(IN PVOID StructuresData) if(StructuresData != NULLPTR) { /* Release the contiguous memory block back */ - MM::Allocator::FreePool(StructuresData, 0); + MM::Allocator::FreePool(StructuresData); } } From 8ee97ac0ae45560b4970241f2ccba0f0faf65511 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 15 May 2026 12:00:50 +0200 Subject: [PATCH 26/32] Add NOX2APIC boot parameter to bypass hardware detection --- xtoskrnl/README.md | 3 +++ xtoskrnl/hl/x86/pic.cc | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/xtoskrnl/README.md b/xtoskrnl/README.md index 924c95e..318a28f 100644 --- a/xtoskrnl/README.md +++ b/xtoskrnl/README.md @@ -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. + * **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. * **NOXPA**: Disables PAE or LA57 support, depending on the CPU architecture. This parameter is handled by the bootloader, which configures paging and selects the appropriate Page Map Level (PML) before transferring control to the kernel. diff --git a/xtoskrnl/hl/x86/pic.cc b/xtoskrnl/hl/x86/pic.cc index 0132078..9e8fb70 100644 --- a/xtoskrnl/hl/x86/pic.cc +++ b/xtoskrnl/hl/x86/pic.cc @@ -102,6 +102,14 @@ BOOLEAN HL::Pic::CheckX2ApicSupport(VOID) { PKPROCESSOR_CONTROL_BLOCK Prcb; + PCWSTR KernelParameter; + + /* Check if the user forced xAPIC via boot parameters */ + if(KE::BootInformation::GetKernelParameter(L"NOX2APIC", &KernelParameter) == STATUS_SUCCESS) + { + /* The NOX2APIC flag is present, explicitly disable x2APIC support */ + return FALSE; + } /* Get current processor control block */ Prcb = KE::Processor::GetCurrentProcessorControlBlock(); From 29368a0dd8aa2b173a26cb65288fae0152947ddc Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 15 May 2026 15:02:22 +0200 Subject: [PATCH 27/32] Implement string to number conversion functions --- sdk/xtdk/xtstatus.h | 2 + xtoskrnl/includes/rtl/string.hh | 3 + xtoskrnl/includes/rtl/widestr.hh | 3 + xtoskrnl/rtl/string.cc | 186 +++++++++++++++++++++++++++++++ xtoskrnl/rtl/widestr.cc | 186 +++++++++++++++++++++++++++++++ 5 files changed, 380 insertions(+) diff --git a/sdk/xtdk/xtstatus.h b/sdk/xtdk/xtstatus.h index 169f507..0803ba2 100644 --- a/sdk/xtdk/xtstatus.h +++ b/sdk/xtdk/xtstatus.h @@ -61,6 +61,8 @@ #define STATUS_NO_MEMORY ((XTSTATUS) 0xC0000017L) #define STATUS_PORT_DISCONNECTED ((XTSTATUS) 0xC0000037L) #define STATUS_CRC_ERROR ((XTSTATUS) 0xC000003FL) +#define STATUS_FLOAT_OVERFLOW ((XTSTATUS) 0xC0000091L) +#define STATUS_INTEGER_OVERFLOW ((XTSTATUS) 0xC0000095L) #define STATUS_INSUFFICIENT_RESOURCES ((XTSTATUS) 0xC000009AL) #define STATUS_DEVICE_NOT_READY ((XTSTATUS) 0xC00000A3L) #define STATUS_NOT_SUPPORTED ((XTSTATUS) 0xC00000BBL) diff --git a/xtoskrnl/includes/rtl/string.hh b/xtoskrnl/includes/rtl/string.hh index 8d1ca18..f6feec4 100644 --- a/xtoskrnl/includes/rtl/string.hh +++ b/xtoskrnl/includes/rtl/string.hh @@ -38,6 +38,9 @@ namespace RTL IN ULONG Length); STATIC XTAPI SIZE_T StringLength(IN PCSTR String, IN SIZE_T MaxLength); + STATIC XTAPI XTSTATUS StringToNumber(IN PCSTR String, + IN ULONG Base, + OUT PULONG Value); STATIC XTAPI SIZE_T StringToWideString(OUT PWCHAR Destination, IN PCSTR *Source, IN SIZE_T Length); diff --git a/xtoskrnl/includes/rtl/widestr.hh b/xtoskrnl/includes/rtl/widestr.hh index 66175fe..8b4b459 100644 --- a/xtoskrnl/includes/rtl/widestr.hh +++ b/xtoskrnl/includes/rtl/widestr.hh @@ -49,6 +49,9 @@ namespace RTL STATIC XTAPI PWCHAR TrimWideString(IN PWCHAR String); STATIC XTAPI SIZE_T WideStringLength(IN PCWSTR String, IN SIZE_T MaxLength); + STATIC XTAPI XTSTATUS WideStringToNumber(IN PCWSTR String, + IN ULONG Base, + OUT PULONG Value); private: STATIC XTAPI XTSTATUS FormatArgumentSpecifier(IN PRTL_PRINT_CONTEXT Context, diff --git a/xtoskrnl/rtl/string.cc b/xtoskrnl/rtl/string.cc index df0812a..12b5182 100644 --- a/xtoskrnl/rtl/string.cc +++ b/xtoskrnl/rtl/string.cc @@ -427,6 +427,192 @@ RTL::String::StringLength(IN PCSTR String, return Length; } +/** + * Converts a string to a number. + * + * @param String + * Supplies a pointer to the NULL-terminated string to convert. + * + * @param Base + * Supplies the optional numerical base for the conversion (2, 8, 10, or 16). + * + * @param Value + * Supplies a pointer to a variable that receives the converted numeric value. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RTL::String::StringToNumber(IN PCSTR String, + IN ULONG Base, + OUT PULONG Value) +{ + BOOLEAN NegativeValue; + ULONG Digit, Result; + + /* Validate input parameters */ + if(!String || !Value) + { + /* Invalid input parameters, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Initialize local variables */ + NegativeValue = FALSE; + Result = 0; + + /* Skip leading whitespaces and control characters */ + while(*String != '\0' && *String <= ' ') + { + /* Advance to the next character */ + String++; + } + + /* Consume and record sign */ + if(*String == '-') + { + /* Set negative value flag and advance to the next character */ + NegativeValue = TRUE; + String++; + } + else if(*String == '+') + { + /* Advance to the next character */ + String++; + } + + /* Autodetect and validate the base */ + if(Base == 0) + { + /* Autodetect base based on prefix */ + if(String[0] == '0') + { + /* Validate prefix */ + if(String[1] == 'x' || String[1] == 'X') + { + /* Hexadecimal base */ + Base = 16; + String += 2; + } + else if(String[1] == 'o' || String[1] == 'O') + { + /* Octal base */ + Base = 8; + String += 2; + } + else if(String[1] == 'b' || String[1] == 'B') + { + /* Binary base */ + Base = 2; + String += 2; + } + else + { + /* Starts with 0 but no known prefix, treat as decimal */ + Base = 10; + } + } + else + { + /* Default to decimal base */ + Base = 10; + } + } + else + { + /* Validate explicitly provided base */ + if(Base != 2 && Base != 8 && Base != 10 && Base != 16) + { + /* Invalid base, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Check if number starts with 0 */ + if(String[0] == '0') + { + /* Check for prefix */ + if(Base == 16 && (String[1] == 'x' || String[1] == 'X')) + { + /* Skip hexadecimal prefix */ + String += 2; + } + else if(Base == 8 && (String[1] == 'o' || String[1] == 'O')) + { + /* Skip octal prefix */ + String += 2; + } + else if(Base == 2 && (String[1] == 'b' || String[1] == 'B')) + { + /* Skip binary prefix */ + String += 2; + } + } + } + + /* Parse string character by character */ + while(*String != '\0') + { + /* Convert character to numeric digit */ + if(*String >= '0' && *String <= '9') + { + /* Convert decimal digit */ + Digit = *String - '0'; + } + else if(*String >= 'A' && *String <= 'F') + { + /* Convert hexadecimal digit */ + Digit = *String - 'A' + 10; + } + else if(*String >= 'a' && *String <= 'f') + { + /* Convert hexadecimal digit */ + Digit = *String - 'a' + 10; + } + else + { + /* Invalid character for a number encountered, stop parsing */ + break; + } + + /* Check if digit is valid for the current base */ + if(Digit >= Base) + { + /* Digit out of range for this base, stop parsing */ + break; + } + + /* Check for integer overflow */ + if((Result > (MAXULONG / Base)) || ((Result * Base) > (MAXULONG - Digit))) + { + /* Integer overflow, return error code */ + return STATUS_INTEGER_OVERFLOW; + } + + /* Accumulate result */ + Result = (Result * Base) + Digit; + + /* Advance to the next character */ + String++; + } + + /* Check for negative value */ + if(NegativeValue) + { + /* Apply sign and return the result */ + *Value = (ULONG)(-(LONG)Result); + } + else + { + /* Return the result */ + *Value = Result; + } + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Converts a multibyte character string to its wide character representation. * diff --git a/xtoskrnl/rtl/widestr.cc b/xtoskrnl/rtl/widestr.cc index dc8dbec..aed6c05 100644 --- a/xtoskrnl/rtl/widestr.cc +++ b/xtoskrnl/rtl/widestr.cc @@ -1524,6 +1524,192 @@ RTL::WideString::WideStringLength(IN PCWSTR String, return Length; } +/** + * Converts a wide string to a number. + * + * @param String + * Supplies a pointer to the NULL-terminated wide string to convert. + * + * @param Base + * Supplies the optional numerical base for the conversion (2, 8, 10, or 16). + * + * @param Value + * Supplies a pointer to a variable that receives the converted numeric value. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RTL::WideString::WideStringToNumber(IN PCWSTR String, + IN ULONG Base, + OUT PULONG Value) +{ + BOOLEAN NegativeValue; + ULONG Digit, Result; + + /* Validate input parameters */ + if(!String || !Value) + { + /* Invalid input parameters, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Initialize local variables */ + NegativeValue = FALSE; + Result = 0; + + /* Skip leading whitespaces and control characters */ + while(*String != L'\0' && *String <= L' ') + { + /* Advance to the next character */ + String++; + } + + /* Consume and record sign */ + if(*String == L'-') + { + /* Set negative value flag and advance to the next character */ + NegativeValue = TRUE; + String++; + } + else if(*String == L'+') + { + /* Advance to the next character */ + String++; + } + + /* Autodetect and validate the base */ + if(Base == 0) + { + /* Autodetect base based on prefix */ + if(String[0] == L'0') + { + /* Validate prefix */ + if(String[1] == L'x' || String[1] == L'X') + { + /* Hexadecimal base */ + Base = 16; + String += 2; + } + else if(String[1] == L'o' || String[1] == L'O') + { + /* Octal base */ + Base = 8; + String += 2; + } + else if(String[1] == L'b' || String[1] == L'B') + { + /* Binary base */ + Base = 2; + String += 2; + } + else + { + /* Starts with 0 but no known prefix, treat as decimal */ + Base = 10; + } + } + else + { + /* Default to decimal base */ + Base = 10; + } + } + else + { + /* Validate explicitly provided base */ + if(Base != 2 && Base != 8 && Base != 10 && Base != 16) + { + /* Invalid base, return error code */ + return STATUS_INVALID_PARAMETER; + } + + /* Check if number starts with 0 */ + if(String[0] == L'0') + { + /* Check for prefix */ + if(Base == 16 && (String[1] == L'x' || String[1] == L'X')) + { + /* Skip hexadecimal prefix */ + String += 2; + } + else if(Base == 8 && (String[1] == L'o' || String[1] == L'O')) + { + /* Skip octal prefix */ + String += 2; + } + else if(Base == 2 && (String[1] == L'b' || String[1] == L'B')) + { + /* Skip binary prefix */ + String += 2; + } + } + } + + /* Parse string character by character */ + while(*String != L'\0') + { + /* Convert wide character to numeric digit */ + if(*String >= L'0' && *String <= L'9') + { + /* Convert decimal digit */ + Digit = *String - L'0'; + } + else if(*String >= L'A' && *String <= L'F') + { + /* Convert hexadecimal digit */ + Digit = *String - L'A' + 10; + } + else if(*String >= L'a' && *String <= L'f') + { + /* Convert hexadecimal digit */ + Digit = *String - L'a' + 10; + } + else + { + /* Invalid character for a number encountered, stop parsing */ + break; + } + + /* Check if digit is valid for the current base */ + if(Digit >= Base) + { + /* Digit out of range for this base, stop parsing */ + break; + } + + /* Check for integer overflow */ + if((Result > (MAXULONG / Base)) || ((Result * Base) > (MAXULONG - Digit))) + { + /* Integer overflow, return error code */ + return STATUS_INTEGER_OVERFLOW; + } + + /* Accumulate result */ + Result = (Result * Base) + Digit; + + /* Advance to the next character */ + String++; + } + + /* Check for negative value */ + if(NegativeValue) + { + /* Apply sign and return the result */ + *Value = (ULONG)(-(LONG)Result); + } + else + { + /* Return the result */ + *Value = Result; + } + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Writes a wide character to the destination provided by the print context. * From 96034533348a39ab26d05c102a80a457ae3bf7e5 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Sun, 17 May 2026 14:29:45 +0200 Subject: [PATCH 28/32] Implement Application Processor bootstrap function --- xtoskrnl/includes/ke/krnlinit.hh | 3 +- xtoskrnl/ke/amd64/krnlinit.cc | 173 +++++++++++++++++++------------ xtoskrnl/ke/i686/krnlinit.cc | 117 ++++++++++++++------- 3 files changed, 186 insertions(+), 107 deletions(-) diff --git a/xtoskrnl/includes/ke/krnlinit.hh b/xtoskrnl/includes/ke/krnlinit.hh index e92ccee..351890f 100644 --- a/xtoskrnl/includes/ke/krnlinit.hh +++ b/xtoskrnl/includes/ke/krnlinit.hh @@ -18,12 +18,13 @@ namespace KE class KernelInit { public: + STATIC XTAPI VOID BootstrapApplicationProcessor(IN PPROCESSOR_START_BLOCK StartBlock); STATIC XTAPI VOID InitializeMachine(VOID); STATIC XTAPI VOID SwitchBootStack(VOID); private: + STATIC XTAPI VOID BootstrapKernel(VOID); STATIC XTAPI VOID InitializeKernel(VOID); - STATIC XTAPI VOID StartKernel(VOID); }; } diff --git a/xtoskrnl/ke/amd64/krnlinit.cc b/xtoskrnl/ke/amd64/krnlinit.cc index d19742b..d56e301 100644 --- a/xtoskrnl/ke/amd64/krnlinit.cc +++ b/xtoskrnl/ke/amd64/krnlinit.cc @@ -9,6 +9,111 @@ #include +/** + * Bootstraps an Application Processor (AP) into the active kernel. This routine is executed exclusively by secondary + * processors after being awakened by the BSP. It is called directly from the startup trampoline. + * + * @param StartBlock + * Supplies a pointer to the processor start block containing initialization information provided by the kernel. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::KernelInit::BootstrapApplicationProcessor(IN PPROCESSOR_START_BLOCK StartBlock) +{ + PKPROCESSOR_BLOCK ProcessorBlock; + + /* Initialize application CPU */ + AR::ProcSup::InitializeProcessor(StartBlock->ProcessorStructures); + + /* Initialize processor */ + HL::Cpu::InitializeProcessor(); + + /* Raise to HIGH runlevel */ + KE::RunLevel::RaiseRunLevel(HIGH_LEVEL); + + /* Mark processor as started */ + StartBlock->Started = TRUE; + + /* Get current processor block */ + ProcessorBlock = KE::Processor::GetCurrentProcessorBlock(); + + /* Enter infinite loop */ + DebugPrint(L"KernelInit::BootstrapApplicationProcessor() finished for CPU #%lu. Entering infinite loop.\n", + ProcessorBlock->CpuNumber); + KE::Crash::HaltSystem(); +} + +/** + * Bootstraps the XT kernel and global subsystems. This routine is executed exclusively by the Bootstrap Processor + * and it is called immediately after switching to the kernel boot stack. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::KernelInit::BootstrapKernel(VOID) +{ + PKPROCESSOR_CONTROL_BLOCK Prcb; + ULONG_PTR PageDirectory[2]; + PKPROCESS CurrentProcess; + PKTHREAD CurrentThread; + + /* Get processor control block and current thread */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + CurrentThread = KE::Processor::GetCurrentThread(); + + /* Get current process */ + CurrentProcess = CurrentThread->ApcState.Process; + + /* Initialize CPU power state structures */ + PO::Idle::InitializeProcessorIdleState(Prcb); + + /* Save processor state */ + KE::Processor::SaveProcessorState(&Prcb->ProcessorState); + + /* Initialize spin locks */ + KE::SpinLock::InitializeAllLocks(); + KE::SpinLock::InitializeLockQueues(); + + /* Lower to APC runlevel */ + KE::RunLevel::LowerRunLevel(APC_LEVEL); + + /* Initialize XTOS kernel */ + InitializeKernel(); + + /* Initialize Idle process */ + PageDirectory[0] = 0; + PageDirectory[1] = 0; + KE::KProcess::InitializeProcess(CurrentProcess, 0, MAXULONG_PTR, PageDirectory, FALSE); + CurrentProcess->Quantum = MAXCHAR; + + /* Initialize Idle thread */ + KE::KThread::InitializeThread(CurrentProcess, CurrentThread, NULLPTR, NULLPTR, NULLPTR, + NULLPTR, NULLPTR, AR::ProcSup::GetBootStack(), TRUE); + CurrentThread->NextProcessor = Prcb->CpuNumber; + CurrentThread->Priority = THREAD_HIGH_PRIORITY; + CurrentThread->State = Running; + CurrentThread->Affinity = (ULONG_PTR)1 << Prcb->CpuNumber; + CurrentThread->WaitRunLevel = DISPATCH_LEVEL; + CurrentProcess->ActiveProcessors |= (ULONG_PTR)1 << Prcb->CpuNumber; + + /* Initialize Memory Manager */ + MM::Manager::InitializeMemoryManager(); + + /* Enable shadow buffer for framebuffer */ + HL::FrameBuffer::EnableShadowBuffer(); + + /* Enter infinite loop */ + DebugPrint(L"KernelInit::BootstrapKernel() finished. Entering infinite loop.\n"); + KE::Crash::HaltSystem(); +} + /** * This routine initializes XT kernel. * @@ -60,72 +165,6 @@ KE::KernelInit::InitializeMachine(VOID) HL::Cpu::InitializeProcessor(); } -/** - * This routine starts up the XT kernel. It is called after switching boot stack. - * - * @return This routine does not return any value. - * - * @since XT 1.0 - */ -XTAPI -VOID -KE::KernelInit::StartKernel(VOID) -{ - PKPROCESSOR_CONTROL_BLOCK Prcb; - ULONG_PTR PageDirectory[2]; - PKPROCESS CurrentProcess; - PKTHREAD CurrentThread; - - /* Get processor control block and current thread */ - Prcb = KE::Processor::GetCurrentProcessorControlBlock(); - CurrentThread = KE::Processor::GetCurrentThread(); - - /* Get current process */ - CurrentProcess = CurrentThread->ApcState.Process; - - /* Initialize CPU power state structures */ - PO::Idle::InitializeProcessorIdleState(Prcb); - - /* Save processor state */ - KE::Processor::SaveProcessorState(&Prcb->ProcessorState); - - /* Initialize spin locks */ - KE::SpinLock::InitializeAllLocks(); - KE::SpinLock::InitializeLockQueues(); - - /* Lower to APC runlevel */ - KE::RunLevel::LowerRunLevel(APC_LEVEL); - - /* Initialize XTOS kernel */ - InitializeKernel(); - - /* Initialize Idle process */ - PageDirectory[0] = 0; - PageDirectory[1] = 0; - KE::KProcess::InitializeProcess(CurrentProcess, 0, MAXULONG_PTR, PageDirectory, FALSE); - CurrentProcess->Quantum = MAXCHAR; - - /* Initialize Idle thread */ - KE::KThread::InitializeThread(CurrentProcess, CurrentThread, NULLPTR, NULLPTR, NULLPTR, - NULLPTR, NULLPTR, AR::ProcSup::GetBootStack(), TRUE); - CurrentThread->NextProcessor = Prcb->CpuNumber; - CurrentThread->Priority = THREAD_HIGH_PRIORITY; - CurrentThread->State = Running; - CurrentThread->Affinity = (ULONG_PTR)1 << Prcb->CpuNumber; - CurrentThread->WaitRunLevel = DISPATCH_LEVEL; - CurrentProcess->ActiveProcessors |= (ULONG_PTR)1 << Prcb->CpuNumber; - - /* Initialize Memory Manager */ - MM::Manager::InitializeMemoryManager(); - - /* Enable shadow buffer for framebuffer */ - HL::FrameBuffer::EnableShadowBuffer(); - - /* Enter infinite loop */ - DebugPrint(L"KernelInit::StartKernel() finished. Entering infinite loop.\n"); - KE::Crash::HaltSystem(); -} - /** * Switches execution to a new boot stack and transfers control to the KernelInit::StartKernel() routine. * @@ -144,7 +183,7 @@ KE::KernelInit::SwitchBootStack(VOID) Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() + KERNEL_STACK_SIZE) & ~(STACK_ALIGNMENT - 1); /* Get address of KernelInit::StartKernel() */ - StartKernel = (PVOID)KE::KernelInit::StartKernel; + StartKernel = (PVOID)KE::KernelInit::BootstrapKernel; /* Discard old stack frame, switch stack and jump to KernelInit::StartKernel() */ __asm__ volatile("movq %[Stack], %%rsp\n" diff --git a/xtoskrnl/ke/i686/krnlinit.cc b/xtoskrnl/ke/i686/krnlinit.cc index 955d618..78ce2b5 100644 --- a/xtoskrnl/ke/i686/krnlinit.cc +++ b/xtoskrnl/ke/i686/krnlinit.cc @@ -10,7 +10,11 @@ /** - * This routine initializes XT kernel. + * Bootstraps an Application Processor (AP) into the active kernel. This routine is executed exclusively by secondary + * processors after being awakened by the BSP. It is called directly from the startup trampoline. + * + * @param StartBlock + * Supplies a pointer to the processor start block containing initialization information provided by the kernel. * * @return This routine does not return any value. * @@ -18,50 +22,34 @@ */ XTAPI VOID -KE::KernelInit::InitializeKernel(VOID) +KE::KernelInit::BootstrapApplicationProcessor(IN PPROCESSOR_START_BLOCK StartBlock) { - XTSTATUS Status; + PKPROCESSOR_BLOCK ProcessorBlock; - /* Initialize hardware layer subsystem */ - Status = HL::Init::InitializeSystem(); - if(Status != STATUS_SUCCESS) - { - /* Hardware layer initialization failed, kernel panic */ - DebugPrint(L"Failed to initialize hardware layer subsystem!\n"); - KE::Crash::Panic(0); - } -} - -/** - * Performs architecture-specific initialization for the kernel executive. - * - * @return This routine does not return any value. - * - * @since XT 1.0 - */ -XTAPI -VOID -KE::KernelInit::InitializeMachine(VOID) -{ - /* Re-enable IDE interrupts */ - HL::IoPort::WritePort8(0x376, 0); - HL::IoPort::WritePort8(0x3F6, 0); - - /* Initialize frame buffer */ - HL::FrameBuffer::InitializeFrameBuffer(); - - /* Initialize page map support */ - MM::Paging::InitializePageMapSupport(); - - /* Initialize Kernel Shared Data (KSD) */ - KE::SharedData::InitializeKernelSharedData(); + /* Initialize application CPU */ + AR::ProcSup::InitializeProcessor(StartBlock->ProcessorStructures); /* Initialize processor */ HL::Cpu::InitializeProcessor(); + + /* Raise to HIGH runlevel */ + KE::RunLevel::RaiseRunLevel(HIGH_LEVEL); + + /* Mark processor as started */ + StartBlock->Started = TRUE; + + /* Get current processor block */ + ProcessorBlock = KE::Processor::GetCurrentProcessorBlock(); + + /* Enter infinite loop */ + DebugPrint(L"KernelInit::BootstrapApplicationProcessor() finished for CPU #%lu. Entering infinite loop.\n", + ProcessorBlock->CpuNumber); + KE::Crash::HaltSystem(); } /** - * This routine starts up the XT kernel. It is called after switching boot stack. + * Bootstraps the XT kernel and global subsystems. This routine is executed exclusively by the Bootstrap Processor + * and it is called immediately after switching to the kernel boot stack. * * @return This routine does not return any value. * @@ -69,7 +57,7 @@ KE::KernelInit::InitializeMachine(VOID) */ XTAPI VOID -KE::KernelInit::StartKernel(VOID) +KE::KernelInit::BootstrapKernel(VOID) { PKPROCESSOR_CONTROL_BLOCK Prcb; ULONG_PTR PageDirectory[2]; @@ -126,6 +114,57 @@ KE::KernelInit::StartKernel(VOID) KE::Crash::HaltSystem(); } +/** + * This routine initializes XT kernel. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::KernelInit::InitializeKernel(VOID) +{ + XTSTATUS Status; + + /* Initialize hardware layer subsystem */ + Status = HL::Init::InitializeSystem(); + if(Status != STATUS_SUCCESS) + { + /* Hardware layer initialization failed, kernel panic */ + DebugPrint(L"Failed to initialize hardware layer subsystem!\n"); + KE::Crash::Panic(0); + } +} + +/** + * Performs architecture-specific initialization for the kernel executive. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::KernelInit::InitializeMachine(VOID) +{ + /* Re-enable IDE interrupts */ + HL::IoPort::WritePort8(0x376, 0); + HL::IoPort::WritePort8(0x3F6, 0); + + /* Initialize frame buffer */ + HL::FrameBuffer::InitializeFrameBuffer(); + + /* Initialize page map support */ + MM::Paging::InitializePageMapSupport(); + + /* Initialize Kernel Shared Data (KSD) */ + KE::SharedData::InitializeKernelSharedData(); + + /* Initialize processor */ + HL::Cpu::InitializeProcessor(); +} + /** * Switches execution to a new boot stack and transfers control to the KernelInit::StartKernel() routine. * @@ -144,7 +183,7 @@ KE::KernelInit::SwitchBootStack(VOID) Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() + KERNEL_STACK_SIZE) & ~(STACK_ALIGNMENT - 1); /* Get address of KernelInit::StartKernel() */ - StartKernel = (PVOID)KE::KernelInit::StartKernel; + StartKernel = (PVOID)KE::KernelInit::BootstrapKernel; /* Discard old stack frame, switch stack, make space for NPX and jump to KernelInit::StartKernel() */ __asm__ volatile("movl %[Stack], %%esp\n" From fc0e1384c4a82c517ac00eefd443bd0a0ae1ed64 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Sun, 17 May 2026 14:33:01 +0200 Subject: [PATCH 29/32] Fix debug log message --- xtoskrnl/ke/i686/krnlinit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtoskrnl/ke/i686/krnlinit.cc b/xtoskrnl/ke/i686/krnlinit.cc index 78ce2b5..b18737b 100644 --- a/xtoskrnl/ke/i686/krnlinit.cc +++ b/xtoskrnl/ke/i686/krnlinit.cc @@ -110,7 +110,7 @@ KE::KernelInit::BootstrapKernel(VOID) HL::FrameBuffer::EnableShadowBuffer(); /* Enter infinite loop */ - DebugPrint(L"KernelInit::StartKernel() finished. Entering infinite loop.\n"); + DebugPrint(L"KernelInit::BootstrapKernel() finished. Entering infinite loop.\n"); KE::Crash::HaltSystem(); } From 8080e072811db73d5337d70bb6df8b2baff170dc Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Sun, 17 May 2026 14:38:44 +0200 Subject: [PATCH 30/32] Fix stack pointer miscalculation caused by adding KERNEL_STACK_SIZE twice --- xtoskrnl/ke/amd64/krnlinit.cc | 3 ++- xtoskrnl/ke/i686/krnlinit.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/xtoskrnl/ke/amd64/krnlinit.cc b/xtoskrnl/ke/amd64/krnlinit.cc index d56e301..bd39b4f 100644 --- a/xtoskrnl/ke/amd64/krnlinit.cc +++ b/xtoskrnl/ke/amd64/krnlinit.cc @@ -4,6 +4,7 @@ * FILE: xtoskrnl/ke/amd64/krnlinit.cc * DESCRIPTION: CPU architecture specific kernel initialization * DEVELOPERS: Rafal Kupiec + * Aiken Harris */ #include @@ -180,7 +181,7 @@ KE::KernelInit::SwitchBootStack(VOID) PVOID StartKernel; /* Calculate the stack pointer at the top of the buffer, ensuring it is properly aligned as required by the ABI */ - Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() + KERNEL_STACK_SIZE) & ~(STACK_ALIGNMENT - 1); + Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() & ~(STACK_ALIGNMENT - 1)); /* Get address of KernelInit::StartKernel() */ StartKernel = (PVOID)KE::KernelInit::BootstrapKernel; diff --git a/xtoskrnl/ke/i686/krnlinit.cc b/xtoskrnl/ke/i686/krnlinit.cc index b18737b..3bc5023 100644 --- a/xtoskrnl/ke/i686/krnlinit.cc +++ b/xtoskrnl/ke/i686/krnlinit.cc @@ -4,6 +4,7 @@ * FILE: xtoskrnl/ke/i686/krnlinit.cc * DESCRIPTION: CPU architecture specific kernel initialization * DEVELOPERS: Rafal Kupiec + * Aiken Harris */ #include @@ -180,7 +181,7 @@ KE::KernelInit::SwitchBootStack(VOID) PVOID StartKernel; /* Calculate the stack pointer at the top of the buffer, ensuring it is properly aligned as required by the ABI */ - Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() + KERNEL_STACK_SIZE) & ~(STACK_ALIGNMENT - 1); + Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() & ~(STACK_ALIGNMENT - 1)); /* Get address of KernelInit::StartKernel() */ StartKernel = (PVOID)KE::KernelInit::BootstrapKernel; From 4e7113a0793d449665ba1cedaaad86345d0a77f0 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Sun, 17 May 2026 17:33:35 +0200 Subject: [PATCH 31/32] Implement HL::Cpu::StartAllProcessors to bootstrap all application processors --- xtoskrnl/README.md | 3 + xtoskrnl/hl/x86/cpu.cc | 172 ++++++++++++++++++++++++++++++++++++ xtoskrnl/includes/hl/cpu.hh | 2 + 3 files changed, 177 insertions(+) diff --git a/xtoskrnl/README.md b/xtoskrnl/README.md index 318a28f..4e0cb31 100644 --- a/xtoskrnl/README.md +++ b/xtoskrnl/README.md @@ -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. diff --git a/xtoskrnl/hl/x86/cpu.cc b/xtoskrnl/hl/x86/cpu.cc index c65929c..b3d484e 100644 --- a/xtoskrnl/hl/x86/cpu.cc +++ b/xtoskrnl/hl/x86/cpu.cc @@ -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; +} diff --git a/xtoskrnl/includes/hl/cpu.hh b/xtoskrnl/includes/hl/cpu.hh index bb9201a..e75d011 100644 --- a/xtoskrnl/includes/hl/cpu.hh +++ b/xtoskrnl/includes/hl/cpu.hh @@ -19,8 +19,10 @@ namespace HL { private: STATIC KAFFINITY ActiveProcessors; + public: STATIC XTAPI VOID InitializeProcessor(VOID); + STATIC XTAPI XTSTATUS StartAllProcessors(VOID); }; } From a39835493afcf7c3ea703532260ad027800206e1 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Sun, 17 May 2026 20:45:01 +0200 Subject: [PATCH 32/32] Enable multi-processor support --- xtoskrnl/ke/amd64/krnlinit.cc | 4 ++++ xtoskrnl/ke/i686/krnlinit.cc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/xtoskrnl/ke/amd64/krnlinit.cc b/xtoskrnl/ke/amd64/krnlinit.cc index bd39b4f..9612a39 100644 --- a/xtoskrnl/ke/amd64/krnlinit.cc +++ b/xtoskrnl/ke/amd64/krnlinit.cc @@ -110,6 +110,10 @@ KE::KernelInit::BootstrapKernel(VOID) /* Enable shadow buffer for framebuffer */ HL::FrameBuffer::EnableShadowBuffer(); + /* Start all application processors */ + KE::Processor::InitializeProcessorBlocks(); + HL::Cpu::StartAllProcessors(); + /* Enter infinite loop */ DebugPrint(L"KernelInit::BootstrapKernel() finished. Entering infinite loop.\n"); KE::Crash::HaltSystem(); diff --git a/xtoskrnl/ke/i686/krnlinit.cc b/xtoskrnl/ke/i686/krnlinit.cc index 3bc5023..b127d71 100644 --- a/xtoskrnl/ke/i686/krnlinit.cc +++ b/xtoskrnl/ke/i686/krnlinit.cc @@ -110,6 +110,10 @@ KE::KernelInit::BootstrapKernel(VOID) /* Enable shadow buffer for framebuffer */ HL::FrameBuffer::EnableShadowBuffer(); + /* Start all application processors */ + KE::Processor::InitializeProcessorBlocks(); + HL::Cpu::StartAllProcessors(); + /* Enter infinite loop */ DebugPrint(L"KernelInit::BootstrapKernel() finished. Entering infinite loop.\n"); KE::Crash::HaltSystem();