Files
exectos/xtoskrnl/ke/amd64/proc.cc
Aiken Harris c8cd198c4e
All checks were successful
Builds / ExectOS (amd64, release) (push) Successful in 34s
Builds / ExectOS (i686, release) (push) Successful in 32s
Builds / ExectOS (amd64, debug) (push) Successful in 47s
Builds / ExectOS (i686, debug) (push) Successful in 45s
Fix Task Register restoration by clearing busy bit in TSS descriptor
2026-06-08 13:40:20 +02:00

503 lines
18 KiB
C++

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/ke/amd64/proc.cc
* DESCRIPTION: AMD64 processor-related functionality for the kernel
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include <xtos.hh>
/**
* Gets the processor block for the currently executing processor.
*
* @return This routine returns the current processor block read from the GS register.
*
* @since XT 1.0
*/
XTAPI
PKPROCESSOR_BLOCK
KE::Processor::GetCurrentProcessorBlock(VOID)
{
/* Get processor block from GS register */
return (PKPROCESSOR_BLOCK)AR::CpuFunctions::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Self));
}
/**
* Gets the processor control block for the currently executing processor.
*
* @return This routine returns the current processor control block read from the GS register.
*
* @since XT 1.0
*/
XTAPI
PKPROCESSOR_CONTROL_BLOCK
KE::Processor::GetCurrentProcessorControlBlock(VOID)
{
return (PKPROCESSOR_CONTROL_BLOCK)AR::CpuFunctions::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, CurrentPrcb));
}
/**
* Gets the number of the currently executing processor.
*
* @return This routine returns the zero-indexed processor number.
*
* @since XT 1.0
*/
XTAPI
ULONG
KE::Processor::GetCurrentProcessorNumber(VOID)
{
return (ULONG)AR::CpuFunctions::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, CpuNumber));
}
/**
* Gets the current thread running on the currently executing processor.
*
* @return This routine returns the address of the current thread object.
*
* @since NT 3.5
*/
XTAPI
PKTHREAD
KE::Processor::GetCurrentThread(VOID)
{
return (PKTHREAD)AR::CpuFunctions::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Prcb.CurrentThread));
}
/**
* Retrieves the number of installed and enabled CPUs in the system.
*
* @return This routine returns the number of installed CPUs in the system.
*
* @since XT 1.0
*/
XTAPI
ULONG
KE::Processor::GetInstalledCpus(VOID)
{
/* Return number of installed CPUs */
return InstalledCpus;
}
/**
* 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));
/* Register the processor block for the BSP processor */
KE::Processor::RegisterProcessorBlock(0, KE::Processor::GetCurrentProcessorBlock());
/* 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;
}
}
/**
* Restores the processor's architectural state from the provided context frame back into the hardware trap
* and exception frames.
*
* @param TrapFrame
* Supplies a pointer to the trap frame that will receive the volatile processor state and control registers.
*
* @param ExceptionFrame
* Supplies a pointer to the exception frame that will receive the non-volatile integer and FP registers.
*
* @param ContextFrame
* Supplies a pointer to the context frame containing the state to be restored.
*
* @param ContextFlags
* Supplies a bitmask indicating which specific register groups should be restored from the ContextFrame.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::RestoreProcessorContext(IN OUT PKTRAP_FRAME TrapFrame,
IN OUT PKEXCEPTION_FRAME ExceptionFrame,
IN PCONTEXT ContextFrame,
IN ULONG ContextFlags)
{
UNIMPLEMENTED;
}
/**
* Restores the processor's low-level hardware control state.
*
* @param CpuState
* Supplies a pointer to the processor state block containing the previously saved control data.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::RestoreProcessorControlState(IN PKPROCESSOR_STATE CpuState)
{
/* Restore the CR registers */
AR::CpuFunctions::WriteControlRegister(0, CpuState->SpecialRegisters.Cr0);
AR::CpuFunctions::WriteControlRegister(2, CpuState->SpecialRegisters.Cr2);
AR::CpuFunctions::WriteControlRegister(3, CpuState->SpecialRegisters.Cr3);
AR::CpuFunctions::WriteControlRegister(4, CpuState->SpecialRegisters.Cr4);
AR::CpuFunctions::WriteControlRegister(8, CpuState->SpecialRegisters.Cr8);
/* Restore the DR registers */
AR::CpuFunctions::WriteDebugRegister(0, CpuState->SpecialRegisters.KernelDr0);
AR::CpuFunctions::WriteDebugRegister(1, CpuState->SpecialRegisters.KernelDr1);
AR::CpuFunctions::WriteDebugRegister(2, CpuState->SpecialRegisters.KernelDr2);
AR::CpuFunctions::WriteDebugRegister(3, CpuState->SpecialRegisters.KernelDr3);
AR::CpuFunctions::WriteDebugRegister(6, CpuState->SpecialRegisters.KernelDr6);
AR::CpuFunctions::WriteDebugRegister(7, CpuState->SpecialRegisters.KernelDr7);
/* Restore MSR registers */
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_GSBASE, CpuState->SpecialRegisters.MsrGsBase);
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_KERNEL_GSBASE, CpuState->SpecialRegisters.MsrGsSwap);
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_CSTAR, CpuState->SpecialRegisters.MsrCStar);
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_LSTAR, CpuState->SpecialRegisters.MsrLStar);
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_STAR, CpuState->SpecialRegisters.MsrStar);
AR::CpuFunctions::WriteModelSpecificRegister(X86_MSR_FMASK, CpuState->SpecialRegisters.MsrSyscallMask);
/* Restore XMM control/status register */
AR::CpuFunctions::LoadMxcsrRegister(CpuState->SpecialRegisters.MxCsr);
/* Restore GDT, IDT and LDT */
AR::CpuFunctions::LoadGlobalDescriptorTable(&CpuState->SpecialRegisters.Gdtr.Limit);
AR::CpuFunctions::LoadInterruptDescriptorTable(&CpuState->SpecialRegisters.Idtr.Limit);
AR::CpuFunctions::LoadLocalDescriptorTable(CpuState->SpecialRegisters.Ldtr);
/* Force the TSS descriptor into a non-busy state and restore TaskRegister */
*(VOLATILE PUCHAR)((ULONG_PTR)CpuState->SpecialRegisters.Gdtr.Base + CpuState->SpecialRegisters.Tr + 5) &= ~0x02;
AR::CpuFunctions::LoadTaskRegister(CpuState->SpecialRegisters.Tr);
}
/**
* Restores the executing processor's state.
*
* @param TrapFrame
* Supplies a pointer to the hardware trap frame that will be populated with the restored volatile state.
*
* @param ExceptionFrame
* Supplies a pointer to the exception frame that will be populated with the restored non-volatile state.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::RestoreProcessorState(OUT PKTRAP_FRAME TrapFrame,
OUT PKEXCEPTION_FRAME ExceptionFrame)
{
PKPROCESSOR_CONTROL_BLOCK Prcb;
/* Retrieve current processor control block */
Prcb = GetCurrentProcessorControlBlock();
/* Restore processor context */
RestoreProcessorContext(TrapFrame, ExceptionFrame, &Prcb->ProcessorState.ContextFrame, CONTEXT_ALL);
/* Restore processor control registers */
RestoreProcessorControlState(&Prcb->ProcessorState);
}
/**
* Constructs a standardized processor context from the hardware trap and exception frames.
*
* @param TrapFrame
* Supplies a pointer to the trap frame containing volatile processor state, control registers and debug
* registers saved at the time of the interrupt or exception.
*
* @param ExceptionFrame
* Supplies a pointer to the exception frame containing non-volatile integer and floating-point registers.
*
* @param ContextFrame
* Supplies a pointer to the context frame that will receive the processor state.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::SaveProcessorContext(IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN OUT PCONTEXT ContextFrame)
{
/* Elevate RunLevel to APC_LEVEL to prevent thread preemption */
KE::RaiseRunLevel RunLevel(APC_LEVEL);
/* Check if control registers are requested */
if(ContextFrame->ContextFlags & CONTEXT_CONTROL)
{
/* Copy instruction pointer, stack pointer, and CPU status flags */
ContextFrame->Flags = TrapFrame->Flags;
ContextFrame->Rbp = TrapFrame->Rbp;
ContextFrame->Rip = TrapFrame->Rip;
ContextFrame->Rsp = TrapFrame->Rsp;
ContextFrame->SegCs = TrapFrame->SegCs;
ContextFrame->SegSs = TrapFrame->SegSs;
}
/* Check if general-purpose integer registers are requested */
if(ContextFrame->ContextFlags & CONTEXT_INTEGER)
{
/* Copy volatile integer registers directly from the trap frame */
ContextFrame->Rax = TrapFrame->Rax;
ContextFrame->Rcx = TrapFrame->Rcx;
ContextFrame->Rdx = TrapFrame->Rdx;
ContextFrame->R8 = TrapFrame->R8;
ContextFrame->R9 = TrapFrame->R9;
ContextFrame->R10 = TrapFrame->R10;
ContextFrame->R11 = TrapFrame->R11;
/* Check if a valid exception frame was provided */
if(ExceptionFrame)
{
/* Copy non-volatile integer registers from the exception frame */
ContextFrame->Rbx = ExceptionFrame->Rbx;
ContextFrame->R12 = ExceptionFrame->R12;
ContextFrame->R13 = ExceptionFrame->R13;
ContextFrame->R14 = ExceptionFrame->R14;
ContextFrame->R15 = ExceptionFrame->R15;
ContextFrame->Rdi = ExceptionFrame->Rdi;
ContextFrame->Rsi = ExceptionFrame->Rsi;
}
}
/* Check if segment registers are requested */
if(ContextFrame->ContextFlags & CONTEXT_SEGMENTS)
{
/* Populate segment selectors with standard Ring 3 values */
ContextFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
ContextFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
ContextFrame->SegFs = KGDT_R3_CMTEB | RPL_MASK;
ContextFrame->SegGs = KGDT_R3_DATA | RPL_MASK;
}
/* Check if floating-point registers are requested */
if(ContextFrame->ContextFlags & CONTEXT_FLOATING_POINT)
{
/* Copy the SSE control/status register and volatile XMM registers */
ContextFrame->MxCsr = TrapFrame->MxCsr;
ContextFrame->Xmm0 = TrapFrame->Xmm0;
ContextFrame->Xmm1 = TrapFrame->Xmm1;
ContextFrame->Xmm2 = TrapFrame->Xmm2;
ContextFrame->Xmm3 = TrapFrame->Xmm3;
ContextFrame->Xmm4 = TrapFrame->Xmm4;
ContextFrame->Xmm5 = TrapFrame->Xmm5;
/* Check if a valid exception frame was provided */
if(ExceptionFrame)
{
/* Copy non-volatile XMM registers from the exception frame */
ContextFrame->Xmm6 = ExceptionFrame->Xmm6;
ContextFrame->Xmm7 = ExceptionFrame->Xmm7;
ContextFrame->Xmm8 = ExceptionFrame->Xmm8;
ContextFrame->Xmm9 = ExceptionFrame->Xmm9;
ContextFrame->Xmm10 = ExceptionFrame->Xmm10;
ContextFrame->Xmm11 = ExceptionFrame->Xmm11;
ContextFrame->Xmm12 = ExceptionFrame->Xmm12;
ContextFrame->Xmm13 = ExceptionFrame->Xmm13;
ContextFrame->Xmm14 = ExceptionFrame->Xmm14;
ContextFrame->Xmm15 = ExceptionFrame->Xmm15;
}
}
/* Check if debug registers are requested */
if(ContextFrame->ContextFlags & CONTEXT_DEBUG_REGISTERS)
{
/* Copy debug registers */
ContextFrame->Dr0 = TrapFrame->Dr0;
ContextFrame->Dr1 = TrapFrame->Dr1;
ContextFrame->Dr2 = TrapFrame->Dr2;
ContextFrame->Dr3 = TrapFrame->Dr3;
ContextFrame->Dr6 = TrapFrame->Dr6;
ContextFrame->Dr7 = TrapFrame->Dr7;
}
}
/**
* Saves the current processor's low-level hardware control state.
*
* @param State
* Supplies a pointer to the processor state block that will receive processor's control data.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::SaveProcessorControlState(OUT PKPROCESSOR_STATE CpuState)
{
/* Save CR registers */
CpuState->SpecialRegisters.Cr0 = AR::CpuFunctions::ReadControlRegister(0);
CpuState->SpecialRegisters.Cr2 = AR::CpuFunctions::ReadControlRegister(2);
CpuState->SpecialRegisters.Cr3 = AR::CpuFunctions::ReadControlRegister(3);
CpuState->SpecialRegisters.Cr4 = AR::CpuFunctions::ReadControlRegister(4);
CpuState->SpecialRegisters.Cr8 = AR::CpuFunctions::ReadControlRegister(8);
/* Save DR registers */
CpuState->SpecialRegisters.KernelDr0 = AR::CpuFunctions::ReadDebugRegister(0);
CpuState->SpecialRegisters.KernelDr1 = AR::CpuFunctions::ReadDebugRegister(1);
CpuState->SpecialRegisters.KernelDr2 = AR::CpuFunctions::ReadDebugRegister(2);
CpuState->SpecialRegisters.KernelDr3 = AR::CpuFunctions::ReadDebugRegister(3);
CpuState->SpecialRegisters.KernelDr6 = AR::CpuFunctions::ReadDebugRegister(6);
CpuState->SpecialRegisters.KernelDr7 = AR::CpuFunctions::ReadDebugRegister(7);
/* Save MSR registers */
CpuState->SpecialRegisters.MsrGsBase = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_GSBASE);
CpuState->SpecialRegisters.MsrGsSwap = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_KERNEL_GSBASE);
CpuState->SpecialRegisters.MsrCStar = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_CSTAR);
CpuState->SpecialRegisters.MsrLStar = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_LSTAR);
CpuState->SpecialRegisters.MsrStar = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_STAR);
CpuState->SpecialRegisters.MsrSyscallMask = AR::CpuFunctions::ReadModelSpecificRegister(X86_MSR_FMASK);
/* Save XMM control/status register */
CpuState->SpecialRegisters.MxCsr = AR::CpuFunctions::ReadMxCsrRegister();
/* Save GDT, IDT, LDT and TaskRegister */
AR::CpuFunctions::StoreGlobalDescriptorTable(&CpuState->SpecialRegisters.Gdtr.Limit);
AR::CpuFunctions::StoreInterruptDescriptorTable(&CpuState->SpecialRegisters.Idtr.Limit);
AR::CpuFunctions::StoreLocalDescriptorTable(&CpuState->SpecialRegisters.Ldtr);
AR::CpuFunctions::StoreTaskRegister(&CpuState->SpecialRegisters.Tr);
}
/**
* Captures the executing processor's state.
*
* @param TrapFrame
* Supplies a pointer to the hardware trap frame containing the processor's volatile state.
*
* @param ExceptionFrame
* Supplies a pointer to the exception frame containing the processor's non-volatile state.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
KE::Processor::SaveProcessorState(IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame)
{
PKPROCESSOR_CONTROL_BLOCK Prcb;
/* Retrieve current processor control block */
Prcb = GetCurrentProcessorControlBlock();
/* Set context flags to save whole all context */
Prcb->ProcessorState.ContextFrame.ContextFlags = CONTEXT_ALL;
/* Save processor context */
SaveProcessorContext(TrapFrame, ExceptionFrame, &Prcb->ProcessorState.ContextFrame);
/* Save processor control registers */
SaveProcessorControlState(&Prcb->ProcessorState);
}