852 lines
31 KiB
C#
852 lines
31 KiB
C#
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// Isa specifies a standard architecture-neutral interface to the target execution
|
||
|
// environment required by Singularity. Unlike the HAL there is only a single
|
||
|
// Isa available at a time (although the Isa implementation changes on different ISA's.)
|
||
|
//
|
||
|
// Isa defines a basic framework for interrupt handling and dispatching, which is uniform
|
||
|
// across HALs.
|
||
|
//
|
||
|
// The Isa model is:
|
||
|
//
|
||
|
// Each processor has 2 processor-specific records avaiable to it.
|
||
|
// - The CpuRecord is effectively constant for the processor
|
||
|
// - The ThreadRecord will be changed by the OS as context switches occur on the processor
|
||
|
//
|
||
|
// Pointers to these structures are stored in an architecture-specific, yet well-known,
|
||
|
// atomic instruction sequence. (Specifically, atomic with respect to processor migration
|
||
|
// occuring during the sequence.)
|
||
|
//
|
||
|
// This sequence is allowed to be parameterized by a runtime constant parameter
|
||
|
// "offset" (one for each processor and thread.) Values of each offset are
|
||
|
// provided at init time and may not change. (E.g. on x86 this is an offset from fs:[0].)
|
||
|
//
|
||
|
// The target also abstracts ISA-specific interrupt handling and dispatch logic. Each architecture
|
||
|
// identifies interrupts into one of 3 categories (based on static partitioning of dispatch ids):
|
||
|
// - Exceptions. These cause the current context to be saved in the CpuRecord, and
|
||
|
// are dispatched to the debugger infrastructure. (Eventually they will be further
|
||
|
// handled by additional logic, but this is not in place yet.) Note that exceptions
|
||
|
// are not currently reentrant.
|
||
|
// - "Quick" interrupts. These interrupts are intended to be handled by lightweight kernel
|
||
|
// logic. They cannot trigger context switches and thus do not require full context saves.
|
||
|
// - Normal interrupts. These cause a context save into the per-thread spill area. The
|
||
|
// presumption is that execution may resume on a different thread.
|
||
|
//
|
||
|
// There needs to be some discipline enforced in terms of priority of these interrupts, but
|
||
|
// currently there is none. (The current assumption is that normal/quick interrupts
|
||
|
// are non-reentrant, and exceptions may occur at any time but may not be interrupted. However
|
||
|
// note that nothing is preventing quick interrupts from interrupting anything but debugging.)
|
||
|
//
|
||
|
// The interrupt dispatching logic also manages stack switches. The basic model is that only
|
||
|
// normal thread execution occurs on growable stacks. Therefore we maintain only a single
|
||
|
// interrupt stack for all interrupt and exception dispatch - reentrant interrupts simply
|
||
|
// grow that stack. Note that if we ever overflow the interrupt stack it is a panic situation,
|
||
|
// but this is the case anyway since we cannot allocate during interrupt processing. Also note
|
||
|
// that this stack is also suitable for executing application-level stack growth code, since
|
||
|
// such code cannot run during interrupt time.
|
||
|
|
||
|
using System;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Diagnostics;
|
||
|
#if SINGULARITY_KERNEL
|
||
|
using Microsoft.Singularity.Hal;
|
||
|
using HalPlatform = Microsoft.Singularity.Hal.Platform;
|
||
|
#endif
|
||
|
|
||
|
#if ISA_IX
|
||
|
using Microsoft.Singularity.Isal.IX;
|
||
|
#elif ISA_ARM
|
||
|
using Microsoft.Singularity.Isal.Arm;
|
||
|
#endif
|
||
|
|
||
|
namespace Microsoft.Singularity.Isal
|
||
|
{
|
||
|
[NoCCtor]
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[CLSCompliant(false)]
|
||
|
public class Isa
|
||
|
{
|
||
|
[AccessedByRuntime("foo", AllFields=true)]
|
||
|
public enum Type
|
||
|
{
|
||
|
X86,
|
||
|
X64,
|
||
|
Arm
|
||
|
};
|
||
|
|
||
|
#if ISA_IX86
|
||
|
public const Type Target = Type.X86;
|
||
|
#elif ISA_IX64
|
||
|
public const Type Target = Type.X64;
|
||
|
#elif ISA_ARM
|
||
|
public const Type Target = Type.Arm;
|
||
|
#endif
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Thread-local and cpu-local storage
|
||
|
//
|
||
|
// Each ISA sets up an architecure-specific way to access a processor- and thread-specific
|
||
|
// data segment. To allow flexibility in the implementation of this, the HAL
|
||
|
// is allowed to parameterize the location where this data is stored (although this
|
||
|
// offset is interpreted in an ISA-specific way: fs offset on x86, gs offset on
|
||
|
// x64, etc.) This parameter is generically referred to as the "offset".
|
||
|
//
|
||
|
// Note that for each ISA there is a specific well-known assembly sequence, using
|
||
|
// this offset, to access the thread and processor records. (Architecture-neutral code
|
||
|
// should use the function call interfaces.)
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
public static int currentThreadOffset;
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
public static int currentCpuOffset;
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[CLSCompliant(false)]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
public static unsafe extern ThreadRecord *GetCurrentThread();
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[CLSCompliant(false)]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
public static unsafe extern CpuRecord *GetCurrentCpu();
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[CLSCompliant(false)]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
public static unsafe extern void SetCurrentThread(ref ThreadRecord record);
|
||
|
#endif
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Interrupt stack access
|
||
|
//
|
||
|
// This routine allows managed code to be executed on the interrupt stack.
|
||
|
//
|
||
|
// This can only be safely accomplished with interrupt disabled.
|
||
|
// There are two reasons interrupts are required to be disabled here:
|
||
|
//
|
||
|
// 1. We must disable interrupts around the narrow window where we are flipping the stack
|
||
|
// back and forth from the interrupt stack (essentially this must be atomic or the
|
||
|
// interrupt dispatcher could get confused.)
|
||
|
//
|
||
|
// 2. We cannot tolerate a context switch when we are on the interrupt stack.
|
||
|
//
|
||
|
// If we ever support leveled interrupts, we may want to disable interrupts for a very
|
||
|
// narrow window, and separately disable scheduling operations for the longer window.
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
[AccessedByRuntime("referenced by asm")]
|
||
|
public abstract class ICallback
|
||
|
{
|
||
|
[NoStackLinkCheckTrans]
|
||
|
internal abstract UIntPtr Callback(UIntPtr param);
|
||
|
|
||
|
[AccessedByRuntime("referenced by asm")]
|
||
|
[NoStackLinkCheckTrans]
|
||
|
private static UIntPtr DoCallback(ICallback pThis, UIntPtr param)
|
||
|
{
|
||
|
VTable.Assert(Isa.IsRunningOnInterruptStack);
|
||
|
|
||
|
return pThis.Callback(param);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[NoStackLinkCheckTrans]
|
||
|
[NoHeapAllocation]
|
||
|
public static UIntPtr CallbackOnInterruptStack(ICallback callback, UIntPtr param)
|
||
|
{
|
||
|
VTable.Assert(HalPlatform.ThePlatform.AreInterruptsDisabled());
|
||
|
|
||
|
// Here we assert that we aren't reentering the interrupt stack. This would technically
|
||
|
// be just fine (we'd just call back immediately), but the check is here because:
|
||
|
// (a) existing clients currently don't expect this to happen, so it's a sanity check
|
||
|
// (b) we are participating in an annoying hack where we "discard" the NoHeapAllocation
|
||
|
// attribute on our callback path through unmanaged code, since stack allocation
|
||
|
// calls through here and is not annotated properly. Thus we can't directly call
|
||
|
// the callback here. (@todo: This needs to be fixed.)
|
||
|
|
||
|
VTable.Assert(!IsRunningOnInterruptStack);
|
||
|
|
||
|
return SwitchToInterruptStackAndCallback(callback, param);
|
||
|
}
|
||
|
|
||
|
[AccessedByRuntime("defined in interrupt.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public extern static UIntPtr SwitchToInterruptStackAndCallback(ICallback callback,
|
||
|
UIntPtr param);
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
public static bool IsRunningOnInterruptStack
|
||
|
{
|
||
|
[NoHeapAllocation]
|
||
|
get {
|
||
|
unsafe {
|
||
|
return StackLimit == GetCurrentCpu()->interruptStackLimit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Direct register access
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
public static UIntPtr StackLimit
|
||
|
{
|
||
|
[NoHeapAllocation]
|
||
|
get {
|
||
|
unsafe {
|
||
|
return GetCurrentThread()->activeStackLimit;
|
||
|
}
|
||
|
}
|
||
|
[NoHeapAllocation]
|
||
|
set {
|
||
|
unsafe {
|
||
|
GetCurrentThread()->activeStackLimit = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if ISA_ARM
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern uint GetCpsr();
|
||
|
|
||
|
#endif // ISA_ARM
|
||
|
|
||
|
#if ISA_IX
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern UIntPtr GetCs();
|
||
|
|
||
|
#endif // ISA_IX
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void WriteMsr(uint offset,ulong value);
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern ulong ReadMsr(uint offset);
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Specialized instruction support
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern void Halt();
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
|
||
|
#if ISA_ARM
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static extern void EnableCycleCounter();
|
||
|
#else
|
||
|
[NoHeapAllocation]
|
||
|
internal static void EnableCycleCounter()
|
||
|
{
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static extern ulong GetCycleCount();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern ulong ReadPmc(uint offset);
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void ReadCpuid(uint feature,
|
||
|
out uint v0, out uint v1, out uint v2, out uint v3);
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Basic FPU support
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void InitFpu();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
private static extern uint ReadFpuStatus();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
private static extern void ClearFpuStatus();
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// IO space support
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if ISA_IX
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern int In(ushort port);
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void Out(ushort port, int value);
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern unsafe void RepInS(uint count, ushort port, byte *buffer);
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(64)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern unsafe void RepOutS(uint count, ushort port, byte *buffer);
|
||
|
|
||
|
#endif // ISA_IX
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Chip-level interrupt enable flag
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern bool AreInterruptsDisabled();
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
internal static extern bool DisableInterrupts();
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
internal static extern void EnableInterrupts();
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Call frame support
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static unsafe extern UIntPtr GetFrameReturnAddress(UIntPtr framePointer);
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static unsafe extern UIntPtr GetFrameCallerFrame(UIntPtr framePointer);
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static extern UIntPtr GetStackPointer();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static extern UIntPtr GetFramePointer();
|
||
|
|
||
|
// These methods are public and safe to use from any where provided
|
||
|
// there's at least 2 call frames on the stack.
|
||
|
[NoHeapAllocation]
|
||
|
public static UIntPtr GetCallerReturnAddress()
|
||
|
{
|
||
|
UIntPtr currentFrame = GetFramePointer();
|
||
|
UIntPtr callerFrame = GetFrameCallerFrame(currentFrame);
|
||
|
if (callerFrame == UIntPtr.Zero) {
|
||
|
return UIntPtr.Zero;
|
||
|
}
|
||
|
UIntPtr callersCaller = GetFrameReturnAddress(callerFrame);
|
||
|
return callersCaller;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Provides a mini stack trace starting from the caller of the caller
|
||
|
/// of this method.
|
||
|
/// </summary>
|
||
|
[NoHeapAllocation]
|
||
|
public static void GetStackReturnAddresses(out UIntPtr pc1, out UIntPtr pc2, out UIntPtr pc3)
|
||
|
{
|
||
|
pc1 = UIntPtr.Zero;
|
||
|
pc2 = UIntPtr.Zero;
|
||
|
pc3 = UIntPtr.Zero;
|
||
|
|
||
|
UIntPtr currentFrame = GetFramePointer();
|
||
|
UIntPtr callerFrame = GetFrameCallerFrame(currentFrame);
|
||
|
if (callerFrame == UIntPtr.Zero) {
|
||
|
return;
|
||
|
}
|
||
|
pc1 = GetFrameReturnAddress(callerFrame);
|
||
|
callerFrame = GetFrameCallerFrame(callerFrame);
|
||
|
if (callerFrame == UIntPtr.Zero) {
|
||
|
return;
|
||
|
}
|
||
|
pc2 = GetFrameReturnAddress(callerFrame);
|
||
|
callerFrame = GetFrameCallerFrame(callerFrame);
|
||
|
if (callerFrame == UIntPtr.Zero) {
|
||
|
return;
|
||
|
}
|
||
|
pc3 = GetFrameReturnAddress(callerFrame);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Provides the full stack trace starting from the caller of the caller
|
||
|
/// of this method.
|
||
|
/// </summary>
|
||
|
/// <returns>Return address values in stack array from top to bottom</returns>
|
||
|
[NoHeapAllocation]
|
||
|
public static uint GetStackReturnAddresses(UIntPtr[] stack)
|
||
|
{
|
||
|
uint index;
|
||
|
if (stack == null) {
|
||
|
return 0;
|
||
|
}
|
||
|
UIntPtr currentFrame = GetFramePointer();
|
||
|
UIntPtr callerFrame = GetFrameCallerFrame(currentFrame);
|
||
|
for (index = 0; callerFrame != UIntPtr.Zero && index < stack.Length; index++) {
|
||
|
stack[index] = GetFrameReturnAddress(callerFrame);
|
||
|
callerFrame = GetFrameCallerFrame(callerFrame);
|
||
|
}
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Execution mode
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
[NoHeapAllocation]
|
||
|
[AccessedByRuntime("foo")]
|
||
|
internal static bool IsInUserMode()
|
||
|
{
|
||
|
#if ISA_IX
|
||
|
// The bottom two bits of the CS selector are the RPL
|
||
|
// (requested privilege level) of the selector. If
|
||
|
// this is 3, we're running in ring3. Otherwise,
|
||
|
// we're more privileged.
|
||
|
|
||
|
return (GetCs() & 0x3) == 3;
|
||
|
#else
|
||
|
// We don't currently support non-priveleged mode on other archs.
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
internal static extern void EnterUserMode();
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Chip-level paging support
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void EnablePaging(uint pdpt);
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void DisablePaging();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void ChangePageTableRoot(uint pdpt);
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern UIntPtr GetPageTableRoot();
|
||
|
|
||
|
[AccessedByRuntime("foo")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(16)]
|
||
|
[NoHeapAllocation]
|
||
|
public static extern void InvalidateTLBEntry(UIntPtr pageAddr);
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Interrupt dispatch logic
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
public static bool InInterruptContext
|
||
|
{
|
||
|
[NoHeapAllocation]
|
||
|
get {
|
||
|
unsafe {
|
||
|
// Note that there is a small window where we haven't switched stacks where this
|
||
|
// will return false. However this is balanced against the complexity of
|
||
|
// maintaining a specific flag, especially across context restores operations.
|
||
|
|
||
|
// Also note that during normal thread execution there is a small possibility of
|
||
|
// getting another CPU's interruptStackLimit due to processor migration, but it
|
||
|
// should not affect the result.
|
||
|
|
||
|
return StackLimit == GetCurrentCpu()->interruptStackLimit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
// This call signature is a lie - in fact this routine has asm-level
|
||
|
// calling conventions and will never be called by managed code.
|
||
|
[AccessedByRuntime("defined in interrupt.asm")]
|
||
|
public extern static void DispatchVector();
|
||
|
|
||
|
// This type is a lie - in fact this is a code fragment. However
|
||
|
// C# won't let us take the address of code, so we declare it as a byte.
|
||
|
[AccessedByRuntime("output to header : defined in context.asm")]
|
||
|
[ExternalStaticData]
|
||
|
public unsafe static byte DefaultReturnFromInterrupt;
|
||
|
|
||
|
// The HAL is also allowed to customize the behavior of the final sequence of resuming
|
||
|
// a context, since it includes enabling interrupts which is abstracted by the hal.
|
||
|
// Currently this is specified by an unmanaged code snipped pointer. This code
|
||
|
// should have identical semantics to [iretd on x86,x64; tbd otherwise.]
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
private unsafe static void *returnFromInterrupt;
|
||
|
|
||
|
[NoHeapAllocation]
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
public static unsafe void SetReturnFromInterruptRoutine(UIntPtr code)
|
||
|
{
|
||
|
returnFromInterrupt = (void *) code;
|
||
|
}
|
||
|
|
||
|
// DebuggerDispatch dispatches exceptions to the debugger infrastructure.
|
||
|
// Eventually this should move to the HAL, as different platforms will
|
||
|
// have different debugging requiremnts
|
||
|
|
||
|
[NoHeapAllocation]
|
||
|
private static void DispatchDebugger(ref SpillContext context, int exception)
|
||
|
{
|
||
|
#if ISA_IX
|
||
|
// TBD: factor this special case into debugger abstraction
|
||
|
if (exception == EVectors.Nmi) {
|
||
|
|
||
|
if (Microsoft.Singularity.MpExecution.FreezeRequested) {
|
||
|
|
||
|
Singularity.MpExecution.FreezeProcessor(ref context);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
DebugStub.WriteLine("******************* NMI Interrupt **********************\n");
|
||
|
Singularity.DebugStub.Trap(ref context, exception);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// TBD: we should factor the debugger support into the HAL.
|
||
|
Singularity.DebugStub.Trap(ref context, exception);
|
||
|
}
|
||
|
#elif ISA_ARM
|
||
|
// Fix up pc for some software exceptions, regardless of debugger attachment.
|
||
|
|
||
|
if (exception == ExceptionVector.SoftwareInterrupt) {
|
||
|
// Move pc forward to resume after interrupting instruction.
|
||
|
if (context.instruction != 0x0efffff01) {
|
||
|
// Don't move pc forward for debugger-inserted breakpoints.
|
||
|
context.pc += 4;
|
||
|
}
|
||
|
}
|
||
|
else if (exception == ExceptionVector.UndefinedInstruction) {
|
||
|
// Move pc forward to resume after interrupting instruction.
|
||
|
context.pc += 4;
|
||
|
}
|
||
|
|
||
|
// TBD: we should factor the debugger support into the HAL.
|
||
|
Singularity.DebugStub.Trap(ref context, exception);
|
||
|
#endif
|
||
|
|
||
|
context.Resume();
|
||
|
}
|
||
|
|
||
|
// InterruptDispatch is called by the asm code after saving the context
|
||
|
// and switching stacks. This routine may return, in which case execution
|
||
|
// is resumed, or it may call RestoreContext on a new thread.
|
||
|
|
||
|
[AccessedByRuntime("referenced in asm")]
|
||
|
[NoHeapAllocation]
|
||
|
public static unsafe void DispatchInterrupt(InterruptContext *context)
|
||
|
{
|
||
|
#if false
|
||
|
DebugStub.WriteLine("---------------------------------------------------------------\n");
|
||
|
DebugStub.WriteLine("DispatchInterrupt (vector={0:x2})", __arglist(context->vector));
|
||
|
context->Display();
|
||
|
DebugStub.WriteLine("---------------------------------------------------------------\n");
|
||
|
#endif
|
||
|
|
||
|
// This count is stored in the Singularity Processor object, since that is where singx86
|
||
|
// looks for it. Need to move at some point.
|
||
|
Singularity.Processor p = Singularity.Processor.CurrentProcessor;
|
||
|
if (p != null) {
|
||
|
p.IncrementInterruptCounts(context->ExceptionId);
|
||
|
}
|
||
|
|
||
|
if (context->IsException()) {
|
||
|
// All exceptions get reported to the debugger (as first chance.)
|
||
|
DispatchDebugger(ref GetCurrentCpu()->spill, context->ExceptionId);
|
||
|
|
||
|
// TBD: eventually we want to actually have OS logic to do something with
|
||
|
// hardware exceptions...
|
||
|
}
|
||
|
else {
|
||
|
// Reset context for interrupt code (this affects the fpu)
|
||
|
// @todo: investigate details here; this seems wrong -SET
|
||
|
SpillContext.ResetCurrent();
|
||
|
|
||
|
HalPlatform.CurrentCpu.DispatchInterrupt(context);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Initialization
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
[NoHeapAllocation]
|
||
|
[NoStackOverflowCheck]
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
public static void Initialize(int cpuRecordOffset, int threadRecordOffset)
|
||
|
{
|
||
|
// Fetch the offsets from the HAL
|
||
|
currentCpuOffset = cpuRecordOffset;
|
||
|
currentThreadOffset = threadRecordOffset;
|
||
|
|
||
|
// Set default return from interrupt routine
|
||
|
unsafe {
|
||
|
fixed (byte *p = &DefaultReturnFromInterrupt) {
|
||
|
returnFromInterrupt = p;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NoHeapAllocation]
|
||
|
[NoStackOverflowCheck]
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[NoStackLinkCheckTrans]
|
||
|
public static unsafe void InitializeCpu(CpuRecord *cpu,
|
||
|
int cpuid,
|
||
|
UIntPtr stackLimit)
|
||
|
{
|
||
|
unsafe {
|
||
|
cpu->id = cpuid;
|
||
|
|
||
|
// Set up active stack limit before we may incurr
|
||
|
// a linked stack check.
|
||
|
cpu->bootThread.activeStackLimit = stackLimit;
|
||
|
|
||
|
// Initially we just use the current stack for interrupts.
|
||
|
cpu->interruptStackBegin = 0;
|
||
|
cpu->interruptStackLimit = stackLimit;
|
||
|
|
||
|
InitializeCurrentCpu(ref *cpu);
|
||
|
|
||
|
|
||
|
|
||
|
InitializeCurrentThread(ref cpu->bootThread);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[NoHeapAllocation]
|
||
|
public static void InitializeDispatchTable()
|
||
|
{
|
||
|
#if ISA_IX
|
||
|
InitializeIdt();
|
||
|
#elif ISA_ARM
|
||
|
// Nothing to do as there is no table, just code.
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[NoHeapAllocation]
|
||
|
public static void InitializeCpuDispatchTable()
|
||
|
{
|
||
|
#if ISA_IX
|
||
|
LoadIdt();
|
||
|
#elif ISA_ARM
|
||
|
unsafe {
|
||
|
UIntPtr sp = GetCurrentCpu()->dispatchStack.StackBegin;
|
||
|
|
||
|
// Initialize the sp pointers in the various exception modes
|
||
|
SetModeSp(ProcessorMode.Fiq, sp);
|
||
|
SetModeSp(ProcessorMode.Irq, sp);
|
||
|
SetModeSp(ProcessorMode.Supervisor, sp);
|
||
|
SetModeSp(ProcessorMode.Abort, sp);
|
||
|
SetModeSp(ProcessorMode.Undefined, sp);
|
||
|
}
|
||
|
LoadResetVector();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
private static unsafe extern void InitializeCurrentThread(ref ThreadRecord record);
|
||
|
|
||
|
[AccessedByRuntime("referenced in c++")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[GCAnnotation(GCOption.NOGC)]
|
||
|
[StackBound(32)]
|
||
|
[NoHeapAllocation]
|
||
|
private static unsafe extern void InitializeCurrentCpu(ref CpuRecord record);
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Interrupt table
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if SINGULARITY_KERNEL
|
||
|
|
||
|
#if ISA_IX
|
||
|
|
||
|
[AccessedByRuntime("accessed from asm")]
|
||
|
// BUG: declare this ExternalStaticData since it is not properly aligned by bartok
|
||
|
[ExternalStaticData]
|
||
|
public static InterruptTable idt;
|
||
|
|
||
|
[AccessedByRuntime("called from C++")]
|
||
|
[NoHeapAllocation]
|
||
|
public static void InitializeIdt()
|
||
|
{
|
||
|
idt.Initialize();
|
||
|
}
|
||
|
|
||
|
[AccessedByRuntime("defined in interrupt.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern void LoadIdt();
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#if ISA_ARM
|
||
|
|
||
|
[AccessedByRuntime("defined in isa.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern void SetModeSp(int mode, UIntPtr sp);
|
||
|
|
||
|
[AccessedByRuntime("defined in interrupt.asm")]
|
||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||
|
[NoHeapAllocation]
|
||
|
[StackBound(32)]
|
||
|
public static extern void LoadResetVector();
|
||
|
|
||
|
#endif // ISARM
|
||
|
|
||
|
#endif // SINGULARITY_KERNEL
|
||
|
|
||
|
}
|
||
|
}
|