Merge pull request 'Add support for Symmetric Multiprocessing (SMP)' (#26) from smp into master
All checks were successful
Builds / ExectOS (i686, debug) (push) Successful in 36s
Builds / ExectOS (amd64, debug) (push) Successful in 37s
Builds / ExectOS (i686, release) (push) Successful in 44s
Builds / ExectOS (amd64, release) (push) Successful in 46s

This commit was merged in pull request #26.
This commit is contained in:
2026-05-18 18:44:53 +02:00
committed by CodingWorkshop Signing Team
45 changed files with 2236 additions and 566 deletions

View File

@@ -80,4 +80,12 @@ 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, ProcessorStructures);
ADK_OFFSET(PROCESSOR_START_BLOCK, Stack);
ADK_OFFSET(PROCESSOR_START_BLOCK, Started);
}

View File

@@ -54,4 +54,12 @@ 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, ProcessorStructures);
ADK_OFFSET(PROCESSOR_START_BLOCK, Stack);
ADK_OFFSET(PROCESSOR_START_BLOCK, Started);
}

View File

@@ -430,7 +430,9 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame);
/* Processor identification information */
typedef struct _CPU_IDENTIFICATION
{
ULONGLONG ExtendedFeatureBits;
USHORT Family;
ULONGLONG FeatureBits;
USHORT Model;
USHORT Stepping;
CPU_VENDOR Vendor;

View File

@@ -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)
@@ -118,8 +174,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 +526,22 @@ 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 ProcessorStructures;
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 */
@@ -524,6 +591,8 @@ typedef struct _KPROCESSOR_BLOCK
KAFFINITY SetMember;
ULONG StallScaleFactor;
UCHAR CpuNumber;
ULONG HardwareId;
VOLATILE BOOLEAN Started;
PINTERRUPT_HANDLER InterruptDispatchTable[256];
} KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK;

View File

@@ -395,7 +395,9 @@ typedef VOID (*PINTERRUPT_HANDLER)(PKTRAP_FRAME TrapFrame);
/* Processor identification information */
typedef struct _CPU_IDENTIFICATION
{
ULONGLONG ExtendedFeatureBits;
USHORT Family;
ULONGLONG FeatureBits;
USHORT Model;
USHORT Stepping;
CPU_VENDOR Vendor;

View File

@@ -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)
@@ -137,8 +193,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 +487,17 @@ 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 ProcessorStructures;
PVOID Stack;
BOOLEAN Started;
} PROCESSOR_START_BLOCK, *PPROCESSOR_START_BLOCK;
/* Processor state frame structure definition */
typedef struct _KPROCESSOR_STATE
{
@@ -483,6 +550,8 @@ typedef struct _KPROCESSOR_BLOCK
KAFFINITY SetMember;
ULONG StallScaleFactor;
UCHAR CpuNumber;
ULONG HardwareId;
VOLATILE BOOLEAN Started;
PINTERRUPT_HANDLER InterruptDispatchTable[256];
} KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK;

View File

@@ -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)

View File

@@ -15,6 +15,12 @@ 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.
* **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.

View File

@@ -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 $KGDT_R0_CMCODE
pushl %eax
lretl
ApEnterProtectedMode:
/* Enter 32-bit protected mode */
.code32
/* Setup all data segment registers */
movw $KGDT_R0_DATA, %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 $X86_MSR_EFER, %ecx
rdmsr
orl $(X86_MSR_EFER_LME | X86_MSR_EFER_NXE), %eax
wrmsr
/* Enable Paging */
movl %cr0, %eax
orl $CR0_PG, %eax
movl %eax, %cr0
/* Far return to enter 64-bit long mode */
leal (ApEnterLongMode - ArStartApplicationProcessor)(%ebx), %eax
pushl $KGDT_R0_CODE
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:

View File

@@ -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();
@@ -122,11 +124,12 @@ AR::ProcSup::IdentifyProcessor(VOID)
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 +137,133 @@ 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;
ULONG MaxExtendedLeaf, MaxStandardLeaf;
PKPROCESSOR_CONTROL_BLOCK Prcb;
CPUID_REGISTERS CpuRegisters;
/* Check if processor structures buffer provided */
if(ProcessorStructures)
{
/* Assign CPU structures from provided buffer */
InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock,
&KernelBootStack, &KernelFaultStack, &KernelNmiStack);
/* Get current processor control block */
Prcb = KE::Processor::GetCurrentProcessorControlBlock();
/* Use global IDT */
Idt = InitialIdt;
}
else
/* 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)
{
/* 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;
/* 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;
}
/* Initialize processor block */
InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack);
/* 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);
/* Initialize GDT, IDT and TSS */
InitializeGdt(ProcessorBlock);
InitializeIdt(ProcessorBlock);
InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack);
/* 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;
}
/* 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;
/* 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);
/* Load GDT, IDT and TSS */
AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit);
AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit);
AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS);
/* Store CPU power management features in processor control block */
if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) Prcb->CpuId.FeatureBits |= KCF_ARAT;
}
/* Enter passive IRQ level */
HL::RunLevel::SetRunLevel(PASSIVE_LEVEL);
/* 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);
/* Initialize segment registers */
InitializeSegments();
/* 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;
}
/* Set GS base */
AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_GSBASE, (ULONGLONG)ProcessorBlock);
AR::CpuFunc::WriteModelSpecificRegister(X86_MSR_KERNEL_GSBASE, (ULONGLONG)ProcessorBlock);
/* 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);
/* Initialize processor registers */
InitializeProcessorRegisters();
/* Identify processor */
IdentifyProcessor();
/* Store CPU advanced power management features in processor control block */
if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) Prcb->CpuId.ExtendedFeatureBits |= KCF_INVARIANT_TSC;
}
}
/**
@@ -281,6 +346,79 @@ 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);
/* 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.
*
@@ -439,26 +577,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 */
if(KernelBootStack != NULLPTR)
{
/* Return kernel boot stack address */
*KernelBootStack = (PVOID)Address;
}
Address += KERNEL_STACK_SIZE;
/* Assign a space for kernel fault stack and advance */
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 */
if(KernelNmiStack != NULLPTR)
{
/* Return kernel NMI stack address */
*KernelNmiStack = (PVOID)Address;
}
/* Assign a space for GDT and advance */
if(Gdt != NULLPTR)
{
/* Return GDT base address */
*Gdt = (PKGDTENTRY)(PVOID)Address;
Address += sizeof(InitialGdt);
}
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 */
if(ProcessorBlock != NULLPTR)
{
/* Return processor block address */
*ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address;
Address += sizeof(InitialProcessorBlock);
/* Assign a space for TSS */
*Tss = (PKTSS)(PVOID)Address;
}
}
/**

View File

@@ -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,96 @@ _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 $KGDT_R0_CODE
pushl %eax
lretl
ApEnterProtectedMode:
/* Enter 32-bit protected mode */
.code32
/* Setup all data segment registers */
movw $KGDT_R0_DATA, %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 Paging */
movl %cr0, %eax
orl $CR0_PG, %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:

View File

@@ -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();
@@ -117,11 +117,12 @@ AR::ProcSup::IdentifyProcessor(VOID)
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 +130,133 @@ 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;
ULONG MaxExtendedLeaf, MaxStandardLeaf;
PKPROCESSOR_CONTROL_BLOCK Prcb;
CPUID_REGISTERS CpuRegisters;
/* Check if processor structures buffer provided */
if(ProcessorStructures)
{
/* Assign CPU structures from provided buffer */
InitializeProcessorStructures(ProcessorStructures, &Gdt, &Tss, &ProcessorBlock,
&KernelBootStack, &KernelFaultStack, &KernelNmiStack);
/* Get current processor control block */
Prcb = KE::Processor::GetCurrentProcessorControlBlock();
/* Use global IDT */
Idt = InitialIdt;
}
else
/* 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)
{
/* 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;
/* 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;
}
/* Initialize processor block */
InitializeProcessorBlock(ProcessorBlock, Gdt, Idt, Tss, KernelFaultStack);
/* 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);
/* Initialize GDT, IDT and TSS */
InitializeGdt(ProcessorBlock);
InitializeIdt(ProcessorBlock);
InitializeTss(ProcessorBlock, KernelBootStack, KernelFaultStack, KernelNmiStack);
/* 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;
}
/* 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;
/* 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);
/* Load GDT, IDT and TSS */
AR::CpuFunc::LoadGlobalDescriptorTable(&GdtDescriptor.Limit);
AR::CpuFunc::LoadInterruptDescriptorTable(&IdtDescriptor.Limit);
AR::CpuFunc::LoadTaskRegister((UINT)KGDT_SYS_TSS);
/* Store CPU power management features in processor control block */
if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) Prcb->CpuId.FeatureBits |= KCF_ARAT;
}
/* Enter passive IRQ level */
HL::RunLevel::SetRunLevel(PASSIVE_LEVEL);
/* 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);
/* Initialize segment registers */
InitializeSegments();
/* 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;
}
/* Initialize processor registers */
InitializeProcessorRegisters();
/* 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);
/* Identify processor */
IdentifyProcessor();
/* Store CPU advanced power management features in processor control block */
if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) Prcb->CpuId.ExtendedFeatureBits |= KCF_INVARIANT_TSC;
}
}
/**
@@ -274,6 +341,76 @@ 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);
/* Initialize segment registers */
InitializeSegments();
/* Initialize processor registers */
InitializeProcessorRegisters();
/* Identify processor */
IdentifyProcessor();
}
/**
* Initializes processor block.
*
@@ -391,26 +528,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 */
if(KernelBootStack != NULLPTR)
{
/* Return kernel boot stack address */
*KernelBootStack = (PVOID)Address;
}
Address += KERNEL_STACK_SIZE;
/* Assign a space for kernel fault stack and advance */
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 */
if(KernelNmiStack != NULLPTR)
{
/* Return kernel NMI stack address */
*KernelNmiStack = (PVOID)Address;
}
/* Assign a space for GDT and advance */
if(Gdt != NULLPTR)
{
/* Return GDT base address */
*Gdt = (PKGDTENTRY)(PVOID)Address;
Address += sizeof(InitialGdt);
}
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 */
if(ProcessorBlock != NULLPTR)
{
/* Return processor block address */
*ProcessorBlock = (PKPROCESSOR_BLOCK)(PVOID)Address;
Address += sizeof(InitialProcessorBlock);
/* Assign a space for TSS */
*Tss = (PKTSS)(PVOID)Address;
}
}
/**

View File

@@ -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.
*
@@ -478,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 */

View File

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

View File

@@ -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,21 @@ XTAPI
BOOLEAN
HL::Pic::CheckX2ApicSupport(VOID)
{
CPUID_REGISTERS CpuRegisters;
PKPROCESSOR_CONTROL_BLOCK Prcb;
PCWSTR KernelParameter;
/* 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 CPUID */
AR::CpuFunc::CpuId(&CpuRegisters);
/* Check x2APIC status from the CPUID results */
if(!(CpuRegisters.Ecx & CPUID_FEATURES_ECX_X2APIC))
/* Check if the user forced xAPIC via boot parameters */
if(KE::BootInformation::GetKernelParameter(L"NOX2APIC", &KernelParameter) == STATUS_SUCCESS)
{
/* x2APIC is not supported */
/* The NOX2APIC flag is present, explicitly disable x2APIC support */
return FALSE;
}
/* x2APIC is supported */
return TRUE;
/* Get current processor control block */
Prcb = KE::Processor::GetCurrentProcessorControlBlock();
/* Return x2APIC status */
return (Prcb->CpuId.FeatureBits & KCF_X2APIC) ? TRUE : FALSE;
}
/**
@@ -402,6 +380,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,25 +762,21 @@ 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);
/* Processor block not available, return */
return;
}
else
{
/* Nothing to do */
return;
}
}
/* Get the ACPI system information */
HL::Acpi::GetSystemInformation(&SysInfo);
/* Check whether interrupts are enabled */
Interrupts = AR::CpuFunc::InterruptsEnabled();
@@ -807,32 +784,31 @@ HL::Pic::SendBroadcastIpi(IN ULONG Vector,
/* 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;
/* Check current APIC mode */
if(ApicMode == APIC_MODE_X2APIC)
/* Iterate over all logical CPUs */
for(Index = 0; Index < SysInfo->CpuCount; Index++)
{
/* In x2APIC mode, writing the full 64-bit value to the ICR MSR is sufficient */
WriteApicRegister(APIC_ICR0, Register.LongLong);
/* Retrieve the target processor block by its Logical CPU Number */
TargetProcessorBlock = KE::Processor::GetProcessorBlock(Index);
/* Only send to processors that exist and have successfully started */
if(TargetProcessorBlock != NULLPTR && TargetProcessorBlock->Started)
{
/* 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
{
/* Wait for the APIC to clear the delivery status */
while((ReadApicRegister(APIC_ICR0) & 0x1000) != 0)
{
/* Yield the processor */
AR::CpuFunc::YieldProcessor();
/* Dispatch the IPI to the target processor */
SendIpi(TargetProcessorBlock->HardwareId, Vector, APIC_DM_FIXED, APIC_DSH_Destination, APIC_TGM_EDGE);
}
}
/* 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 */
@@ -867,6 +843,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
@@ -874,19 +859,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();
}
}
@@ -904,55 +955,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);
}
/**

View File

@@ -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;
}
}
}
/**

View File

@@ -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,
@@ -42,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,
@@ -50,13 +58,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,

View File

@@ -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,
@@ -45,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,
@@ -53,13 +61,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,
@@ -79,7 +80,6 @@ namespace AR
IN ULONG_PTR Base);
STATIC XTAPI VOID SetNonMaskableInterruptTssEntry(IN PKPROCESSOR_BLOCK ProcessorBlock,
IN PVOID KernelNmiStack);
};
}

View File

@@ -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);

View File

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

View File

@@ -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);

View File

@@ -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,7 +31,7 @@ namespace KD
public:
STATIC XTCDECL VOID DbgPrint(PCWSTR Format,
...);
STATIC XTCDECL VOID DbgPrintEx(PCWSTR Format,
STATIC XTCDECL VOID DbgPrint(PCWSTR Format,
VA_LIST Arguments);
STATIC XTAPI XTSTATUS InitializeDebugIoProviders(VOID);
STATIC XTAPI VOID SetPrintRoutine(PKD_PRINT_ROUTINE DebugPrintRoutine);

View File

@@ -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);
};
}

View File

@@ -17,11 +17,20 @@ 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 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);
};
}

View File

@@ -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);

View File

@@ -25,7 +25,10 @@ namespace MM
public:
STATIC XTAPI XTSTATUS AllocateHardwareMemory(IN PFN_NUMBER PageCount,
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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -9,6 +9,9 @@
#include <xtos.hh>
/* Debug I/O spinlock */
KSPIN_LOCK KD::DebugIo::DebugIoLock;
/* Kernel Debugger mode */
KD_DEBUG_MODE KD::DebugIo::DebugMode;

View File

@@ -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,
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);

View File

@@ -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);

View File

@@ -4,11 +4,121 @@
* FILE: xtoskrnl/ke/amd64/krnlinit.cc
* DESCRIPTION: CPU architecture specific kernel initialization
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* 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();
/* 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();
}
/**
* This routine initializes XT kernel.
*
@@ -60,72 +170,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.
*
@@ -141,10 +185,10 @@ 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::StartKernel;
StartKernel = (PVOID)KE::KernelInit::BootstrapKernel;
/* Discard old stack frame, switch stack and jump to KernelInit::StartKernel() */
__asm__ volatile("movq %[Stack], %%rsp\n"

View File

@@ -66,6 +66,117 @@ 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(CpuNumber >= InstalledCpus || ProcessorBlocks == NULLPTR || ProcessorBlocks[CpuNumber] == NULLPTR)
{
/* 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.
*
* @return This routine returns a status code indicating the success or failure of the allocation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
KE::Processor::InitializeProcessorBlocks()
{
PACPI_SYSTEM_INFO SystemInfo;
XTSTATUS Status;
/* Save number of CPUs installed */
HL::Acpi::GetSystemInformation(&SystemInfo);
InstalledCpus = SystemInfo->CpuCount;
/* Allocate an array of pointers */
Status = MM::Allocator::AllocatePool(NonPagedPool,
InstalledCpus * 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, InstalledCpus * 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;
}
}
/**
* 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.
*

View File

@@ -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;

View File

@@ -4,11 +4,121 @@
* FILE: xtoskrnl/ke/i686/krnlinit.cc
* DESCRIPTION: CPU architecture specific kernel initialization
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* 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();
/* 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();
}
/**
* This routine initializes XT kernel.
*
@@ -60,72 +170,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.
*
@@ -141,10 +185,10 @@ 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::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"

View File

@@ -66,6 +66,117 @@ 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(CpuNumber >= InstalledCpus || ProcessorBlocks == NULLPTR || ProcessorBlocks[CpuNumber] == NULLPTR)
{
/* 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.
*
* @return This routine returns a status code indicating the success or failure of the allocation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
KE::Processor::InitializeProcessorBlocks()
{
PACPI_SYSTEM_INFO SystemInfo;
XTSTATUS Status;
/* Save number of CPUs installed */
HL::Acpi::GetSystemInformation(&SystemInfo);
InstalledCpus = SystemInfo->CpuCount;
/* Allocate an array of pointers */
Status = MM::Allocator::AllocatePool(NonPagedPool,
InstalledCpus * 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, InstalledCpus * 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;
}
}
/**
* 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.
*

View File

@@ -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.
*

View File

@@ -4,6 +4,7 @@
* FILE: xtoskrnl/mm/hlpool.cc
* DESCRIPTION: Hardware layer pool memory management
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
@@ -18,6 +19,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 +33,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 +58,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 +80,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 */
@@ -142,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 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.
*
* @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.
*

View File

@@ -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.
*

View File

@@ -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.
*
@@ -95,9 +92,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 +101,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 +117,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;
@@ -148,8 +130,8 @@ MM::KernelPool::AllocateProcessorStructures(IN ULONG CpuNumber,
* @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.
*
@@ -212,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);
}
}

View File

@@ -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 */

View File

@@ -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.
*

View File

@@ -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.
*