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

705 lines
26 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Apic.cs
//
//
// Note:
// Intel MP Specification 1.4
// IA-32 Intel Architecture Software Developers Manual
// Volume 3: Systems Programming Guide
//
// Caution:
// This code has not been tested on multi I/O apic systems
// and the handling for this case is probably not sane.
namespace Microsoft.Singularity.Hal
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.Singularity.Io;
[CLSCompliant(false)]
internal struct ApicOffset
{
internal const uint Id = 0x0020;
internal const uint Version = 0x0030;
internal const uint TaskPriority = 0x0080;
internal const uint ArbitrationPriority = 0x0090;
internal const uint ProcessorPriority = 0x00a0;
internal const uint EoiRegister = 0x00b0;
internal const uint LocalDestination = 0x00d0;
internal const uint DestinationFormat = 0x00e0;
internal const uint SpuriousIntVector = 0x00f0;
internal const uint IsrBase = 0x0100;
internal const uint TmrBase = 0x0180;
internal const uint IrrBase = 0x0200;
internal const uint ErrorStatus = 0x0280;
internal const uint IcrLo = 0x0300;
internal const uint IcrHi = 0x0310;
internal const uint LvtTimer = 0x0320;
internal const uint LvtThermalSensor = 0x0330;
internal const uint LvtPerfCounts = 0x0340;
internal const uint LvtLint0 = 0x0350;
internal const uint LvtLint1 = 0x0360;
internal const uint LvtError = 0x0370;
internal const uint TimerInitialCount = 0x0380;
internal const uint TimerCurrentCount = 0x0390;
internal const uint TimerDivideConf = 0x03e0;
internal const uint Max = 0x0400;
};
[CLSCompliant(false)]
internal struct LvtFlags
{
internal const uint TimerBit = 1 << 17;
internal const uint Periodic = TimerBit;
internal const uint OneShot = 0;
internal const uint MaskedBit = 1 << 16;
internal const uint Masked = MaskedBit;
internal const uint Unmasked = 0;
internal const uint TriggerBit = 1 << 15;
internal const uint Level = TriggerBit;
internal const uint Edge = 0;
internal const uint RIRRBit = 1 << 14;
internal const uint PolarityBit = 1 << 13;
internal const uint ActiveLow = PolarityBit;
internal const uint ActiveHigh = 0;
internal const uint StatusBit = 1 << 12;
internal const uint Pending = StatusBit;
internal const uint Idle = 0;
internal const uint DeliveryBits = 7 << 8;
internal const uint Fixed = 0 << 8;
internal const uint SMI = 2 << 8;
internal const uint NMI = 4 << 8;
internal const uint INIT = 5 << 8;
internal const uint ExtInt = 7 << 8;
}
[CLSCompliant(false)]
internal struct IcrFlags
{
internal const uint ShorthandBits = 3 << 18;
internal const uint NoShortHand = 0 << 18;
internal const uint Self = 1 << 18;
internal const uint All = 2 << 18;
internal const uint AllBarSelf = 3 << 18;
internal const uint TriggerBit = 1 << 15;
internal const uint Edge = 0;
internal const uint Level = TriggerBit;
internal const uint LevelBit = 1 << 14;
internal const uint DeAssert = 0;
internal const uint Assert = LevelBit;
internal const uint StatusBit = 1 << 12;
internal const uint Idle = 0;
internal const uint Pending = StatusBit;
internal const uint ModeBit = 1 << 11;
internal const uint Physical = 0;
internal const uint Logical = ModeBit;
internal const uint DeliveryBits = 7 << 8;
internal const uint Fixed = 0 << 8;
internal const uint SMI = 2 << 8;
internal const uint NMI = 4 << 8;
internal const uint INIT = 5 << 8;
internal const uint StartUp = 6 << 8;
}
[CLSCompliant(false)]
public class Apic
{
const ushort ImcrAddressPort = 0x22;
const ushort ImcrDataPort = 0x23;
const byte ImcrAddressSelect = 0x70;
const byte ImcrData8259 = 0x00;
const byte ImcrDataApic = 0x01;
const byte ApicMSR = 0x1b;
public const byte RtClockIrq = 8;
public const byte TimerIrq = 0;
static uint baseAddr;
// Table of Apic Ids before changed to cpu-id.
static byte [] initialIdTable;
IoMemory apicRegion;
IoApic [] ioApics;
byte baseVector;
byte maxIrq;
public Apic(IoApic[] ioApics)
{
ulong msr = Processor.ReadMsr(0x1b);
baseAddr = ((uint) msr) & 0xfffff000u;
this.apicRegion =
IoMemory.MapPhysicalMemory(baseAddr,
new UIntPtr(ApicOffset.Max),
true, true);
this.ioApics = ioApics;
DebugStub.Assert(initialIdTable == null); // Check Single Apic instance
initialIdTable = new byte [256];
}
internal void Finalize()
{
}
[NoHeapAllocation]
internal uint Read(uint offset)
{
uint outValue;
IoResult result = apicRegion.Read32NoThrow((int) offset,
out outValue);
DebugStub.Assert(IoResult.Success == result);
return outValue;
}
[NoHeapAllocation]
internal void Write(uint offset, uint value)
{
IoResult result = apicRegion.Write32NoThrow((int) offset, value);
DebugStub.Assert(IoResult.Success == result);
}
internal byte Id
{
[NoHeapAllocation]
get { return (byte)(Read(ApicOffset.Id) >> 24); }
}
[NoHeapAllocation]
private void SetId(byte cpuId)
{
uint v = Read(ApicOffset.Id);
initialIdTable[cpuId] = (byte)(v >> 24);
v = (v & 0xffffff) | (((uint)cpuId) << 24);
Write(ApicOffset.Id, v);
}
private bool IsBsp
{
[NoHeapAllocation]
get { return (Processor.ReadMsr(0x1b) & 0x100) == 0x100; }
}
[NoHeapAllocation]
private IoApic GetIoApic(int id)
{
for (int i = 0; i < ioApics.Length; i++)
{
if (ioApics[i].GetId() == id)
{
return ioApics[i];
}
}
return null;
}
internal void Initialize(byte baseVector)
{
DebugStub.Assert(255 - baseVector >= 48);
this.baseVector = baseVector;
Write(ApicOffset.LvtTimer, LvtFlags.Masked);
Write(ApicOffset.LvtThermalSensor, LvtFlags.Masked);
Write(ApicOffset.LvtPerfCounts, LvtFlags.Masked);
Write(ApicOffset.LvtError, LvtFlags.Masked);
Write(ApicOffset.SpuriousIntVector, 0xdfu);
// haryadi -- set task priority to 0 so that it can
// receive all interrupts from IPI
// Write(ApicOffset.TaskPriority, 0x20u);
Write(ApicOffset.TaskPriority, 0);
Write(ApicOffset.LvtLint0, LvtFlags.Masked);
Write(ApicOffset.LvtLint1, LvtFlags.Masked);
SetId((byte) Processor.GetCurrentProcessorId());
if (this.IsBsp) {
InitializeRouteableEntries();
InitializeMpResourceEntries();
}
// Enable in s/w
SetEnabled(true);
// Enable in h/w
Processor.WriteMsr(ApicMSR, Processor.ReadMsr(ApicMSR) | (1 << 11));
// Watch out for the uniprocessor case where the
// FloatingPointer may be null.
MpFloatingPointer floatingPointer = MpResources.FloatingPointer;
if (floatingPointer != null && floatingPointer.ImcrPresent && this.IsBsp) {
IoPort addrPort = new IoPort(ImcrAddressPort, 1, Access.Write);
IoPort dataPort = new IoPort(ImcrDataPort, 1, Access.Write);
addrPort.Write8(ImcrAddressSelect);
dataPort.Write8(ImcrDataApic);
}
Write(ApicOffset.LvtLint0, LvtFlags.Level | LvtFlags.ExtInt);
Write(ApicOffset.LvtLint1, LvtFlags.NMI | 0x02);
for (int z = 0; z <= maxIrq; z++) {
byte m = IrqToInterrupt((byte) z);
byte n = InterruptToIrq(m);
DebugStub.Assert((byte) z == n);
}
}
internal void Initialize()
{
Initialize(0x70);
}
private void InitializeRouteableEntries()
{
// Initialize entries that may not be part of MP set
// configuration, but might be utilizable via a
// programmable interrupt routing on PCI-LPC bridge
// or elsewhere.
byte irq = 16;
IoBits stdPciBits = (IoBits.DstPhysical | IoBits.DelModFixed |
IoBits.IrqMask | IoBits.IntPolActiveLow |
IoBits.TriggerModeLevel);
for (int id = 0; id < ioApics.Length; id++) {
uint start = (id == 0) ? 16u : 0u;
uint end = ioApics[id].RedirectionEntryCount;
for (uint index = start; index < end; index++) {
RedirectionEntry re =
new RedirectionEntry(this.Id, stdPciBits,
IrqToInterrupt(irq));
ioApics[id].SetRedirectionEntry(index, ref re);
irq++;
}
}
maxIrq = (byte) (irq - 1);
}
private void InitializeMpResourceEntries()
{
foreach (MpInterruptEntry e in MpResources.IoInterruptEntries) {
IoApic ioApic = GetIoApic(e.ApicId);
if (ioApic == null) {
DebugStub.Print("Could not find I/O Apic with id {0}\n",
__arglist(e.ApicId));
continue;
}
MpBusEntry be = MpResources.GetBusEntryById(e.BusId);
if (be == null) {
DebugStub.Print("Unknown bus device id {0:x2}\n",
__arglist(e.BusId));
continue;
}
if (be.BusType == BusType.ISA) {
ConfigureIsaInterrupt(ioApic, e);
}
else if (be.BusType == BusType.PCI) {
ConfigurePciInterrupt(ioApic, e);
}
else {
DebugStub.Print("Unhandled device on bus {0:x2}\n",
__arglist(e.BusId));
continue;
}
}
}
internal void ConfigureIsaInterrupt(IoApic ioApic, MpInterruptEntry e)
{
byte vector = IrqToInterrupt(e.BusIrq);
IoBits iobits = (e.PolarityType == Polarity.ActiveLow) ?
IoBits.IntPolActiveLow : IoBits.IntPolActiveHigh;
iobits |= (e.TriggerType == Trigger.Level) ?
IoBits.TriggerModeLevel : IoBits.TriggerModeEdge;
iobits |= IoBits.DstPhysical;
iobits |= IoBits.DelModFixed;
iobits |= IoBits.IrqMask;
RedirectionEntry r = new RedirectionEntry(this.Id, iobits, vector);
ioApic.SetRedirectionEntry(e.ApicLine, ref r);
}
internal void ConfigurePciInterrupt(IoApic ioApic, MpInterruptEntry e)
{
byte vector = IrqToInterrupt(e.ApicLine);
IoBits iobits = (e.PolarityType == Polarity.ActiveHigh) ?
IoBits.IntPolActiveHigh : IoBits.IntPolActiveLow;
iobits |= (e.TriggerType == Trigger.Edge) ?
IoBits.TriggerModeEdge : IoBits.TriggerModeLevel;
iobits |= IoBits.DstPhysical;
iobits |= IoBits.DelModFixed;
iobits |= IoBits.IrqMask;
RedirectionEntry r = new RedirectionEntry(this.Id, iobits, vector);
ioApic.SetRedirectionEntry(e.ApicLine, ref r);
}
[NoHeapAllocation]
public byte InterruptToIrq(byte interrupt)
{
if (interrupt < baseVector) {
return RtClockIrq;
}
if (interrupt == baseVector) {
return TimerIrq;
}
if (interrupt == baseVector + 16) {
return RtClockIrq;
}
return (byte)(interrupt - (baseVector + 32));
}
[NoHeapAllocation]
public byte IrqToInterrupt(byte irq)
{
if (irq == TimerIrq) {
return (byte) (baseVector + 0);
}
if (irq == RtClockIrq) {
return (byte) (baseVector + 16);
}
return (byte) (baseVector + 32 + irq);
}
public byte MaximumIrq
{
[NoHeapAllocation]
get { return maxIrq; }
}
[NoHeapAllocation]
public void SetEnabled(bool enabled)
{
uint v = Read(ApicOffset.SpuriousIntVector);
v &= ~(1u << 8);
if (enabled)
v |= 1u << 8;
Write(ApicOffset.SpuriousIntVector, v);
}
[NoHeapAllocation]
public bool GetEnabled()
{
return (Read(ApicOffset.SpuriousIntVector) & (1u << 8)) != 0;
}
[NoHeapAllocation]
public void AckIrq(byte irq)
{
// XXX should not ack spurious interrupt
Write(ApicOffset.EoiRegister, 0);
}
[NoHeapAllocation]
public void EnableIrq(byte irq)
{
// XXX TODO fix for multiple I/O apics
ioApics[0].SetEntryMask(irq, false);
}
[NoHeapAllocation]
public void DisableIrq(byte irq)
{
// XXX TODO fix for multiple I/O apics
ioApics[0].SetEntryMask(irq, true);
}
[NoHeapAllocation]
public void ClearInterrupt(byte interrupt)
{
byte irq = InterruptToIrq(interrupt);
Tracing.Log(Tracing.Debug, "Clearing interrupt {0} (irq {1})",
(UIntPtr)interrupt, (UIntPtr)irq);
// byte isrInterrupt = (byte) ActiveIsrInterrupt();
if (interrupt >= baseVector) {
// DebugStub.Assert(interrupt == isrInterrupt);
DisableIrq(irq);
}
AckIrq(irq);
}
[NoHeapAllocation]
private bool SendIPI(uint icrHi, uint icrLo)
{
//
// DebugStub.Print is not permitted here. We may be
// invoked from within DebugStub.Trap with the
// non-recursive communication lock held.
//
Tracing.Log(Tracing.Audit,
"Sending IPI {0:x8}{1:x8}\n", icrHi, icrLo);
// Clear ESR
Write(ApicOffset.ErrorStatus, 0);
Write(ApicOffset.ErrorStatus, 0);
// Send IPI
Write(ApicOffset.IcrHi, icrHi);
Write(ApicOffset.IcrLo, icrLo);
do {
Write(ApicOffset.ErrorStatus, 0); // Trigger ESR update
uint esr = Read(ApicOffset.ErrorStatus);
if (esr != 0) {
Tracing.Log(Tracing.Warning,
"APIC SendIPI Error (esr = {0})\n", esr);
return false;
}
} while ((Read(ApicOffset.IcrLo) & IcrFlags.Pending) != 0);
return true;
}
[NoHeapAllocation]
private static void Delay(uint micros)
{
HalDevices.StallProcessor(micros);
}
[NoHeapAllocation]
internal void BroadcastStartupIPI(byte vector)
{
Write(ApicOffset.IcrHi, 0);
// Per AP initialization in IA-32 Vol 3
// Init IPI // ICR.Lo = 00c4500h
SendIPI(0, IcrFlags.AllBarSelf | IcrFlags.Assert | IcrFlags.Pending | IcrFlags.INIT | (uint)vector);
// ...delay 10ms
Delay(10000);
// Startup IPI // ICR.Lo = 00c46XXh
SendIPI(0, IcrFlags.AllBarSelf | IcrFlags.Assert | IcrFlags.Pending | IcrFlags.StartUp | (uint)vector);
// ...delay 200us
Delay(200);
SendIPI(0,
(IcrFlags.AllBarSelf | IcrFlags.Assert | IcrFlags.Pending |
IcrFlags.StartUp | (uint)vector)
);
}
[NoHeapAllocation]
internal void BroadcastFreezeIPI()
{
Write(ApicOffset.IcrHi, 0);
SendIPI(0,
(IcrFlags.AllBarSelf | IcrFlags.Assert |
IcrFlags.Pending | IcrFlags.NMI)
);
}
[NoHeapAllocation]
internal void SendFixedIPI(byte vector, int from, int to)
{
uint icrHi = ((uint)to & 0x0ff) << 24;
uint icrLo =
IcrFlags.NoShortHand | IcrFlags.Assert |
IcrFlags.Pending | IcrFlags.Fixed |
IcrFlags.Physical | (uint)vector;
Write(ApicOffset.IcrHi, 0);
if (!SendIPI(icrHi, icrLo)) {
DebugStub.Break();
}
}
[NoHeapAllocation]
internal void BroadcastFixedIPI(byte vector, bool includeSelf)
{
uint icrHi = 0;
uint icrLo = includeSelf ? IcrFlags.All : IcrFlags.AllBarSelf;
icrLo |= (IcrFlags.Assert | IcrFlags.Pending | IcrFlags.Fixed |
(uint)vector);
Write(ApicOffset.IcrHi, 0);
if (!SendIPI(icrHi, icrLo)) {
DebugStub.Break();
}
}
[NoHeapAllocation]
internal void ClearFixedIPI()
{
Write(ApicOffset.EoiRegister, 0);
}
[NoHeapAllocation]
private bool IrrIsSet(byte interrupt)
{
// Read() is 4 bytes wide.
// Each DWORD is aligned on 16-byte boundaries.
// IsrBase + 0x00 = interrupts 0-31
// IsrBase + 0x10 = interrupts 32-63
// IsrBase + 0x20 = interrupts 64-95
// ...
// IsrBase + 0x70 = interrupts 224-255
uint d = (uint)interrupt / 32u;
uint o = ApicOffset.IrrBase + d * 16u;
uint m = 1u << (int) ((int)interrupt - d * 32);
return (Read(o) & m) == m;
}
[NoHeapAllocation]
private bool IsrIsSet(byte interrupt)
{
// Read() is 4 bytes wide.
// Each DWORD is aligned on 16-byte boundaries.
// IsrBase + 0x00 = interrupts 0-31
// IsrBase + 0x10 = interrupts 32-63
// IsrBase + 0x20 = interrupts 64-95
// ...
// IsrBase + 0x70 = interrupts 224-255
uint d = (uint)interrupt / 32u;
uint o = ApicOffset.IsrBase + d * 16u;
uint m = 1u << (int) ((int)interrupt - d * 32);
return (Read(o) & m) == m;
}
[NoHeapAllocation]
private uint Log2(uint value)
{
uint l = 0;
if ((value & 0xffff0000u) != 0) l += 16u;
if ((value & 0xff00ff00u) != 0) l += 8u;
if ((value & 0xf0f0f0f0u) != 0) l += 4u;
if ((value & 0xccccccccu) != 0) l += 2u;
if ((value & 0xaaaaaaaau) != 0) l += 1u;
return l;
}
private uint lastByte;
private int lastByteValue;
private uint lastIsr;
[NoHeapAllocation]
private uint ActiveIsrInterrupt()
{
uint isr = ~0u;
uint i;
for (i = 0; i < 0x80; i += 0x10) {
uint value = Read(ApicOffset.IsrBase + i);
if (value != 0) {
lastByteValue = (int) value;
lastByte = i;
DebugStub.Assert((value & (value - 1)) == 0);
isr = i * 2 + Log2(value);
lastIsr = isr;
break;
}
}
i += 0x10;
for (; i < 0x80; i += 0x10) {
uint value = Read(ApicOffset.IsrBase + i);
DebugStub.Assert(value == 0);
}
DebugStub.Assert(isr != ~0u);
return isr;
}
[Conditional("DEBUG_APIC")]
[NoHeapAllocation]
internal void DumpState()
{
ulong msr = Processor.ReadMsr(0x1b);
uint baseAddr = ((uint) msr) & 0xfffff000u;
uint en = (((uint) msr) >> 11) & 0x1;
uint bsp = (((uint) msr) >> 8) & 0x1;
DebugStub.Print("Apic Base {0:x8} MSR Enabled {1} BSP {2}\n",
__arglist(baseAddr, en, bsp));
DebugStub.Print("Id {0:x} Version {1:x} TaskPriority {2:x} " +
"ArbitrationPriority {3:x} ProcessorPriority {4:x}\n",
__arglist(
Read(ApicOffset.Id),
Read(ApicOffset.Version),
Read(ApicOffset.TaskPriority),
Read(ApicOffset.ArbitrationPriority),
Read(ApicOffset.ProcessorPriority)));
DebugStub.Print("Timer LVT {0:x8} Thermal LVT {1:x8} " +
"PerfCounts LVT {2:x8}\n",
__arglist(
Read(ApicOffset.LvtTimer),
Read(ApicOffset.LvtThermalSensor),
Read(ApicOffset.LvtPerfCounts)));
DebugStub.Print("Lint0 LVT {0:x8} Lint1 LVT {1:x8}\n",
__arglist(
Read(ApicOffset.LvtLint0),
Read(ApicOffset.LvtLint1)));
DebugStub.Print("LvtError LVT {0:x8} Spurious LVT {1:x8} ESR {2:x2}\n",
__arglist(
Read(ApicOffset.LvtError),
Read(ApicOffset.SpuriousIntVector),
Read(ApicOffset.ErrorStatus) & 0xef));
}
// haryadi -- print IRR and ISR register
#if FALSE
// : This method is commented out because
// it allocates memory. The fix is trivial.
internal void PrintIrrAndIsr(bool debug)
{
uint addr, a;
uint [] c = new uint [8];
uint [] b = new uint [8];
if (debug) {
DebugStub.Print("HSG: IRR ");
for (int i = 0; i < 8; i++) {
addr = ApicOffset.IrrBase+(uint)(i*32);
a = Read(addr);
DebugStub.Print("{0}.{1:x08} ", __arglist(i,a));
}
DebugStub.WriteLine("");
DebugStub.Print("HSG: ISR ");
for (int i = 0; i < 8; i++) {
addr = ApicOffset.IsrBase+(uint)(i*32);
a = Read(addr);
DebugStub.Print("{0}.{1:x08} ", __arglist(i,a));
}
DebugStub.WriteLine("");
}
for (int i = 0; i < 8; i++) {
addr = ApicOffset.IrrBase+(uint)(i*32);
c[i] = Read(addr);
}
for (int i = 0; i < 8; i++) {
addr = ApicOffset.IsrBase+(uint)(i*32);
b[i] = Read(addr);
}
Tracing.Log(Tracing.Debug, "************************************");
Tracing.Log(Tracing.Debug, "IRR lo: {0:x08} {1:x08} {2:x08} {3:x08}",
c[0], c[1], c[2], c[3]);
Tracing.Log(Tracing.Debug, "IRR hi: {0:x08} {1:x08} {2:x08} {3:x08}",
c[4], c[5], c[6], c[7]);
Tracing.Log(Tracing.Debug, "ISR lo: {0:x08} {1:x08} {2:x08} {3:x08}",
b[0], b[1], b[2], b[3]);
Tracing.Log(Tracing.Debug, "ISRb hi: {0:x08} {1:x08} {2:x08} {3:x08}",
b[4], b[5], b[6], b[7]);
}
#endif // FALSE
}
}