/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: HalClock.cs // // // Note: // // This file is an implementation of Interfaces/Hal/HalClock.csi // // Having some form of time working when one or processors may be in the // debugger makes this hard and expensive. // #if SINGULARITY_MP #error "This file is not for MP builds." #endif // SINGULARITY_MP namespace Microsoft.Singularity.Hal { using Microsoft.Singularity.Hal.Acpi; using System; using System.Diagnostics; using System.Runtime.CompilerServices; public class HalClock { Apic apic; PMClock pmClock; RTClock rtClock; HpetClock hpetClock; long lastKernelTicks; ulong tscSnapshot; bool tscSnapshotValid = false; long ticksSnapshot; int tickScale = 0; const int tickRoll = 24; internal HalClock(Apic apic, RTClock rtClock, PMClock /* ! */ pmClock) { this.apic = apic; this.rtClock = rtClock; this.pmClock = pmClock; this.hpetClock = null; this.tscSnapshot = 0; this.ticksSnapshot = 0; int ticksPerKernelTick = (int)(Processor.CyclesPerSecond / 10000000); this.tickScale = (1 << tickRoll) / ticksPerKernelTick; lastKernelTicks = InternalGetKernelTicks(); } [NoHeapAllocation] public long GetKernelTicks() { bool en = Processor.DisableInterrupts(); try { long kernelTicks = InternalGetKernelTicks(); if (kernelTicks >= lastKernelTicks || (kernelTicks < 0 && lastKernelTicks > 0)) { lastKernelTicks = kernelTicks; return kernelTicks; } else { // Skew from switching between tsc and underlying clock DebugStub.Assert(lastKernelTicks - kernelTicks < 5000000); return lastKernelTicks; } } finally { Processor.RestoreInterrupts(en); } } [NoHeapAllocation] private long InternalGetKernelTicks() { if (!tscSnapshotValid) { if (hpetClock == null) { ticksSnapshot = (long) pmClock.GetKernelTicks(); } else { ticksSnapshot = (long) hpetClock.GetKernelTicks(); } tscSnapshot = Processor.CycleCount; tscSnapshotValid = true; return ticksSnapshot; } else { long tscDelta = (long)(Processor.CycleCount - tscSnapshot); long tickDelta = (tscDelta * tickScale) >> tickRoll; return ticksSnapshot + tickDelta; } } public byte Interrupt { [NoHeapAllocation] get { return rtClock.Interrupt; } } [NoHeapAllocation] public void ClearInterrupt() { if (hpetClock == null) { pmClock.Update(); } else { hpetClock.Update(); } rtClock.ClearInterrupt(); tscSnapshotValid = false; } internal void SwitchToHpetClock(HpetClock hc) { // Change rt clock interrupt frequency to appropriate // rate for HPET main clock. rtClock.SetFrequency(HpetClock.UpdateFrequency(hc.Hpet)); hpetClock = hc; DebugStub.Print("Hal switching to HpetClock.\n"); } [NoHeapAllocation] public void CpuResumeFromHaltEvent() { tscSnapshotValid = false; } [NoHeapAllocation] public long GetRtcTime() { return rtClock.GetBootTime() + GetKernelTicks(); } public void SetRtcTime(long newRtcTime) { rtClock.SetRtcTime(newRtcTime, GetKernelTicks()); } } }