diff --git a/xtoskrnl/includes/ke/crash.hh b/xtoskrnl/includes/ke/crash.hh index b37ba2ca2..1cd288bda 100644 --- a/xtoskrnl/includes/ke/crash.hh +++ b/xtoskrnl/includes/ke/crash.hh @@ -18,9 +18,15 @@ namespace KE class Crash { private: + STATIC PKPROCESSOR_CONTROL_BLOCK FreezeOwner; STATIC BOOLEAN KernelPanic; + STATIC KRUNLEVEL RunLevel; public: + STATIC XTAPI VOID FreezeCurrentExecution(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame); + STATIC XTAPI BOOLEAN FreezeExecution(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame); STATIC XTAPI VOID HaltSystem(VOID); STATIC XTAPI VOID Panic(IN ULONG Code); STATIC XTAPI VOID Panic(IN ULONG Code, @@ -29,6 +35,7 @@ namespace KE IN ULONG_PTR Parameter3, IN ULONG_PTR Parameter4); STATIC XTAPI BOOLEAN SystemCrashed(VOID); + STATIC XTAPI VOID ThawExecution(IN BOOLEAN Interrupts); }; } diff --git a/xtoskrnl/ke/crash.cc b/xtoskrnl/ke/crash.cc index dbb56d753..170e6fd08 100644 --- a/xtoskrnl/ke/crash.cc +++ b/xtoskrnl/ke/crash.cc @@ -10,6 +10,228 @@ #include +/** + * Freezes the execution of the current processor during a fatal system crash or an interactive debugging session. + * + * @param TrapFrame + * Supplies an optional pointer to the trap frame captured at the moment the IPI_FREEZE was delivered. + * + * @param ExceptionFrame + * Supplies an optional pointer to the exception frame containing the non-volatile processor state. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Crash::FreezeCurrentExecution(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame) +{ + KCONTINUE_STATUS ContinueStatus; + PKPROCESSOR_CONTROL_BLOCK Prcb; + BOOLEAN Interrupts; + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunctions::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunctions::ClearInterruptFlag(); + + /* Start a guarded code block */ + { + /* Raise runlevel to HIGH level */ + KE::RaiseRunLevel RunLevel(HIGH_LEVEL); + + /* Get the processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Verify that this core was genuinely requested to freeze */ + if(Prcb->IpiFrozen != IPI_FROZEN_STATE_FREEZE) + { + /* Spurious call or already thawed, return */ + return; + } + + /* Acknowledge the freeze request to the initiator */ + Prcb->IpiFrozen = IPI_FROZEN_STATE_FROZEN; + + /* Check if there is a valid trap frame to save the context */ + if(TrapFrame != NULLPTR) + { + /* Capture the processor state */ + KE::Processor::SaveProcessorState(TrapFrame, ExceptionFrame); + } + + /* Enter the stateless polling loop */ + while(Prcb->IpiFrozen != IPI_FROZEN_STATE_THAW) + { + /* Check for debugger processor switch */ + if(Prcb->IpiFrozen & IPI_FROZEN_STATE_ACTIVE) + { + /* Let the CPU enter the debugger as the active processor */ + ContinueStatus = KD::Debugger::SwitchProcessor(); + + /* Set the state back to passively frozen */ + Prcb->IpiFrozen = IPI_FROZEN_STATE_FROZEN; + + /* Check if the status is ContinueSuccess */ + if(ContinueStatus == ContinueSuccess) + { + /* Release the freeze owner to globally resume system execution */ + FreezeOwner->IpiFrozen = IPI_FROZEN_STATE_THAW; + } + } + + /* Mitigate spin-lock starvation and enforce memory coherence */ + AR::CpuFunctions::YieldProcessor(); + AR::CpuFunctions::MemoryBarrier(); + } + + /* Thaw signal received, check if there is a need to restore context */ + if(TrapFrame != NULLPTR) + { + /* Restore the processor state */ + KE::Processor::RestoreProcessorState(TrapFrame, ExceptionFrame); + } + + /* Acknowledge the thaw and mark the processor as fully operational */ + Prcb->IpiFrozen = IPI_FROZEN_STATE_RUNNING; + + /* Flush the Translation Lookaside Buffer (TLB) */ + AR::CpuFunctions::FlushTlb(); + } + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunctions::SetInterruptFlag(); + } +} + +/** + * Asserts control over the entire system by freezing all other executing processors. + * + * @param TrapFrame + * Supplies a pointer to the trap frame of the initiating processor. + * + * @param ExceptionFrame + * Supplies a pointer to the exception frame of the initiating processor. + * + * @return This routine returns the interrupt state prior to the freeze execution. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +KE::Crash::FreezeExecution(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame) +{ + PKPROCESSOR_BLOCK ProcessorBlock; + PKPROCESSOR_CONTROL_BLOCK Prcb; + KRUNLEVEL OldRunLevel; + ULONG CpuCount, Index; + BOOLEAN Interrupts; + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunctions::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunctions::ClearInterruptFlag(); + + /* Raise runlevel to HIGH level */ + OldRunLevel = KE::RunLevel::RaiseRunLevel(HIGH_LEVEL); + + /* Get processor control block for the executing core */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Check if CPU already owns the freeze */ + if(FreezeOwner == Prcb && Prcb->IpiFrozen == IPI_FROZEN_STATE_OWNER) + { + /* Freeze already owned, return interrupt state */ + return Interrupts; + } + + /* Attempt to establish ownership of the freeze */ + while(RTL::Atomic::CompareExchangePointer((PVOID*)&FreezeOwner, NULLPTR, Prcb)) + { + /* Freeze owned by another processor, spin until the lock is released */ + while(FreezeOwner != NULLPTR) + { + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunctions::SetInterruptFlag(); + } + + /* Process pending IPIs */ + KE::Ipi::HandleIpiService(TrapFrame, ExceptionFrame); + + /* Disable interrupts */ + AR::CpuFunctions::ClearInterruptFlag(); + } + } + + /* Re-fetch the processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Set the freeze state */ + Prcb->IpiFrozen = IPI_FROZEN_STATE_OWNER | IPI_FROZEN_STATE_ACTIVE; + + /* Get the number of installed CPUs */ + CpuCount = KE::Processor::GetInstalledCpus(); + + /* Iterate over all installed CPUs */ + for(Index = 0; Index < CpuCount; Index++) + { + /* Get the processor block for next CPU */ + ProcessorBlock = KE::Processor::GetProcessorBlock(Index); + + /* Check if the processor block exists and the processor is running */ + if(ProcessorBlock != NULLPTR && ProcessorBlock->Started) + { + /* Do not freeze ourselves */ + if(ProcessorBlock->Prcb.CpuNumber != Prcb->CpuNumber) + { + /* Request the target processor to freeze execution */ + ProcessorBlock->Prcb.IpiFrozen = IPI_FROZEN_STATE_FREEZE; + } + } + } + + /* Broadcast the freeze request to all other processors */ + KE::Ipi::SendBroadcastIpi(IPI_FREEZE, FALSE); + + /* Iterate over all installed CPUs to verify their frozen state */ + for(Index = 0; Index < CpuCount; Index++) + { + /* Get the processor block for next CPU */ + ProcessorBlock = KE::Processor::GetProcessorBlock(Index); + + /* Check if the processor block exists and the processor is running */ + if(ProcessorBlock != NULLPTR && ProcessorBlock->Started) + { + /* Do not wait for ourselves */ + if(ProcessorBlock->Prcb.CpuNumber != Prcb->CpuNumber) + { + /* Wait for the target processor to acknowledge the freeze */ + while(ProcessorBlock->Prcb.IpiFrozen != IPI_FROZEN_STATE_FROZEN) + { + /* Mitigate spin-lock starvation */ + AR::CpuFunctions::YieldProcessor(); + AR::CpuFunctions::MemoryBarrier(); + } + } + } + } + + /* Save original runlevel and return the original interrupt state */ + RunLevel = OldRunLevel; + return Interrupts; +} + /** * Halts the system. * @@ -103,3 +325,87 @@ KE::Crash::SystemCrashed(VOID) /* Return kernel panic state */ return KernelPanic; } + +/** + * Resumes execution of the entire system following a system freeze or an interactive debugging session. + * + * @param Interrupts + * Supplies the original interrupt state captured prior to the freeze. + * This determines if local interrupts should be re-enabled. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Crash::ThawExecution(IN BOOLEAN Interrupts) +{ + PKPROCESSOR_BLOCK ProcessorBlock; + PKPROCESSOR_CONTROL_BLOCK Prcb; + ULONG CpuCount, Index; + + /* Get the processor control block and available CPU count */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + CpuCount = KE::Processor::GetInstalledCpus(); + + /* Mark the freeze owner processor as running */ + Prcb->IpiFrozen = IPI_FROZEN_STATE_RUNNING; + + /* Iterate over all CPUs to signal them to thaw */ + for(Index = 0; Index < CpuCount; Index++) + { + /* Get the processor block for the next CPU */ + ProcessorBlock = KE::Processor::GetProcessorBlock(Index); + + /* Check if the processor block exists and the processor is running */ + if(ProcessorBlock != NULLPTR && ProcessorBlock->Started) + { + /* Do not thaw ourselves */ + if(ProcessorBlock->Prcb.CpuNumber != Prcb->CpuNumber) + { + /* Request the target processor to thaw execution */ + ProcessorBlock->Prcb.IpiFrozen = IPI_FROZEN_STATE_THAW; + } + } + } + + /* Iterate over all installed CPUs to verify they have resumed */ + for(Index = 0; Index < CpuCount; Index++) + { + /* Get the processor block for the next CPU */ + ProcessorBlock = KE::Processor::GetProcessorBlock(Index); + + /* Check if the processor block exists and the processor is running */ + if(ProcessorBlock != NULLPTR && ProcessorBlock->Started) + { + /* Do not wait for ourselves */ + if(ProcessorBlock->Prcb.CpuNumber != Prcb->CpuNumber) + { + /* Wait for the target processor to acknowledge it is running */ + while(ProcessorBlock->Prcb.IpiFrozen != IPI_FROZEN_STATE_RUNNING) + { + /* Mitigate spin-lock starvation */ + AR::CpuFunctions::YieldProcessor(); + AR::CpuFunctions::MemoryBarrier(); + } + } + } + } + + /* Release the global freeze owner lock */ + RTL::Atomic::ExchangePointer((PVOID *)&FreezeOwner, NULLPTR); + + /* Flush the Translation Lookaside Buffer (TLB) */ + AR::CpuFunctions::FlushTlb(); + + /* Restore the original runlevel from before the freeze */ + KE::RunLevel::LowerRunLevel(RunLevel); + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunctions::SetInterruptFlag(); + } +} diff --git a/xtoskrnl/ke/data.cc b/xtoskrnl/ke/data.cc index a451c808f..a23920478 100644 --- a/xtoskrnl/ke/data.cc +++ b/xtoskrnl/ke/data.cc @@ -12,9 +12,15 @@ /* Kernel initialization block passed by boot loader */ PKERNEL_INITIALIZATION_BLOCK KE::BootInformation::InitializationBlock = {}; +/* Processor control block belonging to the freeze owner */ +PKPROCESSOR_CONTROL_BLOCK KE::Crash::FreezeOwner; + /* Kernel panic state */ BOOLEAN KE::Crash::KernelPanic; +/* System runlevel before execution freeze */ +KRUNLEVEL KE::Crash::RunLevel; + /* Kernel initial process */ EPROCESS KE::KProcess::InitialProcess;