////////////////////////////////////////////////////////////////////////////////
//
// 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 CHECK_DISABLE_INTERRUPTS
// #define SINGULARITY_ASMP
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity.Eventing;
using Microsoft.Singularity.Hal;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Isal;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.Scheduling;
using Microsoft.Singularity.V1.Threads;
// 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]
[AccessedByRuntime("referenced in hal.cpp/processor.cpp")]
public class Processor
{
// Callback object for context switching
static ResumeThreadCallback resumeThreadCallback;
private ProcessorLogger ProcessorLog = null;
private ProcessorCounter processorCounter = null;
internal SamplingProfiler Profiler = null;
internal bool nextSampleIdle = false;
internal static bool IsSamplingEnabled = false;
//
// This is called by HalDevicesApic.StartApProcessors() when
// initializing additional physical AP processors to set its
// hardware state when its started.
//
public void InitializeKernelThreadState(Thread thread,
UIntPtr kernelStackBegin,
UIntPtr kernelStackLimit)
{
kernelThread = thread;
kernelStackBegin = kernelStackBegin;
kernelStackLimit = kernelStackLimit;
}
public HalTimer Timer
{
[NoHeapAllocation]
get { return timer; }
}
public HalClock Clock
{
[NoHeapAllocation]
get { return clock; }
}
internal static void InitializeProcessorTable(int cpus)
{
// use the full value initially
ExpectedProcessors = cpus;
processorTable = new Processor[cpus];
for (int i = 0; i < processorTable.Length; i++) {
processorTable[i] = new Processor(i);
}
DebugStub.WriteLine("Processors: {0} of {1}",
__arglist(processorTable.Length, cpus));
}
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];
}
ProcessorLog = ProcessorLogger.Create("ProcessorLogger:"+index.ToString());
processorCounter = ProcessorCounter.Create("ProcessorCounters:"+index.ToString(), 256);
DebugStub.WriteLine("Processor: {0}", __arglist(index));
}
public void EnableProfiling()
{
if (SamplingEnabled()) {
Profiler = SamplingProfiler.Create("SampleProfiler:" + Id.ToString(),
32, // maximum stack depth
Kernel.ProfilerBufferSize); // sampling buffer size
DebugStub.WriteLine("Sampling profiler enabled");
}
}
public static Processor EnableProcessor(int processorId)
{
Processor p = processorTable[processorId];
p.Initialize(processorId);
return p;
}
private unsafe void Initialize(int processorId)
{
uint DefaultStackSize = 0xA000;
processorTable[processorId] = this;
context = (ProcessorContext*) Isa.GetCurrentCpu();
DebugStub.WriteLine("Processor context: {0} {1:x8}",
__arglist(processorId, Kernel.AddressOf(context)));
context->UpdateAfterGC(this);
if (0 != processorId) {
Thread.BindKernelThread(kernelThread,
kernelStackBegin,
kernelStackLimit);
}
AllocateStack(DefaultStackSize,
out context->cpuRecord.interruptStackBegin,
out context->cpuRecord.interruptStackLimit);
Tracing.Log(Tracing.Debug, "Initialized Processor {0}",
(UIntPtr)processorId);
Tracing.Log(Tracing.Debug, "asmInterruptStack={0:x}..{1:x}",
context->cpuRecord.interruptStackBegin,
context->cpuRecord.interruptStackLimit);
#if false
DebugStub.WriteLine("proc{0}: InterruptStack={1:x}..{2:x}",
__arglist(
processorId,
context->cpuRecord.interruptStackBegin,
context->cpuRecord.interruptStackLimit
));
#endif
Interlocked.Increment(ref runningCpus);
MpExecution.AddProcessorContext(context);
// Need to allocate this callback object outside of NoThreadAllocation region
if (processorId == 0) {
resumeThreadCallback = new ResumeThreadCallback();
}
Isa.EnableCycleCounter();
}
///
///
/// Initialize dispatcher
///
///
/// Id of the processor dispatcher belongs to
///
public static void InitializeDispatcher(int processorId)
{
// Create a processor dispatcher
processorTable[processorId].dispatcher = new ProcessorDispatcher();
// Initialize dispatcher
processorTable[processorId].dispatcher.Initialize(processorTable[processorId]);
}
///
///
/// Activate Timer
///
///
///
public static void ActivateTimer(int processorId)
{
processorTable[processorId].timer.SetNextInterrupt(TimeSpan.FromMilliseconds(5));
}
[NoHeapAllocation]
public void Uninitialize(int processorId)
{
Tracing.Log(Tracing.Debug, "UnInitializing Processor {0}",
(UIntPtr)processorId);
Interlocked.Decrement(ref runningCpus);
// #if DEBUG
// Interrupts should be off now
if (!InterruptsDisabled()) {
DebugStub.WriteLine("Processor::Uninitialize AP Processor does not have interrupts disabled\n");
DebugStub.Break();
}
// #endif // DBG
// Processor is out of commission
HaltUntilInterrupt();
// #if DEBUG
DebugStub.WriteLine("Processor::Uninitialize: AP processor woke up on shutdown!\n");
DebugStub.Break();
// #endif // DBG
}
public void AddPic(HalPic pic)
{
Tracing.Log(Tracing.Audit, "AddPic({0})\n",
Kernel.TypeName(pic));
this.pic = pic;
}
[NoHeapAllocation]
public HalPic GetPic()
{
return this.pic;
}
[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 AddMemory(HalMemory 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->cpuRecord.interruptStackBegin,
context->cpuRecord.interruptStackLimit);
}
}
// Returns the processor that the calling thread is running on.
// TODO:Needs to be fixed. (Added for consistency)
public static Processor CurrentProcessor
{
[NoHeapAllocation]
get { return GetCurrentProcessor(); }
}
///
///
/// Retrieve current dispatcher
///
public ProcessorDispatcher Dispatcher
{
[NoHeapAllocation]
get { return dispatcher; }
}
[NoHeapAllocation]
public static int GetCurrentProcessorId()
{
return GetCurrentProcessor().Id;
}
public unsafe int Id
{
[NoHeapAllocation]
get {
return context->cpuRecord.id;
}
}
[NoHeapAllocation]
public static Processor GetProcessor(int i)
{
if (null == processorTable && i == 0) {
return CurrentProcessor;
}
else {
return processorTable[i];
}
}
public static int CpuCount
{
[NoHeapAllocation]
get { return null == processorTable ? 1 : processorTable.Length; }
}
[NoHeapAllocation]
public static void HaltUntilInterrupt()
{
Platform.ThePlatform.Halt();
}
[NoHeapAllocation]
public int GetInterruptCount(byte interrupt)
{
return interruptCounts[interrupt];
}
public int GetIrqCount(byte irq)
{
HalPic pic = CurrentProcessor.pic;
// Only set on native hal
if (pic == null) {
return 0;
}
return interruptCounts[pic.IrqToInterrupt(irq)];
}
public static byte GetMaxIrq()
{
HalPic pic = CurrentProcessor.pic;
// This is not set on halhyper or halwin32
if (pic == null) {
return 0;
}
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 Isa.GetCycleCount(); }
}
//////////////////////////////////////////////////////////////////////
//
//
[NoHeapAllocation]
public static bool SamplingEnabled()
{
return (Kernel.ProfilerBufferSize != 0);
}
internal static void StartSampling()
{
if (SamplingEnabled()) {
IsSamplingEnabled = true;
}
}
[NoHeapAllocation]
internal void NextSampleIsIdle()
{
nextSampleIdle = true;
}
//////////////////////////////////////////////////// External Methods.
//
[NoHeapAllocation]
internal static Processor GetCurrentProcessor()
{
unsafe {
return GetCurrentProcessorContext()->processor;
}
}
[NoHeapAllocation]
[AccessedByRuntime("output to header : called by c code")]
internal static unsafe ThreadContext * GetCurrentThreadContext()
{
unsafe {
return (ThreadContext *) Isa.GetCurrentThread();
}
}
[NoHeapAllocation]
[AccessedByRuntime("output to header : called by c code")]
internal static unsafe ProcessorContext * GetCurrentProcessorContext()
{
unsafe {
return (ProcessorContext *) Isa.GetCurrentCpu();
}
}
[NoHeapAllocation]
internal static Thread GetCurrentThread()
{
unsafe {
return GetCurrentThreadContext()->thread;
}
}
[NoHeapAllocation]
internal static void SetCurrentThreadContext(ref ThreadContext context)
{
unsafe {
fixed (ThreadContext *c = &context) {
Isa.SetCurrentThread(ref c->threadRecord);
}
}
}
///
///
/// Verify if thread currently is running on interrupt stack
///
///
[NoHeapAllocation]
[Inline]
internal bool IsOnInterruptStack(Thread currentThread)
{
return Isa.IsRunningOnInterruptStack;
}
internal bool InInterruptContext
{
[NoHeapAllocation]
get {
return Isa.InInterruptContext;
}
}
[AccessedByRuntime("defined in halforgc.asm")]
[MethodImpl(MethodImplOptions.InternalCall)]
[GCAnnotation(GCOption.GCFRIEND)]
[StackBound(32)]
[NoHeapAllocation]
internal static extern void SwitchToThreadContext(ref ThreadContext oldContext, ref ThreadContext newContext);
private class ResumeThreadCallback : Isa.ICallback
{
internal override UIntPtr Callback(UIntPtr param)
{
unsafe {
ThreadContext* newContext = (ThreadContext *) param;
// Switch our thread context, synchronizing with the dispatcher as necessary.
ProcessorDispatcher.TransferToThreadContext(ref *GetCurrentThreadContext(),
ref *newContext);
// Resume in the new context. Note that this call does not return.
newContext->threadRecord.spill.Resume();
return 0;
}
}
}
[AccessedByRuntime("referenced by halforgc.asm")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
internal static unsafe void SwitchToThreadContextNoGC(ref ThreadContext newContext)
{
// Interrupts should be disabled at this point
VTable.Assert(Processor.InterruptsDisabled());
// Save appears to returns twice: once with true on this thread after
// the save, and once with false when the context is restored.
if (GetCurrentThreadContext()->threadRecord.spill.Save()) {
// Initial return from save; time to swap in the new context.
// Must do this on the interrupt stack, since once we release the
// dispatch lock the saved context is free to run (and we would
// be on the same stack.)
fixed (ThreadContext *c = &newContext) {
// Note that this does not return.
Isa.CallbackOnInterruptStack(resumeThreadCallback, (UIntPtr) c);
}
}
// Saved context will resume here
}
[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);
//////////////////////////////////////////////////////////////////////
//
// 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!
//
[AccessedByRuntime("accessed by C++")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
public static bool DisableLocalPreemption()
{
return DisableInterrupts();
}
[AccessedByRuntime("accessed by C++")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
public static void RestoreLocalPreemption(bool enabled)
{
RestoreInterrupts(enabled);
}
[AccessedByRuntime("accessed by C++")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
public static bool DisableInterrupts()
{
#if CHECK_DISABLE_INTERRUPTS
bool wasDisabled = InterruptsDisabled();
#endif
bool result = Isa.DisableInterrupts();
#if CHECK_DISABLE_INTERRUPTS
if (result && wasDisabled) {
DebugStub.Break();
}
#endif
return result;
}
[AccessedByRuntime("accessed by C++")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
public static void RestoreInterrupts(bool enabled)
{
int i = 0;
try {
#if CHECK_DISABLE_INTERRUPTS
if (!InterruptsDisabled()) {
DebugStub.Break();
}
#endif
i = 1;
if (enabled) {
i = 2;
// Processor flag should be turned off before this call
if (GetCurrentProcessor().InInterruptContext) {
DebugStub.Break();
}
i = 3;
Isa.EnableInterrupts();
}
i = 5;
#if CHECK_DISABLE_INTERRUPTS
if (enabled && InterruptsDisabled()) {
DebugStub.Break();
}
#endif
}
catch(Exception e) {
DebugStub.Break();
}
}
// Use this method for assertions only!
[AccessedByRuntime("accessed by C++")]
[NoHeapAllocation]
[GCAnnotation(GCOption.NOGC)]
public static bool InterruptsDisabled()
{
return Platform.ThePlatform.AreInterruptsDisabled();
}
[NoHeapAllocation]
public unsafe void IncrementInterruptCounts(int interrupt)
{
NumInterrupts++;
NumExceptions++;
interruptCounts[interrupt]++;
if (ProcessorLog != null) {
// ProcessorLog.Log(interrupt);
processorCounter.Buffer[interrupt].Hits += 1;
}
}
[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);
}
[NoHeapAllocation]
internal void ActivateDispatcher()
{
if (Platform.Cpu(processorIndex) != null){
// Wake processor
Platform.ThePlatform.WakeNow(processorIndex);
}
}
///
///
/// Set next alarm: timer that we are interested in
///
///
/// Time until the next time we would like to be awaken
///
[NoHeapAllocation]
public void SetNextTimerInterrupt(TimeSpan delta)
{
// Make sure that interrupts are disabled
bool iflag = Processor.DisableInterrupts();
TimeSpan start = delta;
if (delta < timer.MinInterruptInterval) {
delta = timer.MinInterruptInterval;
}
if (delta > timer.MaxInterruptInterval) {
delta = timer.MaxInterruptInterval;
}
#if false
DebugStub.WriteLine("-- SetNextTimerInterrupt(delta={0}, start={1} [min={2},max={3})",
__arglist(delta.Ticks,
start.Ticks,
timer.MinInterruptInterval.Ticks,
timer.MaxInterruptInterval.Ticks));
#endif
timer.SetNextInterrupt(delta);
// Restore interrupts if necessary
Processor.RestoreInterrupts(iflag);
}
//////////////////////////////////////////////////////////////////////
//
// These (native) methods manipulate the local processor's paging
// hardware. They can be used even before Processor.Initialize()
// has been called.
//
internal static void EnablePaging(AddressSpace bootstrapSpace)
{
Isa.EnablePaging((uint)bootstrapSpace.PdptPage.Value);
}
internal static void ChangeAddressSpace(AddressSpace space)
{
Isa.ChangePageTableRoot((uint)space.PdptPage.Value);
}
internal static void InvalidateTLBEntry(UIntPtr pageAddr)
{
DebugStub.Assert(MemoryManager.IsPageAligned(pageAddr));
Isa.InvalidateTLBEntry(pageAddr);
}
internal static AddressSpace GetCurrentAddressSpace()
{
return new AddressSpace(new PhysicalAddress(Isa.GetPageTableRoot()));
}
//
public static HalMemory.ProcessorAffinity[] GetProcessorAffinity()
{
HalMemory.ProcessorAffinity[] processors =
halMemory.GetProcessorAffinity();
return processors;
}
public static HalMemory.MemoryAffinity[] GetMemoryAffinity()
{
HalMemory.MemoryAffinity[] memories =
halMemory.GetMemoryAffinity();
return memories;
}
public static unsafe void EnableMoreProcessors(int cpus)
{
ExpectedProcessors = cpus;
Platform.ThePlatform.EnableMoreCpus(cpus);
StartApProcessors(cpus);
}
/// Start application processors.
[System.Diagnostics.Conditional("SINGULARITY_MP")]
public static void StartApProcessors(int cpuCount)
{
// At this point only the BSP is running.
Tracing.Log(Tracing.Debug, "Processor.StartApProcessors()");
Platform.StartApProcessors(cpuCount);
// At this point the BSP and APs are running.
}
/// Stop application processors.
[NoHeapAllocation]
[System.Diagnostics.Conditional("SINGULARITY_MP")]
public static void StopApProcessors()
{
//
// Note: This should go into a HAL interface and this
// code confined to Platform.cs
//
// At this point the BSP and APs are running.
Tracing.Log(Tracing.Debug, "Processor.StopApProcessors()");
if (Processor.GetRunningProcessorCount() > 1) {
//
// This stops them in MpExecution in a halt state with
// interrupts off.
//
Platform.BroadcastFixedIPI((byte)Isal.IX.EVectors.HaltApProcessors, true);
}
while (GetRunningProcessorCount() != 1) {
// Thread.Sleep(100); Thread.Sleep needs NoHeapAllocation annotation
Thread.Yield();
}
//
// We must reset the AP Processors since a debug entry
// will generated a NMI which will wake them up from HALT,
// and they may start executing code again while the kernel
// is still shutting down.
//
Platform.ResetApProcessors();
DebugStub.RevertToUniprocessor();
// At this point only the BSP is running.
}
/// Gets the number of processors in use by
/// the system.
[NoHeapAllocation]
public static int GetRunningProcessorCount()
{
return runningCpus;
}
/// Gets the total number of processors known
/// to the system. This includes processors not
/// currently in use by the system.
[NoHeapAllocation]
public static int GetProcessorCount()
{
return Platform.GetProcessorCount();
}
//
//public static int GetDomainCount()
//{
// HalMemory.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 PerProcessorAddressSpaceDisabled()
{
return halMemory.PerProcessorAddressSpaceDisabled();
}
public static bool HasAffinityInfo()
{
HalMemory.ProcessorAffinity[] processors = halMemory.GetProcessorAffinity();
HalMemory.MemoryAffinity[] memories = halMemory.GetMemoryAffinity();
if (processors == null || memories == null) {
return false;
}
return true;
}
// return cpuId if context is not null
[NoHeapAllocation]
public unsafe int ValidProcessorId(int i)
{
if (context != null) {
return context->cpuRecord.id;
}
else {
return -1;
}
}
///
///
/// Is system MP or UP
///
///
public static bool IsUP
{
[NoHeapAllocation]
get
{
return CpuCount == 1;
}
}
/// Count of the number of processors expected to be running
public static int ExpectedProcessors;
/// Global processor table
public static Processor[] processorTable;
/// Processor contexts
internal unsafe ProcessorContext* context;
/// Processor dispatcher
[AccessedByRuntime("referenced from c++")]
internal ProcessorDispatcher dispatcher;
// Hardware pic, or its emulation
private HalPic pic;
/// Per processor HalTimer
internal HalTimer timer = null;
/// Per processor HalClock
internal HalClock clock = null;
/// Hal memory interface
private static HalMemory halMemory;
/// Id of a timer interrupt
internal byte timerInterrupt;
/// Id of a clock interrupt
internal byte clockInterrupt;
/// Shows if a processor currently in a context of interrupt
private bool inInterruptContext = false;
/// Shows if a processor currently in halt state
private bool halted = false;
/// Processor Index
private int processorIndex;
/// A number of exception occured on this processor
public uint NumExceptions = 0;
/// A number of interrupts occrued on this processor
public uint NumInterrupts = 0;
/// A number of context switches
public uint NumContextSwitches = 0;
/// A interrupt statistics per processor
internal int[] interruptCounts;
/// A number of cycles per second on a given processor
private ulong cyclesPerSecond;
/// A number of active Processors
private static int runningCpus = 0;
/// An initial per processor kernel thread
private Thread kernelThread;
/// Beginning of kernel thread stack
private UIntPtr kernelStackBegin = 0;
/// Limit of kernel thread stack
private UIntPtr kernelStackLimit = 0;
}
}