/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // Note: // Based on IA-PC HPET (High Precision Event Timers) Specification // Revision 1.0, June 2004. // // The HPET code includes unchecked I/O memory operations so // methods can be marked as NoHeapAllocation. CreateDevice() // must verify that there is at least enough memory for the // MainTimer. namespace Microsoft.Singularity.Hal { using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using System; using System.Runtime.CompilerServices; internal struct HpetTimerCaps { internal const uint FsbDelivery = 1u << 15; internal const uint Width64 = 1u << 5; internal const uint PeriodicSupport = 1u << 4; internal const uint Mask = FsbDelivery | Width64 | PeriodicSupport; } internal struct HpetTimerConf { internal const uint LevelTriggered = 1u << 1; internal const uint InterruptEnable = 1u << 2; internal const uint PeriodicMode = 1u << 3; internal const uint Force32Bit = 1u << 8; } [DriverCategory] [Signature("/pnp/PNP0103")] public sealed class HpetResources : DriverCategoryDeclaration { [IoMemoryRange(0, Default = 0, Length = 0x400)] public IoMemoryRange mem1; } internal sealed class Hpet : IDevice { private PnpConfig config; private IoMemory region; private const int MainCounterOffset = 0xf0; internal const int MinRegionBytes = 0xf8; uint periodFs; // femtoseconds uint capabilities; // bits 0-31 at offset 0 internal Hpet(PnpConfig config) { int cpuId; Microsoft.Singularity.Hal.Platform p = Microsoft.Singularity.Hal.Platform.ThePlatform; cpuId = p.ApicId; this.config = config; IoMemoryRange imr = (IoMemoryRange) config.DynamicRanges[0]; this.region = imr.MemoryAtOffset(0, (int) imr.Length.ToUInt32(), Access.ReadWrite); capabilities = region.Read32(0x00); periodFs = region.Read32(0x04); if (cpuId == 0) { DebugStub.Print("new Hpet writing regions\n"); // Disable interrupts on timers for (uint i = 0; i <= MaxCounterIndex; i++) { DisableInterrupt(i); } uint gc = region.Read32(0x10) & ~3u; // Clear legacy bits region.Write32(0x10, gc | 1); // Enable main counter } } private bool MainCounterWorks() { uint first = this.MainCounterValue32; for (int i = 0; i < 50; i++) { if (this.MainCounterValue32 != first) { return true; } } return false; } void IDevice.Initialize() { } void IDevice.Finalize() { } /// Period in femptoseconds (10^-15) internal uint PeriodFs { [NoHeapAllocation] get { return periodFs; } } internal ushort VendorId { [NoHeapAllocation] get { return (ushort) (capabilities >> 16); } } internal uint Width { [NoHeapAllocation] get { return ((capabilities & 0x2000) != 0) ? 64u : 32u; } } internal uint MaxCounterIndex { [NoHeapAllocation] get { return ((capabilities >> 8) & 0xf); } } internal uint AvailableCounters { [NoHeapAllocation] get { return MaxCounterIndex + 1; } } internal uint Revision { [NoHeapAllocation] get { return capabilities & 0xff; } } internal ulong MainCounterValue { [NoHeapAllocation] get { ulong value; uint hi; do { IoResult r; r = region.Read64NoThrow(MainCounterOffset, out value); DebugStub.Assert(IoResult.Success == r); r = region.Read32NoThrow(MainCounterOffset + 4, out hi); DebugStub.Assert(IoResult.Success == r); } while ((uint)(value >> 32) != hi); return value; } } internal uint MainCounterValue32 { [NoHeapAllocation] get { uint value; IoResult r = region.Read32NoThrow(MainCounterOffset, out value); DebugStub.Assert(IoResult.Success == r); return value; } } [NoHeapAllocation] internal void ClearInterrupt(uint timer) { if (timer <= MaxCounterIndex) { uint value; IoResult result = region.Read32NoThrow(0x20, out value); DebugStub.Assert(IoResult.Success == result); value = value & ~(1u << (int)timer); result = region.Write32NoThrow(0x20, value); DebugStub.Assert(IoResult.Success == result); } } [NoHeapAllocation] private int TimerOffset(uint timer) { return 0x100 + 0x20 * (int)timer; } internal void DisableInterrupt(uint timer) { if (timer <= MaxCounterIndex) { uint m = region.Read32(TimerOffset(timer)); m &= ~HpetTimerConf.InterruptEnable; region.Write32(TimerOffset(timer), m); } } internal static IDevice CreateDevice(IoConfig config, string name) { PnpConfig pnpConfig = config as PnpConfig; if (pnpConfig == null) { return null; } IoMemoryRange imr = config.DynamicRanges[0] as IoMemoryRange; if (imr == null) { return null; } int imrBytes = (int)imr.Length.ToUInt32(); if (imrBytes < Hpet.MinRegionBytes) { DebugStub.Write( "HPET failed as region too small ({0} bytes).\n", __arglist(imrBytes)); return null; } Hpet hpet = new Hpet(pnpConfig); if (hpet.MainCounterWorks()) { HalDevicesApic.SwitchToHpetClock(hpet); } else { DebugStub.Print("WARNING: HPET main counter does not work!\n"); } return hpet; } } }