singrdk/base/Kernel/Singularity/Hal/Cpu.cs

230 lines
8.2 KiB
C#

// ----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ----------------------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity.Isal;
using Microsoft.Singularity.Hal;
using Microsoft.Singularity.Io;
#if ISA_IX
using Microsoft.Singularity.Isal.IX;
#endif
namespace Microsoft.Singularity.Hal
{
//
// Cpu contains per-CPU data structures that HAL sets up.
//
[AccessedByRuntime("referenced in c++")]
[CLSCompliant(false)]
[StructLayout(LayoutKind.Sequential, Pack=4)]
public class Cpu
{
/////////////////////////////////////////////////////////////////////////////////////
// Boot configuration information: this is all filled out by the loader or another CPU
//////////////////////////////////////////////////////////////////////////////////
public Cpu()
{
}
protected Cpu(int id, UIntPtr stackLimit, UIntPtr stackBegin)
{
Size = 0;
Id = id;
KernelStackLimit = stackLimit;
KernelStackBegin = stackBegin;
}
// Size of instance; this is a versioning/consistency check
[AccessedByRuntime("referenced in c++")]
public readonly int Size;
// Cpu index
[AccessedByRuntime("referenced in c++")]
public readonly int Id;
// The HAL is responsible for allocating a page for processor-specific
// data structures. This is the address of that page. (Note it must be paragraph aligned.)
[AccessedByRuntime("referenced in c++")]
public readonly UIntPtr CpuRecordPage;
// The HAL is responsible for allocating the kernel stack. The boundaries
// are reported here.
[AccessedByRuntime("referenced in c++")]
public readonly UIntPtr KernelStackLimit; // lower bounds of stack
[AccessedByRuntime("referenced in c++")]
public readonly UIntPtr KernelStackBegin; // upper bounds of stack
//Is 0 or 1...
[AccessedByRuntime("referenced in c++")]
public readonly int DomainBsp;
///// NaticeCpu
#if ISA_IX
// This is our segment table.
[AccessedByRuntime("referenced in c++")]
DescriptorTable segments;
#endif
internal bool halted;
///////////////////////////////////////////////////////////////////////
// Kernel information: initialized by kernel during early boot
///////////////////////////////////////////////////////////////////////
// Low level initialization (no allocation allowed)
[NoHeapAllocation]
[AccessedByRuntime("referenced in c++")]
public unsafe void Initialize()
{
if (DomainBsp == 0) {
Platform.ThePlatform.RegisterCpu(this);
}
halted = false;
}
// High level intialization (requires object allocation)
public void InitializeServices()
{
}
// Called from the ISA Target upon interrupt dispatch.
[NoHeapAllocation]
public unsafe void DispatchInterrupt(InterruptContext *context)
{
Processor p = Processor.CurrentProcessor;
int interrupt = context->ExceptionId;
// Indicate that we are in an interrupt context.
Thread target = null;
Thread current = Processor.GetCurrentThread();
Kernel.Waypoint(801);
// Don't generate loads of output for debugger-related interrupts
#if DEBUG_INTERRUPTS
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
context->Display();
#endif
if (Processor.IsSamplingEnabled) {
if (p.nextSampleIdle == false) {
p.Profiler.LogStackTrace(context->InstructionPointer,
context->StackPointer);
}
p.nextSampleIdle = false;
}
if (halted) {
p.clock.CpuResumeFromHaltEvent();
halted = false;
}
unchecked {
if (interrupt != p.clockInterrupt &&
interrupt != p.timerInterrupt) {
// We don't log the clockInterrupt because of all the spew.
Tracing.Log(Tracing.Debug, "Interrupt 0x{0:x}, count={1:x}, eip={2:x} [CC={3:x8}]",
(UIntPtr)(uint)interrupt,
(UIntPtr)p.interruptCounts[interrupt],
(UIntPtr)context->InstructionPointer,
(UIntPtr)(uint)Processor.CycleCount);
}
}
Monitoring.Log(Monitoring.Provider.Processor,
(ushort)ProcessorEvent.Interrupt, 0,
(uint)interrupt, 0, 0, 0, 0);
if (interrupt == Kernel.HalIpiInterrupt) {
#if DEBUG_IPI
DebugStub.WriteLine("IPI received 0x{0:x2} on processor {1}",
__arglist(interrupt, p.Id));
#endif // DEBUG_DISPATCH_TIMER
Platform.ClearFixedIPI(interrupt);
//p.dispatcher.HandlePreemptionReschedule();
}
else if (interrupt == p.timerInterrupt) {
// Polling on every timer interrupt is EXCEEDINGLY costly
p.timer.ClearInterrupt();
p.dispatcher.HandlePreemptionReschedule(p.timer);
}
else if (interrupt == p.clockInterrupt) {
p.clock.ClearInterrupt();
#if DEBUG
// Check for a debug break.
if (DebugStub.PollForBreak()) {
DebugStub.WriteLine("Debugger ctrl-break after interrupt 0x{0:x2}",
__arglist(interrupt));
DebugStub.Break();
}
#endif // DEBUG
}
#if ISA_IX
else if (interrupt == EVectors.GCSynchronization) {
Platform.ClearFixedIPI(interrupt);
MpExecution.GCSynchronizationInterrupt();
}
else if (interrupt == EVectors.SpuriousInterrupt) {
// FIXME: identify the source of these isolated interrupts after the
// warmboot. Ignore them for now.
DebugStub.WriteLine("Spurious interrupt");
}
#endif // ISA_IX
else {
if (!Platform.InternalInterrupt((byte)interrupt)) {
HalPic pic = p.GetPic();
DebugStub.Assert(pic != null);
pic.ClearInterrupt((byte)interrupt);
IoIrq.SignalInterrupt(pic.InterruptToIrq((byte)interrupt));
#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
p.dispatcher.HandleIOReschedule();
}
else {
// Potentially missed interrupt
DebugStub.Break();
}
}
#if DEBUG_INTERRUPTS
DebugStub.WriteLine("(2nd)Int{0:x2}", __arglist(interrupt));
context->Display();
if (!Processor.InterruptsDisabled()) {
DebugStub.WriteLine(" interrupts enabled!!!!!!!");
DebugStub.Break();
}
#if DEBUG_DEEPER
DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt));
Thread.DisplayAbbrev(ref context, " int end");
#endif
#endif
// Now swap in the resulting current thread. (Note that this call will not return.)
Isa.GetCurrentThread()->spill.Resume();
}
}
}