/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Kernel.cs // // Note: // using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Bartok.Runtime; using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Drivers; using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; using Microsoft.Singularity.Loader; using Microsoft.Singularity.Memory; using Microsoft.Singularity.Scheduling; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Security; using Microsoft.Singularity.Xml; using Microsoft.Singularity.V1.Services; [assembly: AssemblyTitle("Microsoft.Singularity")] [assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyKeyFile("public.snk")] [assembly: AssemblyDelaySign(true)] namespace Microsoft.Singularity { [NoCCtor] [CLSCompliant(false)] public class Kernel { private static string[] args; private static ManualResetEvent mpEndEvent; private static int bootReturnCode; #region Waypoints public static long[] Waypoints; public static int[] WaypointSeq; public static int[] WaypointThd; public static int WaypointNumber;// = 0; public static long WaypointSamples;// = 0; public static bool WaypointSuspicious; public static uint WaypointInterrupt; public const int NWP = 2048; // XXX if this is readonly then it seems to read as 0! [Conditional("WAYPOINTS")] [NoHeapAllocation] public static void Waypoint0() { WaypointSamples++; Waypoints[0] = (long)Processor.CycleCount; WaypointNumber = 1; WaypointSuspicious = false; WaypointInterrupt = Processor.CurrentProcessor.NumInterrupts; } [Conditional("WAYPOINTS")] [NoHeapAllocation] public static void Waypoint(int num) { if (Waypoints == null || Waypoints[0] == 0 || WaypointNumber > NWP-1) { return; } long delta = (long)Processor.CycleCount - Waypoints[0]; if (delta > 7000000) { WaypointSuspicious = true; } WaypointSeq[WaypointNumber] = num; WaypointThd[WaypointNumber] = Thread.GetCurrentThreadIndex(); Waypoints[WaypointNumber++] = delta; } [Conditional("WAYPOINTS")] [NoHeapAllocation] public static void WaypointDone() { Waypoints[0] = 0; } #if false [Conditional("WAYPOINTS")] [NoHeapAllocation] public static void WaypointDump() { WaypointDump(NWP); } #endif [Conditional("WAYPOINTS")] public static void WaypointDump() { bool iflag = Processor.DisableInterrupts(); DebugStub.WriteLine("Interrupts: {0}", __arglist(Processor.CurrentProcessor.NumInterrupts - WaypointInterrupt)); DebugStub.WriteLine("WPT Waypoint Sequence THD Diff"); for (int i = 1; i < WaypointNumber; i++) { DebugStub.WriteLine("{0,3:d} {1,10:d} {2,10:d} {3,3:d} {4,10:d}", __arglist( i, Waypoints[i], WaypointSeq[i], WaypointThd[i].GetHashCode(), Waypoints[i] - Kernel.Waypoints[i-1])); } Processor.RestoreInterrupts(iflag); } [Conditional("WAYPOINTS")] [NoHeapAllocation] public static void WaypointReset() { for (int i = 0; i < NWP; i++) { Waypoints[i] = 0; } WaypointSamples = 0; } #endregion // Waypoints // Note: This function is called by Hal.cpp. [AccessedByRuntime("referenced from hal.cpp")] internal static int Main() { bootReturnCode = BootInfo.EXIT_AND_RESTART; DebugStub.WriteLine("Kernel.Main()"); // Initialize the memory subsystem. This turns on paging! MemoryManager.Initialize(); // Note for Monitoring early boot process: // if you ever want to monitor stuff before this point, you should // allocate a static memory area in BootInit.cs, init the // monitoring system earlier, hold the system at this point here, // copy over all the collected data up to now to the new // dynamically created buffer and continue Monitoring.Initialize(); // uses page memory HandleTable.Initialize(); Stacks.Initialize(); try { // Initialize the rest of the primitive runtime. VTable.Initialize((RuntimeType)typeof(Kernel)); #if PAGING // Must occur before MemoryManager.PostGCInitialize() ProtectionDomain.Initialize(); #endif // Must occur before SharedHeap.Initialize() MemoryManager.PostGCInitialize(); SharedHeap.Initialize(); #if PAGING // Must occur after SharedHeap.Initialize(); ProtectionDomain.DefaultDomain.InitHook(); #endif args = GetCommandLine(); VTable.ParseArgs(args); // Initialize the processor table. InitType(typeof(Processor)); InitType(typeof(Scheduler)); InitType(typeof(MinScheduler)); Processor.InitializeProcessorTable(); Tracing.Log(Tracing.Audit, "processor"); Processor processor = Processor.processorTable[0]; processor.Initialize(0); PEImage.Initialize(); // initialize endpoints InitType(typeof(Microsoft.Singularity.Channels.EndpointCore)); // get the system manifest XmlReader xmlReader = new XmlReader(Binder.GetSystemManifest()); XmlNode xmlData = xmlReader.Parse(); XmlNode manifestRoot = xmlData.GetChild("system"); XmlNode initConfig = manifestRoot.GetChild("initConfig"); PerfCounters.Initialize(); // need to have processed the manifest before we can call Process initialize PrincipalImpl.Initialize(initConfig); Process.Initialize(manifestRoot.GetChild("processConfig")); // obtain the configuration for the namespace service // and initialize the namespace service DirectoryService.Initialize(initConfig); Tracing.Log(Tracing.Audit, "IoSystem"); IoSystem.Initialize( manifestRoot.GetChild("drivers")); Tracing.Log(Tracing.Audit, "Registering HAL Drivers."); Devices.RegisterPnpResources(); // add the root device HalDevices.Initialize(processor); // #if SINGULARITY_MP // haryadi -- at this point we have the SRAT table, // Create per-processor address space now MemoryManager.InitializeProcessorAddressSpace(); #endif // From here on, we want lazy type initialization to worry about // competing threads. VTable.InitializeForMultipleThread(); Console.WriteLine("Running C# Kernel of {0}", GetLinkDate()); Console.WriteLine(); Console.WriteLine("Initializing Scheduler"); MinScheduler.Initialize(Process.idleProcess); DirectoryService.StartNotificationThread(); Console.WriteLine("Initializing Shared Heap Walker"); Process.InitializeSharedHeapWalker(); Console.WriteLine("Initializing Service Thread"); ServiceThread.Initialize(); #if GENERATE_ABI_SHIM // haryadi: add Ap service thread Console.WriteLine("Initializing AP Service Thread"); ApServiceThread.Initialize(); #endif Tracing.Log(Tracing.Audit, "Enabling GC Heap"); GC.EnableHeap(); Tracing.Log(Tracing.Audit, "Waypoints init"); Waypoints = new long[2048]; WaypointSeq = new int[2048]; WaypointThd = new int[2048]; Tracing.Log(Tracing.Audit, "Interrupts ON."); Processor.RestoreInterrupts(true); Tracing.Log(Tracing.Audit, "Starting Security Service channels"); PrincipalImpl.Export(); Tracing.Log(Tracing.Audit, "Creating Root Directory."); IoSystem.InitializeDirectoryService(); // [TODO]turn off kernel interfaces to namespace here Tracing.Log(Tracing.Audit, "Binder"); Binder.Initialize( manifestRoot.GetChild("namingConventions")); Tracing.Log(Tracing.Audit, "Creating console."); ConsoleOutput.Initialize(); // Initialize MP after Binder and ConsoleOutput // are initialized so there are no // initialization races if the additional // threads try to use them. Tracing.Log(Tracing.Audit, "Starting additional processors"); MpExecution.Initialize(); mpEndEvent = new ManualResetEvent(false); Processor.StartApProcessors(); Tracing.Log(Tracing.Audit, "Initializing Volume Manager."); IoSystem.InitializeVolumeManager(); // Register drivers who depend on scheduling and resource // management. DebugStub.WriteLine("--- Registering Drivers ---------------------------"); Console.WriteLine("Registering Non-HAL Drivers."); // register the metadata-based drivers IoSystem.RegisterDrivers(); // register the internal kernel drivers Devices.RegisterInternalDrivers(); NvPciLpcBridge.Register(); #if DEBUG // and output the results IoSystem.Dump(false); #endif DebugStub.WriteLine("--- Activating Devices ----------------------------"); // now do device initialization IoSystem.ActivateDrivers(); #if DEBUG // and output the results IoSystem.Dump(true); #endif Tracing.Log(Tracing.Audit, "Initializing Service Manager."); IoSystem.InitializeServiceManager(manifestRoot.GetChild("serviceConfig")); // Start up the kernel's diagnostics module Console.WriteLine("Starting diagnostics module..."); Diagnostics.DiagnosticsModule.Initialize(); // Start up the kernel's stress test module Console.WriteLine("Initializing stress module..."); Stress.StressService.Initialize(); Processor.StartSampling(); // Consider boot successful at this stage. bootReturnCode = BootInfo.EXIT_AND_SHUTDOWN; DebugStub.WriteLine("----------------------------------------------------------"); DebugStub.WriteLine("RdTsc, Halt, RdTsc"); ulong beg; ulong end; beg = Processor.CycleCount; Processor.HaltUntilInterrupt(); end = Processor.CycleCount; DebugStub.WriteLine(" Begin: {0,16:d}", __arglist(beg)); DebugStub.WriteLine(" End: {0,16:d}", __arglist(end)); DebugStub.WriteLine(" Diff: {0,16:d}", __arglist(end - beg)); DebugStub.WriteLine("----------------------------------------------------------"); Tracing.Log(Tracing.Audit, "Creating Shell Process"); int exit = -10000; Process process = null; IoMemory memory; Manifest manifest; #if KERNEL_USE_LOGIN if (args[0] == "bvt") { memory = Binder.LoadImage(Thread.CurrentProcess, "tty.x86", out manifest); } else{ // TODO: The login app needs to be fixed to setup stdin and stdout pipes for // the shell and pump the data back and forth. memory = Binder.LoadImage(Thread.CurrentProcess, "login.x86", out manifest); } #else memory = Binder.LoadImage(Thread.CurrentProcess, "tty.x86", out manifest); #endif if (memory != null && memory.Length > 0) { String[] shellArgs = new String[args.Length + 2]; shellArgs[0] = "tty.x86"; shellArgs[1] = "shell.x86"; for (int i = 0; i < args.Length; i++) { shellArgs[i + 2] = args[i]; } process = new Process(Thread.CurrentProcess, memory, null, shellArgs, manifest); if (process != null) { process.Start(); process.Join(); exit = process.ExitCode; } } switch (exit) { case -10000: Tracing.Log(Tracing.Audit, "Failed to start shell process."); bootReturnCode = BootInfo.EXIT_AND_RESTART; break; case BootInfo.EXIT_AND_WARMBOOT: bootReturnCode = BootInfo.EXIT_AND_WARMBOOT; break; case BootInfo.EXIT_AND_RESTART: bootReturnCode = BootInfo.EXIT_AND_RESTART; break; case BootInfo.EXIT_AND_SHUTDOWN: bootReturnCode = BootInfo.EXIT_AND_SHUTDOWN; break; } Tracing.Log(Tracing.Audit, "Shutting down AP processors"); Processor.StopApProcessors(); Tracing.Log(Tracing.Audit, "Shutting down I/O system"); Console.WriteLine("Shutting down I/O system"); IoSystem.Finalize(); Tracing.Log(Tracing.Audit, "Interrupts OFF."); Processor.DisableInterrupts(); Tracing.Log(Tracing.Audit, "Shutting down scheduler"); Console.WriteLine("Shutting down scheduler"); MinScheduler.Finalize(); // We should turn off interrupts here! HalDevices.Finalize(); PEImage.Finalize(); DebugStub.WriteLine("Kernel Exiting [{0}]", __arglist(bootReturnCode)); } catch (Exception e) { Tracing.Log(Tracing.Fatal, "Caught exception {0}", e.Message); DebugStub.WriteLine("Caught {0}", __arglist(e.Message)); bootReturnCode = -1; DebugStub.Break(); } DebugStub.WriteLine("Kernel exiting with 0x{0:x4}", __arglist(bootReturnCode)); Stacks.Finalize(); HandleTable.Finalize(); #if !PAGING SharedHeap.Finalize(); #endif //PAGING MemoryManager.Finalize(); if (bootReturnCode != BootInfo.EXIT_AND_WARMBOOT) { Kill(bootReturnCode); } return bootReturnCode; } // Note: This function is entry point to the managed // kernel for CPU's other than the bootstrap processor. // It is called by Hal.cpp. [AccessedByRuntime("referenced from hal.cpp")] internal static int MpMain(int cpu) { Tracing.Log(Tracing.Audit, "processor"); Processor processor = Processor.processorTable[cpu]; processor.Initialize(cpu); HalDevices.Initialize(processor); Processor.CurrentProcessor.SetNextTimerInterrupt(TimeSpan.Zero); Processor.RestoreInterrupts(true); Tracing.Log(Tracing.Audit, "Halting processor until first interrupt."); Processor.HaltUntilInterrupt(); Tracing.Log(Tracing.Audit, "Resumed from halt."); // This probably won't be the // ultimate way of dissociating kernel entry threads // from the kernel. mpEndEvent.WaitOne(); return 0; } // This function is called by the GC to locate all non-static object // references allocated somewhere other than the stack. internal static void VisitSpecialData(System.GCs.NonNullReferenceVisitor visitor) { Process.VisitSpecialData(visitor); visitor.VisitReferenceFields(Processor.processorTable); } // This function is called by GCs that move object to update all // object references contained in non-objects (i.e. unsafe structs). internal static void UpdateAfterGC(Thread currentThread) { Processor.UpdateAfterGC(currentThread); } [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(8)] [NoHeapAllocation] private static unsafe extern char * HalGetLinkDate(); internal static unsafe string GetLinkDate() { return new String(HalGetLinkDate()); } internal static unsafe string[] GetCommandLine() { String[] args = (new String((char *)(BootInfo.HalGetBootInfo()->CmdLine32))).Split(null); int dst = 0; for (int src = 0; src < args.Length; src++) { if (args[src] != null && args[src].Length > 0) { args[dst++] = args[src]; } } if (dst < args.Length) { String[] list = new String[dst]; for (int i = 0; i < dst; i++) { list[i] = args[i]; } return list; } return args; } [NoHeapAllocation] public static void RequestWarmBoot() { bootReturnCode = BootInfo.EXIT_AND_WARMBOOT; } [NoHeapAllocation] public static void RequestRestart() { bootReturnCode = BootInfo.EXIT_AND_RESTART; } [NoHeapAllocation] public static void RequestShutdown() { bootReturnCode = BootInfo.EXIT_AND_SHUTDOWN; } [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(1024)] [NoHeapAllocation] private static extern void Kill(int exitCode); internal static void Shutdown(int exitCode) { unchecked { Tracing.Log(Tracing.Audit, "Kernel.Shutdown({0})", (UIntPtr)(uint)exitCode); } DebugStub.WriteLine("Kernel.Shutdown(0x{0:x4})", __arglist(exitCode)); DebugStub.Break(); VTable.Shutdown(exitCode); } internal static void Panic(string why) { DebugStub.WriteLine("KERNEL PANIC: {0}", __arglist(why)); Shutdown(BootInfo.EXIT_AND_HALT); } ////////////////////////////////////////////////////////////////////// // [NoHeapAllocation] public static UIntPtr AddressOf(Object o) { return Magic.addressOf(o); } [NoHeapAllocation] public static UIntPtr SizeOf(Object o) { return System.GCs.ObjectLayout.Sizeof(o); } [NoHeapAllocation] public static UIntPtr AddressOf(ITracked tracked) { return Magic.addressOf(tracked); } [NoHeapAllocation] unsafe public static UIntPtr AddressOf(void * ptr) { return (UIntPtr)ptr; } public static void InitType(Type ty) { VTable.initType((RuntimeType) ty); } [NoHeapAllocation] public static string TypeName(Object o) { if (o == null) { return "null"; } else { Type t = o.GetType(); RuntimeType r = (RuntimeType)t; return r.Name; } } [NoHeapAllocation] public static string TypeNameSpace(Object o) { if (o == null) { return "null"; } else { Type t = o.GetType(); RuntimeType r = (RuntimeType)t; return r.Namespace; } } public static string FullTypeName(Object o) { if (o == null) { return "null"; } else { Type t = o.GetType(); RuntimeType r = (RuntimeType)t; return r.Namespace + "." + r.Name; } } public static void DumpPageTable() { System.GCs.PageTable.Dump("PageTable"); } } }