////////////////////////////////////////////////////////////////////////////////////////////////// // // 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; } /// /// Provides a mini stack trace starting from the caller of the caller /// of this method. /// [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); } /// /// Provides the full stack trace starting from the caller of the caller /// of this method. /// /// Return address values in stack array from top to bottom [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 } }