singrdk/base/Kernel/Singularity.Hal.ApicPC/HalDevices.cs

570 lines
20 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: HalDevices.cs
//
// Note:
//
using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Hal.Acpi;
namespace Microsoft.Singularity.Hal
{
// Lockstep states for MP initialization. The bootstrap
// processor (BSP) sets states beginning Bsp and the
// upcoming Application Processor (AP) sets states beginning
// with Ap.
internal enum MpSyncState : int
{
BspWaitingForAp = 0,
ApOnline = 1,
BspWaitingForApCalibration = 2,
ApCalibrationDone = 3,
BspStartingTscCorrelation = 4,
ApTscCorrelationDone = 5,
BspWaitingApRunning = 6,
ApRunning = 7
}
[CLSCompliant(false)]
public sealed class HalDevices
{
private const byte PicBaseVector = 0x60;
private const byte ApicBaseVector = 0x70;
private static IoApic [] ioApics;
private static HalPic halPic; // dumb wrapper for APIC for kernel
private static Apic apic;
private static Pic pic; // for squelching pic interrupts
private static HalTimer halTimer; // wrapper for APIC timer for kernel
private static ApicTimer apicTimer;
private static PMTimer pmTimer;
private static Timer8254 stallTimer;
private static RTClock rtClock;
private static HalClock halClock;
private static HalMemory halMemory;
private static int processorCount;
private static volatile MpSyncState mpSyncState;
// TSC skew calculation variables
private const int TscCalibrationRounds = 20;
private static volatile uint tsc0Lo;
private static volatile uint tsc0Hi;
private static ulong cpu0CyclesPerSecond;
// Kernel thread stack variables
private static Thread nextKernelThread;
private static UIntPtr nextKernelStackBegin;
private static UIntPtr nextKernelStackLimit;
public static void InitializeBsp(Processor rootProcessor)
{
DebugStub.Print("HalDevices.Initialize()\n");
AcpiTables.Parse();
pmTimer = AcpiTables.GetPMTimer();
// Get PIC resources. Pic is masked by default.
PnpConfig picConfig
= (PnpConfig)IoSystem.YieldResources("/pnp/PNP0000", typeof(Pic));
pic = new Pic(picConfig);
pic.Initialize(PicBaseVector);
// Parse MP Table and create IO apics
MpResources.ParseMpTables();
ioApics = IoApic.CreateIOApics();
apic = new Apic(ioApics);
apic.Initialize(ApicBaseVector);
halPic = new HalPic(apic);
// Apic timer is used to provide one-shot timer interrupts.
apicTimer = new ApicTimer(apic);
apicTimer.Initialize();
halTimer = new HalTimer(apicTimer);
// Calibrate timers
Calibrate.CpuCycleCounter(pmTimer);
Calibrate.ApicTimer(pmTimer, apicTimer);
// Store BSPs clock rate
cpu0CyclesPerSecond = Processor.CyclesPerSecond;
// Legacy timer is used to time stalls when starting CPUs.
PnpConfig i8254Config
= (PnpConfig)IoSystem.YieldResources("/pnp/PNP0100", typeof(Timer8254));
stallTimer = new Timer8254(i8254Config);
// Real-time clock
PnpConfig rtClockConfig
= (PnpConfig)IoSystem.YieldResources("/pnp/PNP0B00", typeof(RTClock));
rtClock = new RTClock(rtClockConfig, apic);
// Compose HalClock
halClock = new HalClock(apic, rtClock, new PMClock(pmTimer));
SystemClock.Initialize(halClock, TimeSpan.FromHours(8).Ticks);
rootProcessor.AddPic(halPic);
rootProcessor.AddTimer(halTimer.Interrupt, halTimer);
rootProcessor.AddClock(halClock.Interrupt, halClock);
InitializeProcessorCount();
DebugReportProcessors();
// ------------------------------------------------
// haryadi: add Srat tables to the Processor
halMemory = new HalMemory(AcpiTables.GetSrat());
Processor.AddHalMemory(halMemory);
Processor.SetIdtTable();
apicTimer.Start();
// Get the screen resources. Since we have metadata above to
// declare all fixed resources used by the screen,
// YieldResources("") will keep the resource tracking correct:
Console.Screen =
new HalScreen(IoSystem.YieldResources("", typeof(HalScreen)));
apic.DumpState();
foreach (IoApic ioApic in ioApics) {
ioApic.DumpRedirectionEntries();
}
pic.DumpRegisters();
}
public static void InitializeAp(Processor processor)
{
Processor.SetIdtTable();
Thread.BindKernelThread(nextKernelThread,
nextKernelStackBegin,
nextKernelStackLimit);
// Fleeting check that allocator works.
Object o = new Object();
if (o == null)
DebugStub.Break();
apic.Initialize();
apicTimer.Initialize();
processor.AddPic(halPic);
processor.AddTimer(halTimer.Interrupt, halTimer);
processor.AddClock(halClock.Interrupt, halClock);
SetMpSyncState(MpSyncState.ApOnline);
WaitForMpSyncState(MpSyncState.BspWaitingForApCalibration);
// Calibrate timers
ulong t1 = Processor.CycleCount;
Calibrate.CpuCycleCounter(pmTimer);
Calibrate.ApicTimer(pmTimer, apicTimer);
ulong t2 = Processor.CycleCount;
Tracing.Log(Tracing.Audit, "Calibration time {0}",
new UIntPtr((uint)(t2 - t1)));
SetMpSyncState(MpSyncState.ApCalibrationDone);
// Calculate tsc skew
long [] skewEstimates = new long[TscCalibrationRounds];
for (int i = 0; i < TscCalibrationRounds; i++) {
ApSync(out skewEstimates[i]);
}
bool success = false;
WaitForMpSyncState(MpSyncState.BspWaitingApRunning, 100000,
out success);
SetMpSyncState(MpSyncState.ApRunning);
// Sort skew estimates and picked median
Array.Sort(skewEstimates);
Tracing.Log(Tracing.Audit, "Skew estimate {0}",
new UIntPtr((int)(skewEstimates[TscCalibrationRounds / 2])));
Tracing.SetTscOffset((skewEstimates[TscCalibrationRounds / 2]));
apicTimer.Start();
}
public static void Initialize(Processor processor)
{
if (processor.Id == 0) {
InitializeBsp(processor);
IoSystem.RegisterKernelDriver(
typeof(HpetResources),
new IoDeviceCreate(Hpet.CreateDevice)
);
}
else {
InitializeAp(processor);
}
}
public static void Finalize()
{
rtClock.Finalize();
rtClock = null;
apicTimer.Finalize();
apicTimer = null;
apic.Finalize();
apic = null;
pic.Finalize();
pic = null;
}
[NoHeapAllocation]
public static bool InternalInterrupt(byte interrupt)
{
if (interrupt >= PicBaseVector && interrupt < ApicBaseVector) {
// Spurious PIC interrupt.
// Appears to happen once on test boards even though PIC
// is masked out and no interrupt was raised before masking.
pic.DumpRegisters();
pic.AckIrq((byte) (interrupt - PicBaseVector));
return true;
}
return false;
}
[NoHeapAllocation]
internal static void StallProcessor(uint microseconds)
{
ulong todo = microseconds *
(Processor.CyclesPerSecond / 1000000);
ulong last = Processor.CycleCount;
ulong elapsed = 0;
while (elapsed < todo) {
// Keep track of how much time has elapsed.
ulong now = Processor.CycleCount;
elapsed += (now - last);
last = now;
}
}
internal static void SwitchToHpetClock(Hpet hpet)
{
DebugStub.Print("Switching to HPET clock");
halClock.SwitchToHpetClock(
new HpetClock(hpet, (ulong)halClock.GetKernelTicks() + 1000u)
);
}
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void SetMpSyncState(MpSyncState newState)
{
Tracing.Log(Tracing.Debug, "Changing MP sync state {0} -> {1}",
(uint)mpSyncState, (uint)newState);
mpSyncState = newState;
}
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void WaitForMpSyncState(MpSyncState newState,
uint microseconds,
out bool success)
{
Tracing.Log(
Tracing.Debug,
"Waiting for MP sync state {0} -> {1} ({2} microseconds)",
(uint)mpSyncState, (uint)newState, microseconds
);
ulong todo = microseconds * (Processor.CyclesPerSecond / 1000000);
ulong last = Processor.CycleCount;
ulong elapsed = 0;
success = false;
while (elapsed < todo) {
ulong now = Processor.CycleCount;
elapsed += (now - last);
last = now;
if (HalDevices.mpSyncState == newState) {
success = true;
return;
}
}
Tracing.Log(Tracing.Debug, "Mp sync state timed out");
}
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void WaitForMpSyncState(MpSyncState newState)
{
Tracing.Log(
Tracing.Debug,
"Waiting for MP sync state {0} -> {1} (indefinite)",
(uint)mpSyncState, (uint)newState
);
while (HalDevices.mpSyncState != newState)
;
}
public static byte GetMaximumIrq()
{
return apic.MaximumIrq;
}
//
// Adding and removing interrupts from the Pic.
//
[NoHeapAllocation]
public static void EnableIoInterrupt(byte irq)
{
apic.EnableIrq(irq);
}
[NoHeapAllocation]
public static void DisableIoInterrupt(byte irq)
{
apic.DisableIrq(irq);
}
private static void InitializeProcessorCount()
{
Madt madt = AcpiTables.GetMadt();
if (madt != null) {
processorCount = madt.GetLocalApics().Count;
}
else if (MpResources.ProcessorEntries != null) {
processorCount = MpResources.ProcessorEntries.Count;
}
else {
processorCount = 1;
}
}
[NoHeapAllocation]
private static int GetProcessorCount()
{
return processorCount;
}
private static void DebugReportProcessors()
{
#if SINGULARITY_MP
string kernelType = "Multi";
#else
string kernelType = "Uni";
#endif
DebugStub.Print("{0}processor kernel on {1} processor system.\n",
__arglist(kernelType, GetProcessorCount()));
}
// Runs Bootstrap processor component of TSC offset computation.
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void BspSync(uint microseconds)
{
ulong todo = microseconds * (Processor.CyclesPerSecond / 1000000);
ulong last = Processor.CycleCount;
ulong elapsed = 0;
SetMpSyncState(MpSyncState.BspStartingTscCorrelation);
elapsed = 0;
while (elapsed < todo &&
mpSyncState != MpSyncState.ApTscCorrelationDone) {
// Keep track of how much time has elapsed.
ulong now = Processor.CycleCount;
elapsed += (now - last);
last = now;
// Order of these assignments is important
tsc0Lo = (uint)(now & 0xffffffff);
tsc0Hi = (uint)(now >> 32);
// Yield hardware thread on HT boxes
Thread.NativeNoOp();
}
}
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void ApSync(out long tscOffset)
{
WaitForMpSyncState(MpSyncState.BspStartingTscCorrelation);
ulong now = 0;
uint tscLo;
uint tscHi;
uint tscTmp;
for (int i = 0; i < 5; i++) {
do {
now = Processor.CycleCount;
tscLo = tsc0Lo;
tscHi = tsc0Hi;
tscTmp = tsc0Lo;
// Yield hardware thread on HT boxes
Thread.NativeNoOp();
} while (tscTmp != tscLo);
}
ulong tsc0 = (((ulong)tsc0Hi) << 32) + tsc0Lo;
if (tsc0 > now) {
Tracing.Log(Tracing.Audit, "Skew tsc-{0}",
new UIntPtr(tsc0 - now));
}
else {
Tracing.Log(Tracing.Audit, "Skew tsc+{0}",
new UIntPtr(now - tsc0));
}
tscOffset = (long)(now - tsc0);
SetMpSyncState(MpSyncState.ApTscCorrelationDone);
}
[Conditional("SINGULARITY_MP")]
[NoHeapAllocation]
private static void AnnounceApFail(int nextCpu)
{
MpSyncState localMpState = mpSyncState;
DebugStub.Print("MpState {0}", __arglist(localMpState));
DebugStub.Break();
BootInfo bi = BootInfo.GetBootInfo();
DebugStub.Print("Cpu {0} failed. ",
__arglist(nextCpu));
DebugStub.Print("GotBack-> Status {0} Count {1:x8}\n",
__arglist(bi.MpStatus32, bi.MpCpuCount));
}
[Conditional("SINGULARITY_MP")]
// [NoHeapAllocation]
private static void StartApProcessorsInternal()
{
// Currently we fire up all processors. They
// serialize on a spin lock in low memory.
int expectedCpus = GetProcessorCount();
int nextCpu = 1;
bool en = Processor.DisableInterrupts();
try {
do {
DebugStub.Print("Starting CPU {0} / {1}\n",
__arglist(nextCpu + 1,
expectedCpus));
SetMpSyncState(MpSyncState.BspWaitingForAp);
Tracing.Log(Tracing.Audit,
"Initializing cpu {0}", (uint)nextCpu);
nextKernelThread = Thread.PrepareKernelThread();
MpBootInfo.PrepareForCpuStart(nextCpu);
MpBootInfo mbi = MpBootInfo.GetMpBootInfo();
nextKernelStackBegin = mbi.KernelStackBegin;
nextKernelStackLimit = mbi.KernelStackLimit;
Tracing.Log(Tracing.Audit,
"Starting cpu {0}. Stack start {1:x8} base {2:x8} limit {3:x8})\n",
new UIntPtr(nextCpu),
mbi.KernelStackBegin,
mbi.KernelStack,
mbi.KernelStackLimit);
if (nextCpu == 1) {
BootInfo bi = BootInfo.GetBootInfo();
apic.BroadcastStartupIPI((byte) (bi.MpEnter32 >> 12));
}
bool success = false;
WaitForMpSyncState(MpSyncState.ApOnline, 500000,
out success);
if (!success) {
AnnounceApFail(nextCpu);
continue;
}
SetMpSyncState(MpSyncState.BspWaitingForApCalibration);
WaitForMpSyncState(MpSyncState.ApCalibrationDone, 1000000,
out success);
if (!success) {
AnnounceApFail(nextCpu);
continue;
}
for (int i = 0; i < TscCalibrationRounds; i++) {
BspSync(100000);
}
SetMpSyncState(MpSyncState.BspWaitingApRunning);
WaitForMpSyncState(MpSyncState.ApRunning, 100000,
out success);
if (!success) {
AnnounceApFail(nextCpu);
continue;
}
Processor.RestoreInterrupts(en);
en = false;
DebugStub.Print("Cpu {0} running ({1} remaining).\n",
__arglist(nextCpu,
expectedCpus - nextCpu - 1));
} while (++nextCpu < expectedCpus);
DebugStub.Print("Done.");
}
finally {
DebugStub.Print("Reached finally block in HalDevices.StartApProcessorsInternal()");
Processor.RestoreInterrupts(en);
}
}
// [NoHeapAllocation]
public static void StartApProcessors()
{
if (GetProcessorCount() > 1) {
StartApProcessorsInternal();
}
}
[NoHeapAllocation]
public static void FreezeProcessors()
{
apic.BroadcastFreezeIPI();
}
[NoHeapAllocation]
public static void SendFixedIPI(byte vector, int from, int to)
{
apic.SendFixedIPI(vector, from, to);
}
[NoHeapAllocation]
public static void BroadcastFixedIPI(byte vector, bool includeSelf)
{
apic.BroadcastFixedIPI(vector, includeSelf);
}
[NoHeapAllocation]
public static void ClearFixedIPI()
{
apic.ClearFixedIPI();
}
}
}