forked from xt-sys/exectos
Implement multi-processor freeze and thaw execution for SMP crash state synchronization
This commit is contained in:
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,228 @@
|
||||
#include <xtos.hh>
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user