singrdk/base/Kernel/Singularity/Processor.cs

1353 lines
48 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Processor.cs
//
// Note:
//
//#define DEBUG_EXCEPTIONS
//#define DEBUG_INTERRUPTS
//#define DEBUG_DISPATCH_TIMER
//#define DEBUG_DISPATCH_IO
//#define SAMPLE_PC
// #define SINGULARITY_ASMP
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity.Hal;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.Scheduling;
using Microsoft.Singularity.X86;
// haryadi: for Abi Call
// using Microsoft.Singularity.V1.Services;
namespace Microsoft.Singularity
{
[CLSCompliant(false)]
public enum ProcessorEvent : ushort
{
Exception = 0,
Resume = 1,
Interrupt = 2
}
[CLSCompliant(false)]
[CCtorIsRunDuringStartup]
public class Processor
{
private const uint interruptStackSize = 0x2000;
private const uint exceptionStackSize = 0x2000;
private const uint schedulerStackSize = 0x2000;
internal unsafe ProcessorContext* context;
public static Processor[] processorTable;
//As requested/desired by the SchedulerClock
private HalPic pic;
private HalTimer timer;
private HalClock clock;
// haryadi
private static IHalMemory halMemory;
private byte timerInterrupt;
private byte clockInterrupt;
private bool inInterruptContext = false;
private bool halted = false;
public readonly int processorIndex;
public uint NumExceptions = 0;
public uint NumInterrupts = 0;
public uint NumContextSwitches = 0;
private int[] interruptCounts;
private ulong cyclesPerSecond;
private static int runningCpus = 0;
public HalTimer Timer
{
[NoHeapAllocation]
get { return timer; }
}
public Thread IdleThread;
internal static void InitializeProcessorTable()
{
processorTable = new Processor [MpBootInfo.MAX_CPU];
for (int i = 0; i < processorTable.Length; i++) {
processorTable[i] = new Processor(i);
}
DebugStub.WriteLine("Processors: {0} of {1}",
__arglist(processorTable.Length, MpBootInfo.MAX_CPU));
}
internal static void AllocateStack(UIntPtr size, out UIntPtr begin, out UIntPtr limit)
{
Kernel.Waypoint(818);
size = MemoryManager.PagePad(size);
limit = MemoryManager.KernelAllocate(
MemoryManager.PagesFromBytes(size), null, 0, System.GCs.PageType.Stack);
begin = limit + size;
Kernel.Waypoint(819);
}
private Processor(int index)
{
processorIndex = index;
if (interruptCounts == null) {
interruptCounts = new int [256];
}
}
public unsafe void Initialize(int processorId)
{
processorTable[processorId] = this;
BootInfo bi = BootInfo.GetBootInfo();
CpuInfo ci = bi.GetCpuInfo(processorId);
context = (ProcessorContext*) ci.Fs32;
context->UpdateAfterGC(this);
AllocateStack(interruptStackSize,
out context->interruptStackBegin,
out context->interruptStackLimit);
AllocateStack(exceptionStackSize,
out context->exceptionStackBegin,
out context->exceptionStackLimit);
AllocateStack(schedulerStackSize,
out context->schedulerStackBegin,
out context->schedulerStackLimit);
Tracing.Log(Tracing.Debug, "Initialized Processor {0}",
(UIntPtr)processorId);
Tracing.Log(Tracing.Debug, "asmInterruptStack={0:x}..{1:x}",
context->interruptStackBegin,
context->interruptStackLimit);
inInterruptContext = false;
MpExecution.AddProcessorContext(context);
Interlocked.Increment(ref runningCpus);
}
[NoHeapAllocation]
public void Uninitialize(int processorId)
{
Tracing.Log(Tracing.Debug, "UnInitializing Processor {0}",
(UIntPtr)processorId);
Interlocked.Decrement(ref runningCpus);
// Processor is out of commission
HaltUntilInterrupt();
}
private static void MeasureFxSavePerformance()
{
#if DO_FXSAVE_TEST
DebugStub.WriteLine("Starting fxsave test.");
int timePerFxsave = TestFxsave();
DebugStub.Print("Cycles per fxsave/fxrstor pair: {0}",
__arglist(timePerFxsave));
#endif // DO_FXSAVE_TEST
}
private static void MeasureFs0Performance()
{
#if DO_FS0_TEST
DebugStub.WriteLine("Starting fs:[0] test.");
int timePerFs0 = TestFs0();
DebugStub.WriteLine("Cycles per fs:[0] test: {0}",
__arglist(timePerFs0));
#endif // DO_FS0_TEST
}
private static void MeasureCliStiPerformance()
{
#if DO_CLI_STI_TEST
DebugStub.WriteLine("Starting cli/sti test.");
int timePerCli = TestCliSti();
DebugStub.WriteLine("Cycles per cli/sti test: {0}",
__arglist(timePerCli));
ulong beg;
ulong end;
int loops = 1000000;
DebugStub.WriteLine("Starting C# cli/sti test.");
bool enabled = DisableInterrupts();
RestoreInterrupts(true);
beg = HalGetCycleCount();
while (loops-- > 0) {
RestoreInterrupts(true); // 1
DisableInterrupts();
RestoreInterrupts(true); // 2
DisableInterrupts();
RestoreInterrupts(true); // 3
DisableInterrupts();
RestoreInterrupts(true); // 4
DisableInterrupts();
RestoreInterrupts(true); // 5
DisableInterrupts();
RestoreInterrupts(true); // 6
DisableInterrupts();
RestoreInterrupts(true); // 7
DisableInterrupts();
RestoreInterrupts(true); // 8
DisableInterrupts();
RestoreInterrupts(true); // 9
DisableInterrupts();
RestoreInterrupts(true); // 10
DisableInterrupts();
}
end = HalGetCycleCount();
DisableInterrupts();
RestoreInterrupts(enabled);
timePerCli = ((int)((end - beg) / 1000000)) / 10;
DebugStub.WriteLine("Cycles per C# cli/sti test: {0}",
__arglist(timePerCli));
#endif // DO_CLI_STI_TEST
}
public void AddPic(HalPic pic)
{
Tracing.Log(Tracing.Audit, "AddPic({0})\n",
Kernel.TypeName(pic));
this.pic = pic;
MeasureFxSavePerformance();
MeasureFs0Performance();
MeasureCliStiPerformance();
}
[NoHeapAllocation]
public void AddTimer(byte interrupt, HalTimer timer)
{
Tracing.Log(Tracing.Audit, "AddTimer({0}) on {1}\n",
Kernel.TypeName(timer), interrupt);
this.timer = timer;
this.timerInterrupt = interrupt;
}
[NoHeapAllocation]
public void AddClock(byte interrupt, HalClock clock)
{
Tracing.Log(Tracing.Audit, "AddClock({0}) on {1}\n",
Kernel.TypeName(clock), interrupt);
this.clock = clock;
this.clockInterrupt = interrupt;
}
[NoHeapAllocation]
public static void AddHalMemory(IHalMemory aHalMemory)
{
Tracing.Log(Tracing.Audit, "AddHalMemory({0})\n",
Kernel.TypeName(aHalMemory));
halMemory = aHalMemory;
}
[NoHeapAllocation]
internal unsafe void Display()
{
int stackVariable;
UIntPtr currentStack = new UIntPtr(&stackVariable);
unchecked {
Tracing.Log(Tracing.Debug, "Interrupt stack: {0:x} {1:x}..{2:x} uses",
currentStack,
context->interruptStackBegin,
context->interruptStackLimit);
}
}
// Returns the processor that the calling thread is running on.
// Needs to be fixed. (Added for consistency)
public static Processor CurrentProcessor
{
[NoHeapAllocation]
get { return GetCurrentProcessor(); }
}
[NoHeapAllocation]
public static int GetCurrentProcessorId()
{
return GetCurrentProcessor().Id;
}
public unsafe int Id
{
[NoHeapAllocation]
get { return context->cpuId; }
}
[NoHeapAllocation]
public static void HaltUntilInterrupt()
{
CurrentProcessor.halted = true;
HaltUntilInterruptNative();
}
public bool InInterruptContext
{
[NoHeapAllocation]
get { return inInterruptContext; }
}
[NoHeapAllocation]
public int GetInterruptCount(byte interrupt)
{
return interruptCounts[interrupt];
}
public int GetIrqCount(byte irq)
{
HalPic pic = CurrentProcessor.pic;
return interruptCounts[pic.IrqToInterrupt(irq)];
}
public static byte GetMaxIrq()
{
return CurrentProcessor.pic.MaximumIrq;
}
public static ulong CyclesPerSecond
{
[NoHeapAllocation]
get { return GetCurrentProcessor().cyclesPerSecond; }
[NoHeapAllocation]
set { GetCurrentProcessor().cyclesPerSecond = value; }
}
public static ulong CycleCount
{
[NoHeapAllocation]
get { return GetCycleCount(); }
}
//////////////////////////////////////////////////////////////////////
//
//
#if SAMPLE_PC
#if SINGULARITY_MP
// This should be easy to fix.
#error "SAMPLE_PC does not work in conjunction with SINGULARITY_MP."
#endif
const int pcLog2Samples = 16;
const int pcMaxSamples = 2 << pcLog2Samples;
const int sampleMask = pcMaxSamples - 1;
static bool nextSampleIdle = false;
static UIntPtr[] pcSamples = null;
static int pcHead = 0; // head position
static int pcLength = 0; // length of live data in buffer
static uint pcSamplesI = 0;
static ulong pcSamplesCounterThen;
#endif // SAMPLE_PC
[NoHeapAllocation]
public static bool SamplingEnabled()
{
#if SAMPLE_PC
return true;
#else
return false;
#endif // SAMPLE_PC
}
internal static void StartSampling()
{
#if SAMPLE_PC
pcSamplesCounterThen = Processor.CycleCount;
pcSamples = new UIntPtr[2 << pcLog2Samples];
nextSampleIdle = false;
#endif // SAMPLE_PC
}
[NoHeapAllocation]
internal static void NextSampleIsIdle()
{
#if SAMPLE_PC
nextSampleIdle = true;
#endif // SAMPLE_PC
}
//////////////////////////////////////////////////// External Methods.
//
[MethodImpl(MethodImplOptions.InternalCall)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern ulong GetCycleCount();
[MethodImpl(MethodImplOptions.InternalCall)]
[StackBound(32)]
[NoHeapAllocation]
internal static unsafe extern UIntPtr GetFrameEip(UIntPtr ebp);
[MethodImpl(MethodImplOptions.InternalCall)]
[StackBound(32)]
[NoHeapAllocation]
internal static unsafe extern UIntPtr GetFrameEbp(UIntPtr ebp);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern UIntPtr GetStackPointer();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern UIntPtr GetFramePointer();
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
private static extern Processor GetCurrentProcessor();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static unsafe extern ThreadContext * GetCurrentThreadContext();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static unsafe extern ProcessorContext * GetCurrentProcessorContext();
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern Thread GetCurrentThread();
[MethodImpl(MethodImplOptions.InternalCall)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void SetCurrentThreadContext(ref ThreadContext context);
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.GCFRIEND)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void SwitchToThreadContext(ref ThreadContext oldContext, ref ThreadContext newContext);
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void SwitchToThreadContextNoGC(ref ThreadContext newContext);
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void TestSaveLoad(ref ThreadContext newContext);
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void TestSave(ref ThreadContext newContext);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void EnterRing3();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern bool AtKernelPrivilege();
//////////////////////////////////////////////////////////////////////
//
//
// These methods are currently marked external because they are used
// by device drivers. We need a tool to verify that device drivers
// are in fact using them correctly!
//
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern bool DisableInterrupts();
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern void RestoreInterrupts(bool enabled);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern void SetIdtTable();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
internal static extern void ClearIdtTable();
// Use this method for assertions only!
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern bool InterruptsDisabled();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern void HaltUntilInterruptNative();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern void InitFpu();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern uint ReadFpuStatus();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern void ClearFpuStatus();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern void WriteMsr(uint offset,
ulong value);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern ulong ReadMsr(uint offset);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
public static extern ulong ReadPmc(uint offset);
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(64)]
[NoHeapAllocation]
public static extern void ReadCpuid(uint feature,
out uint v0,
out uint v1,
out uint v2,
out uint v3);
#if DO_FXSAVE_TEST
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(128)]
[NoHeapAllocation]
public static extern int TestFxsave();
#endif
#if DO_FS0_TEST
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(128)]
[NoHeapAllocation]
public static extern int TestFs0();
#endif
#if DO_CLI_STI_TEST
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(128)]
[NoHeapAllocation]
public static extern int TestCliSti();
#endif
//////////////////////////////////////////////////////////////////////
//
/// <summary>
/// Hardware exceptions are mapped to vectors 0x00..0x1f.
/// They always use a processor context record.
/// </summary>
[AccessedByRuntime("referenced from Processor.cpp")]
[NoHeapAllocation]
internal static void DispatchException(int interrupt,
ref ThreadContext context)
{
// Tracing.Log(Tracing.Debug, "Exception {0}", (uint)interrupt);
GetCurrentProcessor().DispatchSpecificException(interrupt, ref context);
}
[NoHeapAllocation]
private void DispatchSpecificException(int interrupt,
ref ThreadContext context)
{
// Indicate that we are in an interrupt context.
bool wasInInterrupt = inInterruptContext;
inInterruptContext = true;
NumExceptions++;
interruptCounts[interrupt]++;
if (interrupt == EVectors.Nmi && MpExecution.FreezeRequested) {
MpExecution.FreezeProcessor(ref context);
}
else if (interrupt == EVectors.SingleStep) {
DebugStub.Trap(ref context, false);
}
else if (interrupt == EVectors.Breakpoint) {
DebugStub.Trap(ref context, false);
}
else if (interrupt == EVectors.FirstChanceException) {
DebugStub.Trap(ref context, true);
}
else if (interrupt == EVectors.FpuMathFault) {
if ((context.mmx.fsw & Fpsw.StackFaultError) != 0) {
if ((context.mmx.fsw & Fpsw.C1) != 0) {
DebugStub.WriteLine("FPU Stack Overflow Exception (FP SW = 0x{0:x4})",
__arglist(context.mmx.fsw));
}
else {
DebugStub.WriteLine("FPU Stack Underflow Exception (FP SW = 0x{0:x4})",
__arglist(context.mmx.fsw));
}
}
else {
DebugStub.WriteLine("FPU Unit exception (FP SW = 0x{0:x4})",
__arglist(context.mmx.fsw));
}
DebugStub.Trap(ref context, false);
context.mmx.fcw |= Fpsw.ErrorClearMask;
}
else {
Tracing.Log(Tracing.Debug,
"No recognized exception handler (0x{0:x2}",
(uint)interrupt);
DebugStub.Trap(ref context, false);
DebugStub.WriteLine("No recognized exception handler (0x{0:x2}",
__arglist(interrupt));
Thread.Display(ref context, "");
}
inInterruptContext = wasInInterrupt;
}
/// <summary>
/// Hardware interrupts are mapped to vectors 0x20..0xff.
/// They always use the thread's context record.
/// </summary>
[AccessedByRuntime("referenced from Processor.cpp")]
[NoHeapAllocation]
internal static void DispatchInterrupt(int interrupt,
ref ThreadContext context)
{
GetCurrentProcessor().DispatchSpecificInterrupt(interrupt, ref context);
}
[NoHeapAllocation]
private unsafe void DispatchSpecificInterrupt(int interrupt,
ref ThreadContext context)
{
// Indicate that we are in an interrupt context.
Thread target = null;
bool wasInInterrupt = inInterruptContext;
inInterruptContext = true;
Kernel.Waypoint(801);
NumInterrupts++;
NumExceptions++;
interruptCounts[interrupt]++;
// Don't generate loads of output for debugger-related interrupts
#if DEBUG_INTERRUPTS
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
Thread.DisplayAbbrev(ref context, " int beg");
Thread.Display(ref context, " int beg");
if (!context.IsFirst()) {
DebugStub.WriteLine("*-*-*-*-*-* !IsFirst *-*-*-*-*-*");
Thread.Display(ref context, " !IsFirst");
}
#endif
#if SAMPLE_PC
// Sample PC values
ulong pcNow = Processor.CycleCount;
uint pcDiff = unchecked((uint)(pcNow - pcSamplesCounterThen));
if (pcSamples != null && pcDiff != 0 && nextSampleIdle == false) {
int oldPcHead = pcHead;
// Sample instance number
pcSamples[pcHead] = (UIntPtr)unchecked(pcSamplesI++);
pcHead = unchecked((pcHead + 1) & sampleMask);
// Save relative time in ticks
pcSamples[pcHead] = pcDiff;
pcHead = unchecked((pcHead + 1) & sampleMask);
pcSamples[pcHead] = (UIntPtr)interrupt;
pcHead = unchecked((pcHead + 1) & sampleMask);
pcSamplesCounterThen = pcNow;
// Save stack
UIntPtr eip = context.eip;
UIntPtr ebp = context.ebp;
while (true) {
if (eip == 0) { break; }
pcSamples[pcHead] = eip;
pcHead = (pcHead + 1) & sampleMask;
if (ebp == 0) { break; }
eip = Processor.GetFrameEip(ebp);
ebp = Processor.GetFrameEbp(ebp);
}
pcSamples[pcHead] = 0;
pcHead = (pcHead + 1) & sampleMask;
pcLength += (pcHead + pcMaxSamples - oldPcHead) & sampleMask;
if (pcLength > pcMaxSamples)
pcLength = pcMaxSamples;
// Clear any partially overwritten sample set ahead
int i = pcHead;
while (pcSamples[i] != 0) {
pcSamples[i] = 0;
pcLength--;
i = (i + 1) & sampleMask;
}
}
nextSampleIdle = false;
#endif // SAMPLE_PC
if (halted) {
clock.CpuResumeFromHaltEvent();
halted = false;
}
unchecked {
if (interrupt != clockInterrupt) {
// We don't log the clockInterrupt because of all the spew.
Tracing.Log(Tracing.Debug, "Interrupt 0x{0:x}, count={1:x}",
(UIntPtr)(uint)interrupt,
(UIntPtr) interruptCounts[interrupt]
);
}
}
Monitoring.Log(Monitoring.Provider.Processor,
(ushort)ProcessorEvent.Interrupt, 0,
(uint)interrupt, 0, 0, 0, 0);
if ((context.efl & EFlags.IF) == 0) {
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
Thread.DisplayAbbrev(ref context, " int beg");
DebugStub.WriteLine("Interrupt 0x{0:x2} thrown while interrupts disabled.",
__arglist(interrupt));
DebugStub.Break();
}
if (interrupt == timerInterrupt) {
timer.ClearInterrupt();
SchedulerTime now = SchedulerTime.Now;
Scheduler.DispatchLock();
try {
if (context.thread == IdleThread) {
#if TRACE_INTERRUPTS
Tracing.Log(Tracing.Audit,
"Timer interrupt in idle.");
#endif // TRACE_INTERRUPTS
target = Scheduler.OnTimerInterrupt(null, now);
}
else {
#if TRACE_INTERRUPTS
Tracing.Log(Tracing.Audit,
"Timer interrupt in tid={0:x3}.",
(uint)context.thread.GetThreadId());
#endif // TRACE_INTERRUPTS
target = Scheduler.OnTimerInterrupt(context.thread, now);
}
Scheduler.SelectingThread(target);
#if DEBUG_DISPATCH_TIMER
DebugStub.WriteLine("OnTime.Selecting");
#endif // DEBUG_DISPATCH_TIMER
}
finally {
Scheduler.DispatchRelease();
}
}
else if (interrupt == clockInterrupt) {
clock.ClearInterrupt();
#if DEBUG
// Check for a debug break.
if (DebugStub.PollForBreak()) {
DebugStub.WriteLine("Debugger ctrl-break after interrupt 0x{0:x2}",
__arglist(interrupt));
MpExecution.FreezeAllProcessors();
DebugStub.Break(); // used to be: Trap(ref context, false);
MpExecution.ThawAllProcessors();
}
#endif
}
else if (interrupt == EVectors.PingPongInt) {
HalDevices.ClearFixedIPI();
inInterruptContext = false;
int task = MpExecution.GetIntrPingPong(this.Id);
DebugStub.WriteLine
("HSG: ***** cpu.{0} gets {1}", __arglist(this.Id, task));
RunPingPongInt(task-1); // send again
inInterruptContext = true;
}
else if (interrupt == EVectors.ApImage) {
HalDevices.ClearFixedIPI();
inInterruptContext = false;
MpExecution.ApImage apImage;
bool okay = MpExecution.GetIntrApImage(this.Id, out apImage);
if (!okay) {
DebugStub.WriteLine("ERROR: ApImage queue is empty");
DebugStub.Break();
}
inInterruptContext = true;
DebugStub.WriteLine ("HSG: ** cpu.{0} gets e.{1:x}",
__arglist(this.Id,
apImage.entryPoint));
Processor.MpCallEntryPoint(apImage.entryPoint);
}
else if (interrupt == EVectors.AbiCall) {
HalDevices.ClearFixedIPI();
#if GENERATE_ABI_SHIM
// haryadi: tells AP service thread to process
// this abi call later
ApServiceThread.abiEvent.Set();
#endif
}
else if (interrupt == EVectors.HaltApProcessors) {
Processor p = Processor.CurrentProcessor;
if (p.Id != 0) {
if (context.thread != IdleThread) {
Scheduler.DispatchLock();
try {
Scheduler.OnProcessorShutdown(context.thread);
}
finally {
Scheduler.DispatchRelease();
}
}
p.Uninitialize(p.Id);
}
}
else if (!HalDevices.InternalInterrupt((byte)interrupt)) {
IoIrq.SignalInterrupt(pic.InterruptToIrq((byte)interrupt));
pic.ClearInterrupt((byte)interrupt);
Scheduler.DispatchLock();
try {
if (context.thread == IdleThread) {
#if TRACE_INTERRUPTS
Tracing.Log(Tracing.Audit,
"I/O interrupt in idle.");
#endif // TRACE_INTERRUPTS
target = Scheduler.OnIoInterrupt(null);
}
else {
#if TRACE_INTERRUPTS
Tracing.Log(Tracing.Audit,
"I/O interrupt in tid={0:x3}.",
(uint)context.thread.GetThreadId());
#endif // TRACE_INTERRUPTS
target = Scheduler.OnIoInterrupt(context.thread);
}
#if DEBUG_DISPATCH_IO
DebugStub.WriteLine("++DispatchInterruptEvent Irq={0:x2}, Thread={1:x8}",
__arglist(pic.InterruptToIrq((byte)interrupt),
Kernel.AddressOf(target)));
#endif // DEBUG_DISPATCH_IO
Scheduler.SelectingThread(target);
}
finally {
Scheduler.DispatchRelease();
}
}
#if DEBUG_INTERRUPTS
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
Thread.DisplayAbbrev(ref context, " int fin");
if (!InterruptsDisabled()) {
DebugStub.WriteLine(" interrupts enabled!!!!!!!");
Thread.Display(ref context, " !IsFirst");
DebugStub.Break();
}
#if DEBUG_DEEPER
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
Thread.DisplayAbbrev(ref context, " int end");
#endif
#endif
// if (context.lastExecutionTimeUpdate !=
// context.thread.context.lastExecutionTimeUpdate) {
// DebugStub.WriteLine("context != context.thread.context: {0}, {1}, ",
// __arglist(context.lastExecutionTimeUpdate,
// context.thread.context.lastExecutionTimeUpdate));
// }
#if THREAD_TIME_ACCOUNTING
ulong now_a = Processor.CycleCount;
context.executionTime += now_a -
context.lastExecutionTimeUpdate;
#endif
if (target != null) {
#if THREAD_TIME_ACCOUNTING
target.context.lastExecutionTimeUpdate = now_a;
#endif
Monitoring.Log(Monitoring.Provider.Processor,
(ushort)ProcessorEvent.Resume, 0,
(uint)target.context.threadIndex, 0, 0, 0, 0);
// Return to the thread selected by the scheduler if any.
SetCurrentThreadContext(ref target.context);
}
else {
#if THREAD_TIME_ACCOUNTING
context.lastExecutionTimeUpdate = now_a;
#endif
Monitoring.Log(Monitoring.Provider.Processor,
(ushort)ProcessorEvent.Resume, 0,
(uint)context.threadIndex, 0, 0, 0, 0);
SetCurrentThreadContext(ref context);
}
inInterruptContext = wasInInterrupt;
}
//Simulator Use
//Sets the nextTimerInterrupt
[NoHeapAllocation]
public bool SetNextTimerInterrupt(TimeSpan delta)
{
long span = delta.Ticks;
if (span > timer.MaxInterruptInterval) {
span = timer.MaxInterruptInterval;
}
else if (span < timer.MinInterruptInterval) {
span = timer.MinInterruptInterval;
}
bool success = timer.SetNextInterrupt(span);
DebugStub.Assert(success);
return success;
}
[NoHeapAllocation]
public bool SetNextTimerInterrupt(SchedulerTime until)
{
return SetNextTimerInterrupt(until - SchedulerTime.Now);
}
[NoHeapAllocation]
internal static unsafe void UpdateAfterGC(Thread currentThread)
{
// Update the processor pointers in processor contexts
for (int i = 0; i < Processor.processorTable.Length; i++) {
Processor p = Processor.processorTable[i];
if (p != null) {
p.context->UpdateAfterGC(p);
}
}
// Ensure that Thread.CurrentThread returns new thread object
SetCurrentThreadContext(ref currentThread.context);
}
//////////////////////////////////////////////////////////////////////
//
//
// These methods are public and safe to use from any where provided
// there's at least 2 call frame on the stack.
//
[NoHeapAllocation]
public static UIntPtr GetCallerEip()
{
UIntPtr currentFrame = GetFramePointer();
UIntPtr callerFrame = GetFrameEbp(currentFrame);
if (callerFrame == UIntPtr.Zero) {
return UIntPtr.Zero;
}
UIntPtr callersCaller = GetFrameEip(callerFrame);
return callersCaller;
}
/// <summary>
/// Provides a mini stack trace starting from the caller of the caller
/// of this method.
/// </summary>
[NoHeapAllocation]
public static void GetStackEips(out UIntPtr pc1, out UIntPtr pc2, out UIntPtr pc3)
{
pc1 = UIntPtr.Zero;
pc2 = UIntPtr.Zero;
pc3 = UIntPtr.Zero;
UIntPtr currentFrame = GetFramePointer();
UIntPtr callerFrame = GetFrameEbp(currentFrame);
if (callerFrame == UIntPtr.Zero) {
return;
}
pc1 = GetFrameEip(callerFrame);
callerFrame = GetFrameEbp(callerFrame);
if (callerFrame == UIntPtr.Zero) {
return;
}
pc2 = GetFrameEip(callerFrame);
callerFrame = GetFrameEbp(callerFrame);
if (callerFrame == UIntPtr.Zero) {
return;
}
pc3 = GetFrameEip(callerFrame);
}
/// <summary>
/// Provides the full stack trace starting from the caller of the caller
/// of this method.
/// </summary>
/// <returns>Eip values in stack array from top to bottom</returns>
[NoHeapAllocation]
public static void GetStackEips(UIntPtr[] stack)
{
if (stack == null) {
return;
}
UIntPtr currentFrame = GetFramePointer();
UIntPtr callerFrame = GetFrameEbp(currentFrame);
for (int index = 0; callerFrame != UIntPtr.Zero && index < stack.Length; index++) {
stack[index] = GetFrameEip(callerFrame);
callerFrame = GetFrameEbp(callerFrame);
}
}
//////////////////////////////////////////////////////////////////////
//
//
// These (native) methods manipulate the local processor's paging
// hardware. They can be used even before Processor.Initialize()
// has been called.
//
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern void PrivateEnablePaging(uint pdpt);
internal static void EnablePaging(AddressSpace bootstrapSpace)
{
PrivateEnablePaging((uint)bootstrapSpace.PdptPage.Value);
}
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern void PrivateChangeAddressSpace(uint pdpt);
internal static void ChangeAddressSpace(AddressSpace space)
{
PrivateChangeAddressSpace((uint)space.PdptPage.Value);
}
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
internal static extern void DisablePaging();
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern void PrivateInvalidateTLBEntry(UIntPtr pageAddr);
internal static void InvalidateTLBEntry(UIntPtr pageAddr)
{
DebugStub.Assert(MemoryManager.IsPageAligned(pageAddr));
PrivateInvalidateTLBEntry(pageAddr);
}
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(16)]
[NoHeapAllocation]
private static extern uint GetCr3();
// haryadi
[AccessedByRuntime("output to header : defined in Processor.cpp")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.NOGC)]
[StackBound(64)]
[NoHeapAllocation]
public static extern void MpCallEntryPoint(UIntPtr entry);
internal static AddressSpace GetCurrentAddressSpace()
{
return new AddressSpace(new PhysicalAddress(GetCr3()));
}
//
public static IHalMemory.ProcessorAffinity[] GetProcessorAffinity()
{
IHalMemory.ProcessorAffinity[] processors =
halMemory.GetProcessorAffinity();
return processors;
}
public static IHalMemory.MemoryAffinity[] GetMemoryAffinity()
{
IHalMemory.MemoryAffinity[] memories =
halMemory.GetMemoryAffinity();
return memories;
}
/// <summary> Start application processors. </summary>
[System.Diagnostics.Conditional("SINGULARITY_MP")]
public static void StartApProcessors()
{
// At this point only the BSP is running.
Tracing.Log(Tracing.Debug, "Processor.StartApProcessors()");
HalDevices.StartApProcessors();
// At this point the BSP and APs are running.
}
/// <summary> Stop application processors. </summary>
[NoHeapAllocation]
[System.Diagnostics.Conditional("SINGULARITY_MP")]
public static void StopApProcessors()
{
// At this point the BSP and APs are running.
Tracing.Log(Tracing.Debug, "Processor.StopApProcessors()");
HalDevices.BroadcastFixedIPI((byte)EVectors.HaltApProcessors,
true);
while (GetRunningProcessorCount() != 1) {
/* Thread.Sleep(100); Thread.Sleep needs NoHeapAllocation annotation */
Thread.Yield();
}
DebugStub.RevertToUniprocessor();
// At this point only the BSP is running.
}
/// <summary> Gets the number of processors in use by
/// the system. </summary>
[NoHeapAllocation]
public static int GetRunningProcessorCount()
{
return runningCpus;
}
/// <summary> Gets the total number of processors known
/// to the system. This includes processors not
/// currently in use by the system. </summary>
[NoHeapAllocation]
public static int GetProcessorCount()
{
return HalDevices.GetProcessorCount();
}
public static int GetDomainCount()
{
IHalMemory.ProcessorAffinity[] processors =
halMemory.GetProcessorAffinity();
uint domain = 0;
for (int i = 0; i < processors.Length; i++) {
if (processors[i].domain > domain) {
domain = processors[i].domain;
}
}
domain++; // domain number starts from 0
return (int)domain;
}
public static bool HasAffinityInfo()
{
IHalMemory.ProcessorAffinity[] processors =
halMemory.GetProcessorAffinity();
IHalMemory.MemoryAffinity[] memories =
halMemory.GetMemoryAffinity();
if (processors == null || memories == null) {
return false;
}
return true;
}
// haryadi -- return cpuId if context is not null
[NoHeapAllocation]
public unsafe int ValidProcessorId(int i)
{
if (context != null) {
return context->cpuId;
}
else {
return -1;
}
}
// haryadi -- determine next ping pong receiver
// i.e. find next processor, if this is
// the last processors, wrap to p0
[NoHeapAllocation]
private static int GetPingPongReceiver()
{
int from = GetCurrentProcessorId();
int to;
// check if processor table is valid
if (processorTable == null || processorTable.Length == 1) {
DebugStub.WriteLine
("HSG: Processors.cs, procTable null/uniproc ...");
return -1;
}
// set up neighbor
to = (from + 1) % processorTable.Length;
// check if processor is valid
if (processorTable[to].ValidProcessorId(to) >= 0) {
to = processorTable[to].ValidProcessorId(to);
}
// this is the last processors, wrap to p0
else {
// DebugStub.WriteLine
// ("HSG: Processors.cs, cpu.{0} is the last processor",
// __arglist(from));
to = 0;
to = processorTable[to].ValidProcessorId(to);
if (to == from) {
DebugStub.WriteLine
("HSG: Processor.cs, only 1 valid processor (error)");
return -1;
}
}
return to;
}
// haryadi -- start ping pong
[NoHeapAllocation]
public static int RunPingPongInt(int start)
{
// Done ping pong
if (start == 0) {
return -1;
}
int from = GetCurrentProcessorId();
int to = GetPingPongReceiver();
// invalid to (or no destination processor)
if (to < 0) {
return -1;
}
// from and to are ready now
DebugStub.WriteLine
("HSG: ***** cpu.{0} sends {1} to p{2}",
__arglist(from, start, to));
// disable interrupt
bool iflag = Processor.DisableInterrupts();
// put integer "to" to neighbor interrupt task queue
MpExecution.PutIntrPingPong(to, start);
// send ping pong
MpExecution.StartPingPongInt(from, to, (byte)EVectors.PingPongInt);
// enable interrupt
Processor.RestoreInterrupts(iflag);
// DebugStub.WriteLine
// ("HSG: Processors.cs, cpu.{0} sent to p{1} (pc:{2}) ",
// __arglist(from, to, start));
return 0;
}
}
}