950 lines
31 KiB
C#
950 lines
31 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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();
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Initialize dispatcher
|
|
/// </summary>
|
|
///
|
|
/// <param name="processorId">Id of the processor dispatcher belongs to</param>
|
|
///
|
|
public static void InitializeDispatcher(int processorId)
|
|
{
|
|
// Create a processor dispatcher
|
|
processorTable[processorId].dispatcher = new ProcessorDispatcher();
|
|
|
|
// Initialize dispatcher
|
|
processorTable[processorId].dispatcher.Initialize(processorTable[processorId]);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Activate Timer
|
|
/// </summary>
|
|
///
|
|
///
|
|
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(); }
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Retrieve current dispatcher
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Verify if thread currently is running on interrupt stack
|
|
/// </summary>
|
|
///
|
|
[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);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Set next alarm: timer that we are interested in
|
|
/// </summary>
|
|
///
|
|
/// <param name="delta">Time until the next time we would like to be awaken</param>
|
|
///
|
|
[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);
|
|
}
|
|
|
|
/// <summary> Start application processors. </summary>
|
|
[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.
|
|
}
|
|
|
|
/// <summary> Stop application processors. </summary>
|
|
[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.
|
|
}
|
|
|
|
/// <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 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;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Is system MP or UP
|
|
/// </summary>
|
|
///
|
|
public static bool IsUP
|
|
{
|
|
[NoHeapAllocation]
|
|
get
|
|
{
|
|
return CpuCount == 1;
|
|
}
|
|
}
|
|
|
|
///<summary> Count of the number of processors expected to be running </summary>
|
|
public static int ExpectedProcessors;
|
|
|
|
///<summary> Global processor table </summary>
|
|
public static Processor[] processorTable;
|
|
|
|
/// <summary> Processor contexts </summary>
|
|
internal unsafe ProcessorContext* context;
|
|
|
|
///<summary> Processor dispatcher </summary>
|
|
[AccessedByRuntime("referenced from c++")]
|
|
internal ProcessorDispatcher dispatcher;
|
|
|
|
//<summary> Hardware pic, or its emulation </summary>
|
|
private HalPic pic;
|
|
|
|
/// <summary> Per processor HalTimer </summary>
|
|
internal HalTimer timer = null;
|
|
|
|
/// <summary> Per processor HalClock </summary>
|
|
internal HalClock clock = null;
|
|
|
|
/// <summary> Hal memory interface </summary>
|
|
private static HalMemory halMemory;
|
|
|
|
/// <summary> Id of a timer interrupt </summary>
|
|
internal byte timerInterrupt;
|
|
|
|
/// <summary> Id of a clock interrupt</summary>
|
|
internal byte clockInterrupt;
|
|
|
|
/// <summary> Shows if a processor currently in a context of interrupt </summary>
|
|
private bool inInterruptContext = false;
|
|
|
|
/// <summary> Shows if a processor currently in halt state </summary>
|
|
private bool halted = false;
|
|
|
|
///<summary> Processor Index </summary>
|
|
private int processorIndex;
|
|
|
|
/// <summary> A number of exception occured on this processor </summary>
|
|
public uint NumExceptions = 0;
|
|
|
|
/// <summary> A number of interrupts occrued on this processor </summary>
|
|
public uint NumInterrupts = 0;
|
|
|
|
/// <summary> A number of context switches </summary>
|
|
public uint NumContextSwitches = 0;
|
|
|
|
/// <summary> A interrupt statistics per processor </summary>
|
|
internal int[] interruptCounts;
|
|
|
|
/// <summary> A number of cycles per second on a given processor </summary>
|
|
private ulong cyclesPerSecond;
|
|
|
|
/// <summary> A number of active Processors </summary>
|
|
private static int runningCpus = 0;
|
|
|
|
/// <summary> An initial per processor kernel thread </summary>
|
|
private Thread kernelThread;
|
|
|
|
/// <summary>Beginning of kernel thread stack </summary>
|
|
private UIntPtr kernelStackBegin = 0;
|
|
|
|
/// <summary> Limit of kernel thread stack </summary>
|
|
private UIntPtr kernelStackLimit = 0;
|
|
}
|
|
}
|